From 97fe642723cec3a3835e9ef95c4447915e8d39b2 Mon Sep 17 00:00:00 2001 From: Yemao Date: Wed, 7 Feb 2018 23:41:34 +0800 Subject: [PATCH] plat: xr871 init version for AliOS-Things(Github) --- board/xr871evb/board.c | 260 ++ board/xr871evb/board.h | 10 + board/xr871evb/k_config.h | 224 ++ board/xr871evb/xr871evb.mk | 61 + example/alinkapp/alinkapp.mk | 2 +- platform/arch/arm/armv7m/gcc/m4/port_s.S | 4 +- platform/mcu/xr871/aos/aos.c | 77 + platform/mcu/xr871/aos/aos.h | 14 + platform/mcu/xr871/aos/aos.mk | 23 + platform/mcu/xr871/aos/aos_impl.c | 85 + platform/mcu/xr871/aos/cli_ext/cli_ext.c | 9 + platform/mcu/xr871/aos/cli_ext/cli_mem.c | 80 + platform/mcu/xr871/aos/cli_ext/cli_upgrade.c | 25 + platform/mcu/xr871/aos/hal/fota_port.c | 186 ++ platform/mcu/xr871/aos/hal/hal.c | 84 + platform/mcu/xr871/aos/hal/soc/adc.c | 112 + platform/mcu/xr871/aos/hal/soc/flash.c | 221 ++ platform/mcu/xr871/aos/hal/soc/gpio.c | 263 ++ platform/mcu/xr871/aos/hal/soc/i2c.c | 249 ++ platform/mcu/xr871/aos/hal/soc/pwm.c | 84 + platform/mcu/xr871/aos/hal/soc/rtc.c | 99 + platform/mcu/xr871/aos/hal/soc/sd.c | 174 ++ platform/mcu/xr871/aos/hal/soc/timer.c | 93 + platform/mcu/xr871/aos/hal/soc/uart.c | 196 ++ platform/mcu/xr871/aos/hal/soc/wdg.c | 74 + platform/mcu/xr871/aos/hal/wifi.c | 184 ++ platform/mcu/xr871/aos/hal/wifi_port.c | 584 ++++ platform/mcu/xr871/aos/hal/wifi_port.h | 65 + platform/mcu/xr871/aos/soc_impl.c | 35 + platform/mcu/xr871/bin/xr871/boot.bin | Bin 0 -> 31208 bytes platform/mcu/xr871/bin/xr871/net.bin | Bin 0 -> 287000 bytes platform/mcu/xr871/bin/xr871/net_ap.bin | Bin 0 -> 254664 bytes platform/mcu/xr871/bin/xr871/wlan_bl.bin | Bin 0 -> 2308 bytes platform/mcu/xr871/bin/xr871/wlan_fw.bin | Bin 0 -> 132180 bytes platform/mcu/xr871/bin/xr871/wlan_sdd.bin | Bin 0 -> 744 bytes platform/mcu/xr871/image-alinkapp-xz.cfg | 16 + platform/mcu/xr871/image-alinkapp.cfg | 16 + platform/mcu/xr871/image-douglasapp.cfg | 16 + platform/mcu/xr871/image-xip.cfg | 16 + platform/mcu/xr871/image-xplayerapp.cfg | 16 + platform/mcu/xr871/image-yts-xz.cfg | 16 + platform/mcu/xr871/image.cfg | 15 + platform/mcu/xr871/include/aos/hal/adc_priv.h | 33 + .../include/audio/manager/audio_manager.h | 58 + .../mcu/xr871/include/audio/pcm/audio_pcm.h | 70 + platform/mcu/xr871/include/compiler.h | 83 + platform/mcu/xr871/include/console/console.h | 65 + platform/mcu/xr871/include/driver/chip/chip.h | 166 ++ .../driver/chip/flashchip/flash_chip.h | 234 ++ .../mcu/xr871/include/driver/chip/hal_adc.h | 215 ++ .../mcu/xr871/include/driver/chip/hal_audio.h | 61 + .../mcu/xr871/include/driver/chip/hal_ccm.h | 308 +++ .../mcu/xr871/include/driver/chip/hal_chip.h | 59 + .../mcu/xr871/include/driver/chip/hal_clock.h | 112 + .../mcu/xr871/include/driver/chip/hal_cmsis.h | 35 + .../mcu/xr871/include/driver/chip/hal_codec.h | 172 ++ .../xr871/include/driver/chip/hal_crypto.h | 414 +++ .../mcu/xr871/include/driver/chip/hal_csi.h | 230 ++ .../mcu/xr871/include/driver/chip/hal_def.h | 82 + .../mcu/xr871/include/driver/chip/hal_dma.h | 276 ++ .../mcu/xr871/include/driver/chip/hal_dmic.h | 281 ++ .../mcu/xr871/include/driver/chip/hal_efuse.h | 93 + .../mcu/xr871/include/driver/chip/hal_flash.h | 142 + .../include/driver/chip/hal_flashcache.h | 141 + .../xr871/include/driver/chip/hal_flashctrl.h | 458 ++++ .../xr871/include/driver/chip/hal_global.h | 48 + .../mcu/xr871/include/driver/chip/hal_gpio.h | 414 +++ .../mcu/xr871/include/driver/chip/hal_i2c.h | 190 ++ .../mcu/xr871/include/driver/chip/hal_i2s.h | 523 ++++ .../mcu/xr871/include/driver/chip/hal_irrx.h | 133 + .../mcu/xr871/include/driver/chip/hal_irtx.h | 209 ++ .../mcu/xr871/include/driver/chip/hal_mbox.h | 160 ++ .../mcu/xr871/include/driver/chip/hal_nvic.h | 85 + .../mcu/xr871/include/driver/chip/hal_prcm.h | 637 +++++ .../mcu/xr871/include/driver/chip/hal_pwm.h | 285 ++ .../mcu/xr871/include/driver/chip/hal_rtc.h | 206 ++ .../mcu/xr871/include/driver/chip/hal_spi.h | 482 ++++ .../xr871/include/driver/chip/hal_spinlock.h | 71 + .../mcu/xr871/include/driver/chip/hal_timer.h | 187 ++ .../mcu/xr871/include/driver/chip/hal_uart.h | 290 ++ .../mcu/xr871/include/driver/chip/hal_util.h | 50 + .../xr871/include/driver/chip/hal_wakeup.h | 155 ++ .../mcu/xr871/include/driver/chip/hal_wdg.h | 127 + .../mcu/xr871/include/driver/chip/hal_xip.h | 60 + .../mcu/xr871/include/driver/chip/ir_nec.h | 199 ++ .../xr871/include/driver/chip/sdmmc/card.h | 210 ++ .../include/driver/chip/sdmmc/hal_sdhost.h | 114 + .../xr871/include/driver/chip/sdmmc/sdmmc.h | 486 ++++ .../xr871/include/driver/chip/system_chip.h | 49 + .../mcu/xr871/include/driver/cmsis/core_cm3.h | 1650 ++++++++++++ .../mcu/xr871/include/driver/cmsis/core_cm4.h | 1802 +++++++++++++ .../xr871/include/driver/cmsis/core_cmFunc.h | 637 +++++ .../xr871/include/driver/cmsis/core_cmInstr.h | 880 +++++++ .../xr871/include/driver/cmsis/core_cmSimd.h | 697 +++++ platform/mcu/xr871/include/driver/hal_board.h | 67 + platform/mcu/xr871/include/driver/hal_dev.h | 94 + platform/mcu/xr871/include/efpg/efpg.h | 75 + platform/mcu/xr871/include/fs/fatfs/ff.h | 365 +++ .../xr871/include/kernel/os/YunOS/os_common.h | 68 + .../xr871/include/kernel/os/YunOS/os_errno.h | 45 + .../xr871/include/kernel/os/YunOS/os_mutex.h | 115 + .../xr871/include/kernel/os/YunOS/os_queue.h | 135 + .../include/kernel/os/YunOS/os_semaphore.h | 126 + .../xr871/include/kernel/os/YunOS/os_thread.h | 128 + .../xr871/include/kernel/os/YunOS/os_time.h | 87 + .../xr871/include/kernel/os/YunOS/os_timer.h | 169 ++ platform/mcu/xr871/include/kernel/os/os.h | 43 + .../mcu/xr871/include/kernel/os/os_common.h | 39 + .../mcu/xr871/include/kernel/os/os_errno.h | 39 + .../mcu/xr871/include/kernel/os/os_mutex.h | 40 + .../mcu/xr871/include/kernel/os/os_queue.h | 39 + .../xr871/include/kernel/os/os_semaphore.h | 39 + .../mcu/xr871/include/kernel/os/os_thread.h | 39 + .../mcu/xr871/include/kernel/os/os_time.h | 39 + .../mcu/xr871/include/kernel/os/os_timer.h | 39 + platform/mcu/xr871/include/libc/errno.h | 180 ++ .../mcu/xr871/include/libc/sys/features.h | 100 + platform/mcu/xr871/include/net/lwip/arch/cc.h | 153 ++ .../mcu/xr871/include/net/lwip/lwipopts.h | 2341 +++++++++++++++++ .../mcu/xr871/include/net/lwip/lwippools.h | 12 + .../mcu/xr871/include/net/udhcp/usr_dhcpd.h | 51 + .../mcu/xr871/include/net/wlan/ethernetif.h | 59 + platform/mcu/xr871/include/net/wlan/wlan.h | 132 + .../mcu/xr871/include/net/wlan/wlan_airkiss.h | 89 + .../mcu/xr871/include/net/wlan/wlan_defs.h | 488 ++++ .../include/net/wlan/wlan_smart_config.h | 75 + platform/mcu/xr871/include/pm/pm.h | 226 ++ platform/mcu/xr871/include/sys/defs.h | 82 + .../mcu/xr871/include/sys/ducc/ducc_addr.h | 62 + .../mcu/xr871/include/sys/ducc/ducc_app.h | 146 + .../mcu/xr871/include/sys/ducc/ducc_net.h | 123 + platform/mcu/xr871/include/sys/endian.h | 234 ++ platform/mcu/xr871/include/sys/fdcm.h | 57 + platform/mcu/xr871/include/sys/image.h | 141 + platform/mcu/xr871/include/sys/interrupt.h | 159 ++ platform/mcu/xr871/include/sys/io.h | 74 + platform/mcu/xr871/include/sys/list.h | 584 ++++ platform/mcu/xr871/include/sys/mbuf.h | 41 + platform/mcu/xr871/include/sys/mbuf_0.h | 157 ++ platform/mcu/xr871/include/sys/mbuf_1.h | 244 ++ platform/mcu/xr871/include/sys/ota.h | 105 + platform/mcu/xr871/include/sys/param.h | 68 + platform/mcu/xr871/include/sys/queue.h | 694 +++++ platform/mcu/xr871/include/sys/xr_debug.h | 355 +++ platform/mcu/xr871/include/sys/xr_util.h | 48 + platform/mcu/xr871/include/types.h | 36 + platform/mcu/xr871/include/version.h | 43 + platform/mcu/xr871/include/xz/decompress.h | 45 + platform/mcu/xr871/include/xz/xz.h | 286 ++ platform/mcu/xr871/lib/libamr/libamr.a | Bin 0 -> 252734 bytes platform/mcu/xr871/lib/libamr/libamr.mk | 8 + platform/mcu/xr871/lib/libamren/libamren.a | Bin 0 -> 936122 bytes platform/mcu/xr871/lib/libamren/libamren.mk | 8 + platform/mcu/xr871/lib/libmp3/libmp3.a | Bin 0 -> 461652 bytes platform/mcu/xr871/lib/libmp3/libmp3.mk | 8 + platform/mcu/xr871/mkimage.mk | 57 + platform/mcu/xr871/pack/.keep | 0 .../mcu/xr871/project/common/board/board.c | 147 ++ .../mcu/xr871/project/common/board/board.h | 66 + .../xr871/project/common/board/board_common.c | 79 + .../xr871/project/common/board/board_common.h | 48 + .../xr871/project/common/board/board_debug.h | 69 + .../board/xr871_evb_main/board_config.c | 399 +++ .../board/xr871_evb_main/board_config.h | 83 + platform/mcu/xr871/project/common/cmd/cmd.h | 81 + .../mcu/xr871/project/common/cmd/cmd_adc.c | 407 +++ .../mcu/xr871/project/common/cmd/cmd_adc.h | 43 + .../xr871/project/common/cmd/cmd_airkiss.c | 122 + .../xr871/project/common/cmd/cmd_airkiss.h | 43 + .../mcu/xr871/project/common/cmd/cmd_arp.c | 91 + .../mcu/xr871/project/common/cmd/cmd_arp.h | 43 + .../mcu/xr871/project/common/cmd/cmd_audio.c | 369 +++ .../mcu/xr871/project/common/cmd/cmd_audio.h | 64 + .../xr871/project/common/cmd/cmd_broadcast.c | 264 ++ .../xr871/project/common/cmd/cmd_broadcast.h | 44 + .../mcu/xr871/project/common/cmd/cmd_ce.c | 808 ++++++ .../mcu/xr871/project/common/cmd/cmd_ce.h | 43 + .../mcu/xr871/project/common/cmd/cmd_cedarx.c | 532 ++++ .../mcu/xr871/project/common/cmd/cmd_cedarx.h | 46 + .../mcu/xr871/project/common/cmd/cmd_clock.c | 103 + .../mcu/xr871/project/common/cmd/cmd_clock.h | 43 + .../mcu/xr871/project/common/cmd/cmd_debug.h | 64 + .../mcu/xr871/project/common/cmd/cmd_defs.h | 72 + .../mcu/xr871/project/common/cmd/cmd_dhcpd.c | 139 + .../mcu/xr871/project/common/cmd/cmd_dhcpd.h | 43 + .../mcu/xr871/project/common/cmd/cmd_echo.c | 36 + .../mcu/xr871/project/common/cmd/cmd_echo.h | 43 + .../mcu/xr871/project/common/cmd/cmd_efpg.c | 162 ++ .../mcu/xr871/project/common/cmd/cmd_efpg.h | 43 + .../mcu/xr871/project/common/cmd/cmd_etf.c | 46 + .../mcu/xr871/project/common/cmd/cmd_etf.h | 43 + .../mcu/xr871/project/common/cmd/cmd_flash.c | 293 +++ .../mcu/xr871/project/common/cmd/cmd_flash.h | 43 + .../mcu/xr871/project/common/cmd/cmd_fs.c | 461 ++++ .../mcu/xr871/project/common/cmd/cmd_fs.h | 43 + .../mcu/xr871/project/common/cmd/cmd_heap.c | 51 + .../mcu/xr871/project/common/cmd/cmd_heap.h | 43 + .../mcu/xr871/project/common/cmd/cmd_httpc.c | 509 ++++ .../mcu/xr871/project/common/cmd/cmd_httpc.h | 43 + .../mcu/xr871/project/common/cmd/cmd_httpd.c | 83 + .../mcu/xr871/project/common/cmd/cmd_httpd.h | 43 + .../mcu/xr871/project/common/cmd/cmd_i2c.c | 508 ++++ .../mcu/xr871/project/common/cmd/cmd_i2c.h | 43 + .../xr871/project/common/cmd/cmd_ifconfig.c | 71 + .../xr871/project/common/cmd/cmd_ifconfig.h | 43 + .../mcu/xr871/project/common/cmd/cmd_iperf.c | 81 + .../mcu/xr871/project/common/cmd/cmd_iperf.h | 43 + .../mcu/xr871/project/common/cmd/cmd_irrx.c | 128 + .../mcu/xr871/project/common/cmd/cmd_irrx.h | 50 + .../mcu/xr871/project/common/cmd/cmd_irtx.c | 184 ++ .../mcu/xr871/project/common/cmd/cmd_irtx.h | 52 + .../mcu/xr871/project/common/cmd/cmd_mem.c | 226 ++ .../mcu/xr871/project/common/cmd/cmd_mem.h | 43 + .../mcu/xr871/project/common/cmd/cmd_mqtt.c | 559 ++++ .../mcu/xr871/project/common/cmd/cmd_mqtt.h | 43 + .../mcu/xr871/project/common/cmd/cmd_netcmd.c | 38 + .../mcu/xr871/project/common/cmd/cmd_netcmd.h | 43 + .../mcu/xr871/project/common/cmd/cmd_nopoll.c | 892 +++++++ .../mcu/xr871/project/common/cmd/cmd_nopoll.h | 43 + .../mcu/xr871/project/common/cmd/cmd_ota.c | 94 + .../mcu/xr871/project/common/cmd/cmd_ota.h | 43 + .../mcu/xr871/project/common/cmd/cmd_ping.c | 110 + .../mcu/xr871/project/common/cmd/cmd_ping.h | 43 + .../mcu/xr871/project/common/cmd/cmd_pm.c | 161 ++ .../mcu/xr871/project/common/cmd/cmd_pm.h | 43 + .../mcu/xr871/project/common/cmd/cmd_pwm.c | 704 +++++ .../mcu/xr871/project/common/cmd/cmd_pwm.h | 43 + .../mcu/xr871/project/common/cmd/cmd_rtc.c | 282 ++ .../mcu/xr871/project/common/cmd/cmd_rtc.h | 43 + .../mcu/xr871/project/common/cmd/cmd_sd.c | 565 ++++ .../mcu/xr871/project/common/cmd/cmd_sd.h | 50 + .../project/common/cmd/cmd_smart_config.c | 123 + .../project/common/cmd/cmd_smart_config.h | 43 + .../mcu/xr871/project/common/cmd/cmd_sntp.c | 79 + .../mcu/xr871/project/common/cmd/cmd_sntp.h | 43 + .../xr871/project/common/cmd/cmd_sysinfo.c | 334 +++ .../xr871/project/common/cmd/cmd_sysinfo.h | 43 + .../mcu/xr871/project/common/cmd/cmd_timer.c | 248 ++ .../mcu/xr871/project/common/cmd/cmd_timer.h | 43 + .../mcu/xr871/project/common/cmd/cmd_uart.c | 385 +++ .../mcu/xr871/project/common/cmd/cmd_uart.h | 43 + .../xr871/project/common/cmd/cmd_upgrade.c | 94 + .../xr871/project/common/cmd/cmd_upgrade.h | 44 + .../mcu/xr871/project/common/cmd/cmd_util.c | 195 ++ .../mcu/xr871/project/common/cmd/cmd_util.h | 115 + .../mcu/xr871/project/common/cmd/cmd_wdg.c | 139 + .../mcu/xr871/project/common/cmd/cmd_wdg.h | 43 + .../mcu/xr871/project/common/cmd/cmd_wlan.c | 1041 ++++++++ .../mcu/xr871/project/common/cmd/cmd_wlan.h | 47 + .../mcu/xr871/project/common/cmd/tls/client.c | 274 ++ .../xr871/project/common/cmd/tls/cmd_tls.c | 177 ++ .../xr871/project/common/cmd/tls/cmd_tls.h | 42 + .../project/common/cmd/tls/custom_certs.c | 29 + .../mcu/xr871/project/common/cmd/tls/server.c | 300 +++ .../mcu/xr871/project/common/cmd/tls/tls.c | 83 + .../mcu/xr871/project/common/cmd/tls/tls.h | 115 + .../project/common/framework/ctrl_msg_debug.h | 73 + .../project/common/framework/fwk_debug.h | 71 + .../xr871/project/common/framework/img_ctrl.c | 78 + .../xr871/project/common/framework/img_ctrl.h | 43 + .../project/common/framework/img_ctrl_debug.h | 73 + .../xr871/project/common/framework/net_ctrl.c | 432 +++ .../xr871/project/common/framework/net_ctrl.h | 93 + .../project/common/framework/net_ctrl_debug.h | 75 + .../xr871/project/common/framework/net_sys.c | 158 ++ .../project/common/framework/platform_init.c | 259 ++ .../project/common/framework/platform_init.h | 45 + .../common/framework/sys_ctrl/container.c | 246 ++ .../common/framework/sys_ctrl/container.h | 47 + .../common/framework/sys_ctrl/event_queue.c | 245 ++ .../common/framework/sys_ctrl/event_queue.h | 55 + .../common/framework/sys_ctrl/observer.c | 194 ++ .../common/framework/sys_ctrl/observer.h | 83 + .../common/framework/sys_ctrl/publisher.c | 212 ++ .../common/framework/sys_ctrl/publisher.h | 63 + .../common/framework/sys_ctrl/sys_ctrl.c | 139 + .../common/framework/sys_ctrl/sys_ctrl.h | 89 + .../xr871/project/common/framework/sysinfo.c | 222 ++ .../xr871/project/common/framework/sysinfo.h | 127 + .../project/common/framework/sysinfo_debug.h | 71 + .../project/common/startup/gcc/exception.c | 231 ++ .../project/common/startup/gcc/retarget.c | 90 + .../common/startup/gcc/retarget_main.c | 59 + .../common/startup/gcc/retarget_stdout.c | 132 + .../project/common/startup/gcc/startup.S | 581 ++++ platform/mcu/xr871/project/main/main.c | 36 + platform/mcu/xr871/project/main/prj_config.h | 105 + platform/mcu/xr871/project/project.mk | 24 + .../src/audio/audio_manager/audio_manager.c | 223 ++ .../src/audio/audio_manager/audio_manager.mk | 8 + .../mcu/xr871/src/audio/audio_pcm/audio_pcm.c | 468 ++++ .../xr871/src/audio/audio_pcm/audio_pcm.mk | 8 + .../Cdx2.0Plugin/include/AudioDec_Decode.h | 123 + .../cedarx/Cdx2.0Plugin/include/CDX_Common.h | 45 + .../Cdx2.0Plugin/include/CDX_Fileformat.h | 107 + .../Cdx2.0Plugin/include/GetAudio_format.h | 184 ++ .../Cdx2.0Plugin/include/PostProcessCom.h | 39 + .../src/cedarx/Cdx2.0Plugin/include/STTypes.h | 152 ++ .../Cdx2.0Plugin/include/TouchToStretch.h | 45 + .../Cdx2.0Plugin/include/ad_cedarlib_com.h | 142 + .../cedarx/Cdx2.0Plugin/include/adecoder.h | 438 +++ .../cedarx/Cdx2.0Plugin/include/auGaincom.h | 15 + .../cedarx/Cdx2.0Plugin/include/aumixcom.h | 32 + .../include/cedar_abs_packet_hdr.h | 63 + .../src/cedarx/Cdx2.0Plugin/include/log.h | 119 + .../cedarx/Cdx2.0Plugin/include/rw_data_api.h | 31 + .../Cdx2.0Plugin/tools/memcheck/memcheck.h | 32 + .../Cdx2.0Plugin/tools/memcheck/memname.h | 7 + .../xr871/src/cedarx/CdxEncPlugin/Android.mk | 38 + .../xr871/src/cedarx/CdxEncPlugin/Makefile | 176 ++ .../xr871/src/cedarx/CdxEncPlugin/aacencApi.h | 18 + .../src/cedarx/CdxEncPlugin/aenc_sw_lib.h | 106 + .../xr871/src/cedarx/CdxEncPlugin/amrencApi.h | 18 + .../xr871/src/cedarx/CdxEncPlugin/config.mk | 140 + .../cedarx/CdxEncPlugin/include/aencoder.h | 84 + .../src/cedarx/CdxEncPlugin/include/log.h | 108 + .../xr871/src/cedarx/CdxEncPlugin/mp3encApi.h | 18 + .../xr871/src/cedarx/CdxEncPlugin/pcm_enc.h | 16 + platform/mcu/xr871/src/cedarx/Makefile | 28 + .../cedarx/awrecorder/AudioEncodeComponent.h | 38 + .../src/cedarx/awrecorder/EncDataComponent.h | 41 + .../xr871/src/cedarx/awrecorder/Makefile.am | 31 + .../xr871/src/cedarx/awrecorder/awencoder.h | 171 ++ .../src/cedarx/base/include/AwMessageQueue.h | 69 + .../xr871/src/cedarx/base/include/AwPool.h | 69 + .../xr871/src/cedarx/base/include/CdxAtomic.h | 67 + .../xr871/src/cedarx/base/include/CdxBase64.h | 29 + .../xr871/src/cedarx/base/include/CdxBinary.h | 50 + .../src/cedarx/base/include/CdxBitReader.h | 55 + .../xr871/src/cedarx/base/include/CdxBuffer.h | 185 ++ .../xr871/src/cedarx/base/include/CdxDebug.h | 43 + .../src/cedarx/base/include/CdxEnumCommon.h | 52 + .../src/cedarx/base/include/CdxISOLang.h | 23 + .../src/cedarx/base/include/CdxKeyedVector.h | 55 + .../xr871/src/cedarx/base/include/CdxList.h | 273 ++ .../xr871/src/cedarx/base/include/CdxList.i | 177 ++ .../xr871/src/cedarx/base/include/CdxLock.h | 32 + .../xr871/src/cedarx/base/include/CdxMemory.h | 24 + .../src/cedarx/base/include/CdxMessage.h | 232 ++ .../xr871/src/cedarx/base/include/CdxMeta.h | 235 ++ .../xr871/src/cedarx/base/include/CdxQueue.h | 70 + .../src/cedarx/base/include/CdxSocketUtil.h | 56 + .../src/cedarx/base/include/CdxStrUtil.h | 45 + .../src/cedarx/base/include/CdxSysinfo.h | 40 + .../xr871/src/cedarx/base/include/CdxTime.h | 22 + .../xr871/src/cedarx/base/include/CdxTypes.h | 96 + .../xr871/src/cedarx/base/include/CdxUrl.h | 36 + .../src/cedarx/base/include/SmartDnsService.h | 35 + .../xr871/src/cedarx/base/include/cdx_log.h | 225 ++ .../src/cedarx/base/include/cdx_malloc_dbg.h | 42 + platform/mcu/xr871/src/cedarx/cedarx.a | Bin 0 -> 1989960 bytes platform/mcu/xr871/src/cedarx/cedarx.mk | 122 + .../mcu/xr871/src/cedarx/cedarx_version.h | 52 + .../cedarx/common/iniparser/iniparserapi.h | 170 ++ .../external/include/adecoder/adecoder.h | 434 +++ .../external/include/aencoder/aencoder.h | 84 + .../src/cedarx/external/include/zlib/zconf.h | 511 ++++ .../src/cedarx/external/include/zlib/zlib.h | 1768 +++++++++++++ .../mcu/xr871/src/cedarx/muxer/Makefile.am | 2 + .../xr871/src/cedarx/muxer/RecoderCbWriter.h | 36 + .../xr871/src/cedarx/muxer/RecoderWriter.h | 40 + .../xr871/src/cedarx/muxer/aac/CdxAacMuxer.h | 18 + .../xr871/src/cedarx/muxer/aac/Makefile.am | 27 + .../src/cedarx/muxer/base/CdxMuxerBaseDef.h | 32 + .../src/cedarx/muxer/base/CdxTsemaphore.h | 58 + .../xr871/src/cedarx/muxer/base/Makefile.am | 38 + .../xr871/src/cedarx/muxer/include/CdxMuxer.h | 217 ++ .../src/cedarx/muxer/include/CdxWriter.h | 82 + .../xr871/src/cedarx/os_glue/include/atomic.h | 41 + .../src/cedarx/os_glue/include/cedarx_fs.h | 56 + .../xr871/src/cedarx/os_glue/include/list.h | 193 ++ .../src/cedarx/os_glue/include/pthread.h | 290 ++ .../xr871/src/cedarx/os_glue/include/unistd.h | 26 + .../src/cedarx/parser/amr/CdxAmrParser.h | 50 + .../cedarx/parser/base/id3base/BaseUtils.h | 60 + .../cedarx/parser/base/id3base/CdxMetaData.h | 70 + .../cedarx/parser/base/id3base/CdxUtfCode.h | 30 + .../src/cedarx/parser/base/id3base/Id3Base.h | 108 + .../parser/base/id3base/StringContainer.h | 33 + .../src/cedarx/parser/id3v2/CdxId3v2Parser.h | 150 ++ .../src/cedarx/parser/include/CdxParser.h | 721 +++++ .../src/cedarx/parser/mp3/CdxMp3Parser.h | 179 ++ .../src/cedarx/playback/audioDecComponent.h | 99 + .../cedarx/playback/audioRenderComponent.h | 61 + .../mcu/xr871/src/cedarx/playback/avtimer.h | 58 + .../xr871/src/cedarx/playback/baseComponent.h | 108 + .../src/cedarx/playback/bitrateEstimater.h | 48 + .../src/cedarx/playback/framerateEstimater.h | 45 + .../src/cedarx/playback/include/player.h | 407 +++ .../cedarx/playback/include/soundControl.h | 166 ++ .../mcu/xr871/src/cedarx/playback/player_i.h | 91 + .../xr871/src/cedarx/playback/streamManager.h | 57 + .../cedarx/record/include/CaptureControl.h | 77 + .../src/cedarx/stream/http/CdxHttpStream.h | 228 ++ .../src/cedarx/stream/include/CdxSeqBuffer.h | 63 + .../src/cedarx/stream/include/CdxStream.h | 575 ++++ .../cedarx/stream/include/CdxStreamErrno.h | 24 + platform/mcu/xr871/src/cedarx/xplayer/cache.h | 121 + .../xr871/src/cedarx/xplayer/demuxComponent.h | 159 ++ .../src/cedarx/xplayer/include/mediaInfo.h | 89 + .../src/cedarx/xplayer/include/xplayer.h | 233 ++ .../src/cedarx/xrecoder/include/xrecord.h | 47 + platform/mcu/xr871/src/console/Makefile | 24 + platform/mcu/xr871/src/console/console.c | 396 +++ platform/mcu/xr871/src/console/console.mk | 8 + .../mcu/xr871/src/console/console_debug.h | 69 + platform/mcu/xr871/src/driver/chip/chip.mk | 44 + .../mcu/xr871/src/driver/chip/codec/codec.h | 115 + .../xr871/src/driver/chip/codec/hal_ac101.c | 907 +++++++ .../xr871/src/driver/chip/codec/hal_ac101.h | 434 +++ .../xr871/src/driver/chip/codec/hal_codec.c | 627 +++++ .../src/driver/chip/flashchip/flash_chip.c | 962 +++++++ .../src/driver/chip/flashchip/flash_default.c | 275 ++ .../src/driver/chip/flashchip/flash_default.h | 49 + platform/mcu/xr871/src/driver/chip/hal_adc.c | 879 +++++++ platform/mcu/xr871/src/driver/chip/hal_base.h | 46 + .../mcu/xr871/src/driver/chip/hal_board.c | 65 + platform/mcu/xr871/src/driver/chip/hal_ccm.c | 661 +++++ .../mcu/xr871/src/driver/chip/hal_crypto.c | 1932 ++++++++++++++ platform/mcu/xr871/src/driver/chip/hal_csi.c | 393 +++ .../mcu/xr871/src/driver/chip/hal_debug.h | 97 + platform/mcu/xr871/src/driver/chip/hal_dma.c | 328 +++ platform/mcu/xr871/src/driver/chip/hal_dmic.c | 700 +++++ .../mcu/xr871/src/driver/chip/hal_efuse.c | 320 +++ .../mcu/xr871/src/driver/chip/hal_flash.c | 1158 ++++++++ .../xr871/src/driver/chip/hal_flashcache.c | 141 + .../mcu/xr871/src/driver/chip/hal_flashctrl.c | 1242 +++++++++ .../mcu/xr871/src/driver/chip/hal_global.c | 46 + platform/mcu/xr871/src/driver/chip/hal_gpio.c | 423 +++ platform/mcu/xr871/src/driver/chip/hal_i2c.c | 909 +++++++ platform/mcu/xr871/src/driver/chip/hal_i2s.c | 1353 ++++++++++ platform/mcu/xr871/src/driver/chip/hal_irrx.c | 426 +++ platform/mcu/xr871/src/driver/chip/hal_irtx.c | 502 ++++ platform/mcu/xr871/src/driver/chip/hal_mbox.c | 394 +++ platform/mcu/xr871/src/driver/chip/hal_nvic.c | 321 +++ platform/mcu/xr871/src/driver/chip/hal_os.h | 148 ++ platform/mcu/xr871/src/driver/chip/hal_prcm.c | 665 +++++ platform/mcu/xr871/src/driver/chip/hal_pwm.c | 955 +++++++ platform/mcu/xr871/src/driver/chip/hal_rtc.c | 385 +++ platform/mcu/xr871/src/driver/chip/hal_spi.c | 1356 ++++++++++ .../mcu/xr871/src/driver/chip/hal_spinlock.c | 217 ++ .../mcu/xr871/src/driver/chip/hal_timer.c | 243 ++ platform/mcu/xr871/src/driver/chip/hal_uart.c | 1226 +++++++++ platform/mcu/xr871/src/driver/chip/hal_util.c | 62 + .../mcu/xr871/src/driver/chip/hal_wakeup.c | 364 +++ platform/mcu/xr871/src/driver/chip/hal_wdg.c | 257 ++ platform/mcu/xr871/src/driver/chip/hal_xip.c | 245 ++ platform/mcu/xr871/src/driver/chip/ir_nec.c | 284 ++ .../mcu/xr871/src/driver/chip/sdmmc/_sd.h | 50 + .../mcu/xr871/src/driver/chip/sdmmc/core.c | 970 +++++++ .../mcu/xr871/src/driver/chip/sdmmc/core.h | 219 ++ .../xr871/src/driver/chip/sdmmc/hal_sdhost.c | 1462 ++++++++++ platform/mcu/xr871/src/driver/chip/sdmmc/sd.c | 773 ++++++ .../mcu/xr871/src/driver/chip/sdmmc/sdhost.h | 553 ++++ .../mcu/xr871/src/driver/chip/sdmmc/test.c | 261 ++ .../mcu/xr871/src/driver/chip/system_chip.c | 115 + platform/mcu/xr871/src/efpg/efpg.c | 199 ++ platform/mcu/xr871/src/efpg/efpg.mk | 6 + platform/mcu/xr871/src/efpg/efpg_debug.h | 68 + platform/mcu/xr871/src/efpg/efpg_efuse.c | 532 ++++ platform/mcu/xr871/src/efpg/efpg_frame.c | 340 +++ platform/mcu/xr871/src/efpg/efpg_i.h | 181 ++ platform/mcu/xr871/src/image/fdcm.c | 262 ++ platform/mcu/xr871/src/image/flash.c | 166 ++ platform/mcu/xr871/src/image/flash.h | 51 + platform/mcu/xr871/src/image/image.c | 568 ++++ platform/mcu/xr871/src/image/image.mk | 6 + platform/mcu/xr871/src/image/image_debug.h | 116 + platform/mcu/xr871/src/net/lwip/checksum.c | 126 + platform/mcu/xr871/src/net/lwip/lwip.mk | 5 + platform/mcu/xr871/src/net/lwip/memcpy.c | 59 + platform/mcu/xr871/src/net/udhcp/AUTHORS | 13 + platform/mcu/xr871/src/net/udhcp/COPYING | 339 +++ platform/mcu/xr871/src/net/udhcp/ChangeLog | 249 ++ platform/mcu/xr871/src/net/udhcp/Makefile | 39 + platform/mcu/xr871/src/net/udhcp/README | 50 + .../mcu/xr871/src/net/udhcp/README.dumpleases | 17 + .../mcu/xr871/src/net/udhcp/README.udhcpc | 139 + .../mcu/xr871/src/net/udhcp/README.udhcpd | 59 + platform/mcu/xr871/src/net/udhcp/TODO | 14 + platform/mcu/xr871/src/net/udhcp/arpping.c | 200 ++ platform/mcu/xr871/src/net/udhcp/arpping.h | 41 + .../mcu/xr871/src/net/udhcp/clientpacket.c | 253 ++ .../mcu/xr871/src/net/udhcp/clientpacket.h | 12 + platform/mcu/xr871/src/net/udhcp/debug.h | 48 + platform/mcu/xr871/src/net/udhcp/dhcp_time.c | 13 + platform/mcu/xr871/src/net/udhcp/dhcp_time.h | 12 + platform/mcu/xr871/src/net/udhcp/dhcpc.c | 560 ++++ platform/mcu/xr871/src/net/udhcp/dhcpc.h | 34 + platform/mcu/xr871/src/net/udhcp/dhcpd.c | 289 ++ platform/mcu/xr871/src/net/udhcp/dhcpd.h | 139 + platform/mcu/xr871/src/net/udhcp/dhcpd_cfg.h | 27 + platform/mcu/xr871/src/net/udhcp/dns.h | 82 + platform/mcu/xr871/src/net/udhcp/dns_server.c | 120 + platform/mcu/xr871/src/net/udhcp/dumpleases.1 | 30 + platform/mcu/xr871/src/net/udhcp/dumpleases.c | 112 + platform/mcu/xr871/src/net/udhcp/files.c | 322 +++ platform/mcu/xr871/src/net/udhcp/files.h | 19 + platform/mcu/xr871/src/net/udhcp/frontend.c | 16 + platform/mcu/xr871/src/net/udhcp/leases.c | 161 ++ platform/mcu/xr871/src/net/udhcp/leases.h | 27 + .../mcu/xr871/src/net/udhcp/libbb_udhcp.h | 29 + platform/mcu/xr871/src/net/udhcp/log | 9 + platform/mcu/xr871/src/net/udhcp/options.c | 230 ++ platform/mcu/xr871/src/net/udhcp/options.h | 40 + platform/mcu/xr871/src/net/udhcp/packet.c | 357 +++ platform/mcu/xr871/src/net/udhcp/packet.h | 54 + platform/mcu/xr871/src/net/udhcp/pidfile.c | 69 + platform/mcu/xr871/src/net/udhcp/pidfile.h | 26 + .../mcu/xr871/src/net/udhcp/samples/README | 11 + .../xr871/src/net/udhcp/samples/sample.bound | 30 + .../src/net/udhcp/samples/sample.deconfig | 4 + .../xr871/src/net/udhcp/samples/sample.nak | 4 + .../xr871/src/net/udhcp/samples/sample.renew | 30 + .../xr871/src/net/udhcp/samples/sample.script | 7 + .../xr871/src/net/udhcp/samples/simple.script | 39 + .../xr871/src/net/udhcp/samples/udhcpd.conf | 116 + platform/mcu/xr871/src/net/udhcp/script.c | 228 ++ platform/mcu/xr871/src/net/udhcp/script.h | 6 + .../mcu/xr871/src/net/udhcp/serverpacket.c | 428 +++ .../mcu/xr871/src/net/udhcp/serverpacket.h | 11 + platform/mcu/xr871/src/net/udhcp/socket.c | 211 ++ platform/mcu/xr871/src/net/udhcp/socket.h | 9 + platform/mcu/xr871/src/net/udhcp/udhcp.mk | 25 + platform/mcu/xr871/src/net/udhcp/udhcpc.8 | 208 ++ platform/mcu/xr871/src/net/udhcp/udhcpd.8 | 17 + .../mcu/xr871/src/net/udhcp/udhcpd.conf.5 | 164 ++ platform/mcu/xr871/src/net/udhcp/usr_dhcpd.c | 333 +++ .../mcu/xr871/src/net/wlan/airkiss/airkiss.h | 236 ++ .../xr871/src/net/wlan/airkiss/airkiss_ack.c | 151 ++ .../src/net/wlan/airkiss/airkiss_discover.c | 314 +++ .../xr871/src/net/wlan/airkiss/airkiss_main.c | 261 ++ platform/mcu/xr871/src/net/wlan/ethernetif.c | 529 ++++ .../net/wlan/smartconfig/smart_config_ack.c | 170 ++ .../net/wlan/smartconfig/smart_config_crc.c | 94 + .../net/wlan/smartconfig/smart_config_crc.h | 41 + .../wlan/smartconfig/smart_config_decode.c | 370 +++ .../wlan/smartconfig/smart_config_decode.h | 95 + .../net/wlan/smartconfig/smart_config_main.c | 255 ++ platform/mcu/xr871/src/net/wlan/wlan.c | 459 ++++ platform/mcu/xr871/src/net/wlan/wlan.mk | 13 + platform/mcu/xr871/src/net/wlan/wlan_ctrl.c | 572 ++++ platform/mcu/xr871/src/net/wlan/wlan_debug.h | 81 + .../mcu/xr871/src/net/wlan/wpa_ctrl_req.h | 88 + platform/mcu/xr871/src/ota/ota.c | 765 ++++++ platform/mcu/xr871/src/ota/ota.mk | 6 + platform/mcu/xr871/src/ota/ota_debug.h | 68 + platform/mcu/xr871/src/ota/ota_file.c | 76 + platform/mcu/xr871/src/ota/ota_file.h | 47 + platform/mcu/xr871/src/ota/ota_http.c | 64 + platform/mcu/xr871/src/ota/ota_http.h | 47 + platform/mcu/xr871/src/ota/ota_i.h | 80 + platform/mcu/xr871/src/pm/pm.c | 1058 ++++++++ platform/mcu/xr871/src/pm/pm.mk | 5 + platform/mcu/xr871/src/pm/pm_i.h | 150 ++ platform/mcu/xr871/src/pm/port.c | 116 + platform/mcu/xr871/src/pm/port.h | 66 + platform/mcu/xr871/src/sys/ducc/ducc.c | 77 + platform/mcu/xr871/src/sys/ducc/ducc.h | 81 + platform/mcu/xr871/src/sys/ducc/ducc_app.c | 433 +++ platform/mcu/xr871/src/sys/ducc/ducc_debug.h | 93 + .../mcu/xr871/src/sys/ducc/ducc_hw_mbox.c | 270 ++ .../mcu/xr871/src/sys/ducc/ducc_hw_mbox.h | 47 + platform/mcu/xr871/src/sys/ducc/ducc_mbox.c | 142 + platform/mcu/xr871/src/sys/ducc/ducc_mbox.h | 49 + platform/mcu/xr871/src/sys/ducc/ducc_os.h | 147 ++ platform/mcu/xr871/src/sys/mbuf/mbuf_1.c | 202 ++ platform/mcu/xr871/src/sys/mbuf/mbuf_debug.h | 63 + platform/mcu/xr871/src/sys/mbuf/mbuf_util.h | 44 + platform/mcu/xr871/src/sys/sys.mk | 11 + platform/mcu/xr871/src/xz/decompress.c | 124 + platform/mcu/xr871/src/xz/xz.h | 286 ++ platform/mcu/xr871/src/xz/xz.mk | 7 + platform/mcu/xr871/src/xz/xz_config.h | 111 + platform/mcu/xr871/src/xz/xz_crc32.c | 89 + platform/mcu/xr871/src/xz/xz_dec_lzma2.c | 1172 +++++++++ platform/mcu/xr871/src/xz/xz_dec_stream.c | 821 ++++++ platform/mcu/xr871/src/xz/xz_lzma2.h | 204 ++ platform/mcu/xr871/src/xz/xz_private.h | 156 ++ platform/mcu/xr871/src/xz/xz_stream.h | 62 + platform/mcu/xr871/tools/calc_flash_offs | 43 + platform/mcu/xr871/tools/mkimage | Bin 0 -> 45880 bytes platform/mcu/xr871/tools/mkimage.exe | Bin 0 -> 22016 bytes platform/mcu/xr871/tools/phoenixMC.exe | Bin 0 -> 2555904 bytes platform/mcu/xr871/tools/phoenixMC_Linux | Bin 0 -> 98009 bytes platform/mcu/xr871/tools/xz64 | Bin 0 -> 68648 bytes platform/mcu/xr871/xr871-xip.ld | 221 ++ platform/mcu/xr871/xr871.ld | 170 ++ platform/mcu/xr871/xr871.mk | 106 + 589 files changed, 109777 insertions(+), 3 deletions(-) create mode 100644 board/xr871evb/board.c create mode 100644 board/xr871evb/board.h create mode 100644 board/xr871evb/k_config.h create mode 100644 board/xr871evb/xr871evb.mk create mode 100644 platform/mcu/xr871/aos/aos.c create mode 100644 platform/mcu/xr871/aos/aos.h create mode 100644 platform/mcu/xr871/aos/aos.mk create mode 100644 platform/mcu/xr871/aos/aos_impl.c create mode 100644 platform/mcu/xr871/aos/cli_ext/cli_ext.c create mode 100644 platform/mcu/xr871/aos/cli_ext/cli_mem.c create mode 100644 platform/mcu/xr871/aos/cli_ext/cli_upgrade.c create mode 100644 platform/mcu/xr871/aos/hal/fota_port.c create mode 100644 platform/mcu/xr871/aos/hal/hal.c create mode 100644 platform/mcu/xr871/aos/hal/soc/adc.c create mode 100644 platform/mcu/xr871/aos/hal/soc/flash.c create mode 100644 platform/mcu/xr871/aos/hal/soc/gpio.c create mode 100644 platform/mcu/xr871/aos/hal/soc/i2c.c create mode 100644 platform/mcu/xr871/aos/hal/soc/pwm.c create mode 100644 platform/mcu/xr871/aos/hal/soc/rtc.c create mode 100644 platform/mcu/xr871/aos/hal/soc/sd.c create mode 100644 platform/mcu/xr871/aos/hal/soc/timer.c create mode 100644 platform/mcu/xr871/aos/hal/soc/uart.c create mode 100644 platform/mcu/xr871/aos/hal/soc/wdg.c create mode 100644 platform/mcu/xr871/aos/hal/wifi.c create mode 100644 platform/mcu/xr871/aos/hal/wifi_port.c create mode 100644 platform/mcu/xr871/aos/hal/wifi_port.h create mode 100644 platform/mcu/xr871/aos/soc_impl.c create mode 100644 platform/mcu/xr871/bin/xr871/boot.bin create mode 100644 platform/mcu/xr871/bin/xr871/net.bin create mode 100644 platform/mcu/xr871/bin/xr871/net_ap.bin create mode 100644 platform/mcu/xr871/bin/xr871/wlan_bl.bin create mode 100644 platform/mcu/xr871/bin/xr871/wlan_fw.bin create mode 100644 platform/mcu/xr871/bin/xr871/wlan_sdd.bin create mode 100644 platform/mcu/xr871/image-alinkapp-xz.cfg create mode 100644 platform/mcu/xr871/image-alinkapp.cfg create mode 100644 platform/mcu/xr871/image-douglasapp.cfg create mode 100644 platform/mcu/xr871/image-xip.cfg create mode 100644 platform/mcu/xr871/image-xplayerapp.cfg create mode 100644 platform/mcu/xr871/image-yts-xz.cfg create mode 100644 platform/mcu/xr871/image.cfg create mode 100644 platform/mcu/xr871/include/aos/hal/adc_priv.h create mode 100644 platform/mcu/xr871/include/audio/manager/audio_manager.h create mode 100644 platform/mcu/xr871/include/audio/pcm/audio_pcm.h create mode 100644 platform/mcu/xr871/include/compiler.h create mode 100644 platform/mcu/xr871/include/console/console.h create mode 100644 platform/mcu/xr871/include/driver/chip/chip.h create mode 100644 platform/mcu/xr871/include/driver/chip/flashchip/flash_chip.h create mode 100644 platform/mcu/xr871/include/driver/chip/hal_adc.h create mode 100644 platform/mcu/xr871/include/driver/chip/hal_audio.h create mode 100644 platform/mcu/xr871/include/driver/chip/hal_ccm.h create mode 100644 platform/mcu/xr871/include/driver/chip/hal_chip.h create mode 100644 platform/mcu/xr871/include/driver/chip/hal_clock.h create mode 100644 platform/mcu/xr871/include/driver/chip/hal_cmsis.h create mode 100644 platform/mcu/xr871/include/driver/chip/hal_codec.h create mode 100644 platform/mcu/xr871/include/driver/chip/hal_crypto.h create mode 100644 platform/mcu/xr871/include/driver/chip/hal_csi.h create mode 100644 platform/mcu/xr871/include/driver/chip/hal_def.h create mode 100644 platform/mcu/xr871/include/driver/chip/hal_dma.h create mode 100644 platform/mcu/xr871/include/driver/chip/hal_dmic.h create mode 100644 platform/mcu/xr871/include/driver/chip/hal_efuse.h create mode 100644 platform/mcu/xr871/include/driver/chip/hal_flash.h create mode 100644 platform/mcu/xr871/include/driver/chip/hal_flashcache.h create mode 100644 platform/mcu/xr871/include/driver/chip/hal_flashctrl.h create mode 100644 platform/mcu/xr871/include/driver/chip/hal_global.h create mode 100644 platform/mcu/xr871/include/driver/chip/hal_gpio.h create mode 100644 platform/mcu/xr871/include/driver/chip/hal_i2c.h create mode 100644 platform/mcu/xr871/include/driver/chip/hal_i2s.h create mode 100644 platform/mcu/xr871/include/driver/chip/hal_irrx.h create mode 100644 platform/mcu/xr871/include/driver/chip/hal_irtx.h create mode 100644 platform/mcu/xr871/include/driver/chip/hal_mbox.h create mode 100644 platform/mcu/xr871/include/driver/chip/hal_nvic.h create mode 100644 platform/mcu/xr871/include/driver/chip/hal_prcm.h create mode 100644 platform/mcu/xr871/include/driver/chip/hal_pwm.h create mode 100644 platform/mcu/xr871/include/driver/chip/hal_rtc.h create mode 100644 platform/mcu/xr871/include/driver/chip/hal_spi.h create mode 100644 platform/mcu/xr871/include/driver/chip/hal_spinlock.h create mode 100644 platform/mcu/xr871/include/driver/chip/hal_timer.h create mode 100644 platform/mcu/xr871/include/driver/chip/hal_uart.h create mode 100644 platform/mcu/xr871/include/driver/chip/hal_util.h create mode 100644 platform/mcu/xr871/include/driver/chip/hal_wakeup.h create mode 100644 platform/mcu/xr871/include/driver/chip/hal_wdg.h create mode 100644 platform/mcu/xr871/include/driver/chip/hal_xip.h create mode 100644 platform/mcu/xr871/include/driver/chip/ir_nec.h create mode 100644 platform/mcu/xr871/include/driver/chip/sdmmc/card.h create mode 100644 platform/mcu/xr871/include/driver/chip/sdmmc/hal_sdhost.h create mode 100644 platform/mcu/xr871/include/driver/chip/sdmmc/sdmmc.h create mode 100644 platform/mcu/xr871/include/driver/chip/system_chip.h create mode 100644 platform/mcu/xr871/include/driver/cmsis/core_cm3.h create mode 100644 platform/mcu/xr871/include/driver/cmsis/core_cm4.h create mode 100644 platform/mcu/xr871/include/driver/cmsis/core_cmFunc.h create mode 100644 platform/mcu/xr871/include/driver/cmsis/core_cmInstr.h create mode 100644 platform/mcu/xr871/include/driver/cmsis/core_cmSimd.h create mode 100644 platform/mcu/xr871/include/driver/hal_board.h create mode 100644 platform/mcu/xr871/include/driver/hal_dev.h create mode 100644 platform/mcu/xr871/include/efpg/efpg.h create mode 100644 platform/mcu/xr871/include/fs/fatfs/ff.h create mode 100644 platform/mcu/xr871/include/kernel/os/YunOS/os_common.h create mode 100644 platform/mcu/xr871/include/kernel/os/YunOS/os_errno.h create mode 100644 platform/mcu/xr871/include/kernel/os/YunOS/os_mutex.h create mode 100644 platform/mcu/xr871/include/kernel/os/YunOS/os_queue.h create mode 100644 platform/mcu/xr871/include/kernel/os/YunOS/os_semaphore.h create mode 100644 platform/mcu/xr871/include/kernel/os/YunOS/os_thread.h create mode 100644 platform/mcu/xr871/include/kernel/os/YunOS/os_time.h create mode 100644 platform/mcu/xr871/include/kernel/os/YunOS/os_timer.h create mode 100644 platform/mcu/xr871/include/kernel/os/os.h create mode 100644 platform/mcu/xr871/include/kernel/os/os_common.h create mode 100644 platform/mcu/xr871/include/kernel/os/os_errno.h create mode 100644 platform/mcu/xr871/include/kernel/os/os_mutex.h create mode 100644 platform/mcu/xr871/include/kernel/os/os_queue.h create mode 100644 platform/mcu/xr871/include/kernel/os/os_semaphore.h create mode 100644 platform/mcu/xr871/include/kernel/os/os_thread.h create mode 100644 platform/mcu/xr871/include/kernel/os/os_time.h create mode 100644 platform/mcu/xr871/include/kernel/os/os_timer.h create mode 100644 platform/mcu/xr871/include/libc/errno.h create mode 100644 platform/mcu/xr871/include/libc/sys/features.h create mode 100644 platform/mcu/xr871/include/net/lwip/arch/cc.h create mode 100644 platform/mcu/xr871/include/net/lwip/lwipopts.h create mode 100644 platform/mcu/xr871/include/net/lwip/lwippools.h create mode 100644 platform/mcu/xr871/include/net/udhcp/usr_dhcpd.h create mode 100644 platform/mcu/xr871/include/net/wlan/ethernetif.h create mode 100644 platform/mcu/xr871/include/net/wlan/wlan.h create mode 100644 platform/mcu/xr871/include/net/wlan/wlan_airkiss.h create mode 100644 platform/mcu/xr871/include/net/wlan/wlan_defs.h create mode 100644 platform/mcu/xr871/include/net/wlan/wlan_smart_config.h create mode 100644 platform/mcu/xr871/include/pm/pm.h create mode 100644 platform/mcu/xr871/include/sys/defs.h create mode 100644 platform/mcu/xr871/include/sys/ducc/ducc_addr.h create mode 100644 platform/mcu/xr871/include/sys/ducc/ducc_app.h create mode 100644 platform/mcu/xr871/include/sys/ducc/ducc_net.h create mode 100644 platform/mcu/xr871/include/sys/endian.h create mode 100644 platform/mcu/xr871/include/sys/fdcm.h create mode 100644 platform/mcu/xr871/include/sys/image.h create mode 100644 platform/mcu/xr871/include/sys/interrupt.h create mode 100644 platform/mcu/xr871/include/sys/io.h create mode 100644 platform/mcu/xr871/include/sys/list.h create mode 100644 platform/mcu/xr871/include/sys/mbuf.h create mode 100644 platform/mcu/xr871/include/sys/mbuf_0.h create mode 100644 platform/mcu/xr871/include/sys/mbuf_1.h create mode 100644 platform/mcu/xr871/include/sys/ota.h create mode 100644 platform/mcu/xr871/include/sys/param.h create mode 100644 platform/mcu/xr871/include/sys/queue.h create mode 100644 platform/mcu/xr871/include/sys/xr_debug.h create mode 100644 platform/mcu/xr871/include/sys/xr_util.h create mode 100644 platform/mcu/xr871/include/types.h create mode 100644 platform/mcu/xr871/include/version.h create mode 100644 platform/mcu/xr871/include/xz/decompress.h create mode 100644 platform/mcu/xr871/include/xz/xz.h create mode 100644 platform/mcu/xr871/lib/libamr/libamr.a create mode 100644 platform/mcu/xr871/lib/libamr/libamr.mk create mode 100644 platform/mcu/xr871/lib/libamren/libamren.a create mode 100644 platform/mcu/xr871/lib/libamren/libamren.mk create mode 100644 platform/mcu/xr871/lib/libmp3/libmp3.a create mode 100644 platform/mcu/xr871/lib/libmp3/libmp3.mk create mode 100644 platform/mcu/xr871/mkimage.mk create mode 100644 platform/mcu/xr871/pack/.keep create mode 100644 platform/mcu/xr871/project/common/board/board.c create mode 100644 platform/mcu/xr871/project/common/board/board.h create mode 100644 platform/mcu/xr871/project/common/board/board_common.c create mode 100644 platform/mcu/xr871/project/common/board/board_common.h create mode 100644 platform/mcu/xr871/project/common/board/board_debug.h create mode 100644 platform/mcu/xr871/project/common/board/xr871_evb_main/board_config.c create mode 100644 platform/mcu/xr871/project/common/board/xr871_evb_main/board_config.h create mode 100644 platform/mcu/xr871/project/common/cmd/cmd.h create mode 100644 platform/mcu/xr871/project/common/cmd/cmd_adc.c create mode 100644 platform/mcu/xr871/project/common/cmd/cmd_adc.h create mode 100644 platform/mcu/xr871/project/common/cmd/cmd_airkiss.c create mode 100644 platform/mcu/xr871/project/common/cmd/cmd_airkiss.h create mode 100644 platform/mcu/xr871/project/common/cmd/cmd_arp.c create mode 100644 platform/mcu/xr871/project/common/cmd/cmd_arp.h create mode 100644 platform/mcu/xr871/project/common/cmd/cmd_audio.c create mode 100644 platform/mcu/xr871/project/common/cmd/cmd_audio.h create mode 100644 platform/mcu/xr871/project/common/cmd/cmd_broadcast.c create mode 100644 platform/mcu/xr871/project/common/cmd/cmd_broadcast.h create mode 100644 platform/mcu/xr871/project/common/cmd/cmd_ce.c create mode 100644 platform/mcu/xr871/project/common/cmd/cmd_ce.h create mode 100644 platform/mcu/xr871/project/common/cmd/cmd_cedarx.c create mode 100644 platform/mcu/xr871/project/common/cmd/cmd_cedarx.h create mode 100644 platform/mcu/xr871/project/common/cmd/cmd_clock.c create mode 100644 platform/mcu/xr871/project/common/cmd/cmd_clock.h create mode 100644 platform/mcu/xr871/project/common/cmd/cmd_debug.h create mode 100644 platform/mcu/xr871/project/common/cmd/cmd_defs.h create mode 100644 platform/mcu/xr871/project/common/cmd/cmd_dhcpd.c create mode 100644 platform/mcu/xr871/project/common/cmd/cmd_dhcpd.h create mode 100644 platform/mcu/xr871/project/common/cmd/cmd_echo.c create mode 100644 platform/mcu/xr871/project/common/cmd/cmd_echo.h create mode 100644 platform/mcu/xr871/project/common/cmd/cmd_efpg.c create mode 100644 platform/mcu/xr871/project/common/cmd/cmd_efpg.h create mode 100644 platform/mcu/xr871/project/common/cmd/cmd_etf.c create mode 100644 platform/mcu/xr871/project/common/cmd/cmd_etf.h create mode 100644 platform/mcu/xr871/project/common/cmd/cmd_flash.c create mode 100644 platform/mcu/xr871/project/common/cmd/cmd_flash.h create mode 100644 platform/mcu/xr871/project/common/cmd/cmd_fs.c create mode 100644 platform/mcu/xr871/project/common/cmd/cmd_fs.h create mode 100644 platform/mcu/xr871/project/common/cmd/cmd_heap.c create mode 100644 platform/mcu/xr871/project/common/cmd/cmd_heap.h create mode 100644 platform/mcu/xr871/project/common/cmd/cmd_httpc.c create mode 100644 platform/mcu/xr871/project/common/cmd/cmd_httpc.h create mode 100644 platform/mcu/xr871/project/common/cmd/cmd_httpd.c create mode 100644 platform/mcu/xr871/project/common/cmd/cmd_httpd.h create mode 100644 platform/mcu/xr871/project/common/cmd/cmd_i2c.c create mode 100644 platform/mcu/xr871/project/common/cmd/cmd_i2c.h create mode 100644 platform/mcu/xr871/project/common/cmd/cmd_ifconfig.c create mode 100644 platform/mcu/xr871/project/common/cmd/cmd_ifconfig.h create mode 100644 platform/mcu/xr871/project/common/cmd/cmd_iperf.c create mode 100644 platform/mcu/xr871/project/common/cmd/cmd_iperf.h create mode 100644 platform/mcu/xr871/project/common/cmd/cmd_irrx.c create mode 100644 platform/mcu/xr871/project/common/cmd/cmd_irrx.h create mode 100644 platform/mcu/xr871/project/common/cmd/cmd_irtx.c create mode 100644 platform/mcu/xr871/project/common/cmd/cmd_irtx.h create mode 100644 platform/mcu/xr871/project/common/cmd/cmd_mem.c create mode 100644 platform/mcu/xr871/project/common/cmd/cmd_mem.h create mode 100644 platform/mcu/xr871/project/common/cmd/cmd_mqtt.c create mode 100644 platform/mcu/xr871/project/common/cmd/cmd_mqtt.h create mode 100644 platform/mcu/xr871/project/common/cmd/cmd_netcmd.c create mode 100644 platform/mcu/xr871/project/common/cmd/cmd_netcmd.h create mode 100644 platform/mcu/xr871/project/common/cmd/cmd_nopoll.c create mode 100644 platform/mcu/xr871/project/common/cmd/cmd_nopoll.h create mode 100644 platform/mcu/xr871/project/common/cmd/cmd_ota.c create mode 100644 platform/mcu/xr871/project/common/cmd/cmd_ota.h create mode 100644 platform/mcu/xr871/project/common/cmd/cmd_ping.c create mode 100644 platform/mcu/xr871/project/common/cmd/cmd_ping.h create mode 100644 platform/mcu/xr871/project/common/cmd/cmd_pm.c create mode 100644 platform/mcu/xr871/project/common/cmd/cmd_pm.h create mode 100644 platform/mcu/xr871/project/common/cmd/cmd_pwm.c create mode 100644 platform/mcu/xr871/project/common/cmd/cmd_pwm.h create mode 100644 platform/mcu/xr871/project/common/cmd/cmd_rtc.c create mode 100644 platform/mcu/xr871/project/common/cmd/cmd_rtc.h create mode 100644 platform/mcu/xr871/project/common/cmd/cmd_sd.c create mode 100644 platform/mcu/xr871/project/common/cmd/cmd_sd.h create mode 100644 platform/mcu/xr871/project/common/cmd/cmd_smart_config.c create mode 100644 platform/mcu/xr871/project/common/cmd/cmd_smart_config.h create mode 100644 platform/mcu/xr871/project/common/cmd/cmd_sntp.c create mode 100644 platform/mcu/xr871/project/common/cmd/cmd_sntp.h create mode 100644 platform/mcu/xr871/project/common/cmd/cmd_sysinfo.c create mode 100644 platform/mcu/xr871/project/common/cmd/cmd_sysinfo.h create mode 100644 platform/mcu/xr871/project/common/cmd/cmd_timer.c create mode 100644 platform/mcu/xr871/project/common/cmd/cmd_timer.h create mode 100644 platform/mcu/xr871/project/common/cmd/cmd_uart.c create mode 100644 platform/mcu/xr871/project/common/cmd/cmd_uart.h create mode 100644 platform/mcu/xr871/project/common/cmd/cmd_upgrade.c create mode 100644 platform/mcu/xr871/project/common/cmd/cmd_upgrade.h create mode 100644 platform/mcu/xr871/project/common/cmd/cmd_util.c create mode 100644 platform/mcu/xr871/project/common/cmd/cmd_util.h create mode 100644 platform/mcu/xr871/project/common/cmd/cmd_wdg.c create mode 100644 platform/mcu/xr871/project/common/cmd/cmd_wdg.h create mode 100644 platform/mcu/xr871/project/common/cmd/cmd_wlan.c create mode 100644 platform/mcu/xr871/project/common/cmd/cmd_wlan.h create mode 100644 platform/mcu/xr871/project/common/cmd/tls/client.c create mode 100644 platform/mcu/xr871/project/common/cmd/tls/cmd_tls.c create mode 100644 platform/mcu/xr871/project/common/cmd/tls/cmd_tls.h create mode 100644 platform/mcu/xr871/project/common/cmd/tls/custom_certs.c create mode 100644 platform/mcu/xr871/project/common/cmd/tls/server.c create mode 100644 platform/mcu/xr871/project/common/cmd/tls/tls.c create mode 100644 platform/mcu/xr871/project/common/cmd/tls/tls.h create mode 100644 platform/mcu/xr871/project/common/framework/ctrl_msg_debug.h create mode 100644 platform/mcu/xr871/project/common/framework/fwk_debug.h create mode 100644 platform/mcu/xr871/project/common/framework/img_ctrl.c create mode 100644 platform/mcu/xr871/project/common/framework/img_ctrl.h create mode 100644 platform/mcu/xr871/project/common/framework/img_ctrl_debug.h create mode 100644 platform/mcu/xr871/project/common/framework/net_ctrl.c create mode 100644 platform/mcu/xr871/project/common/framework/net_ctrl.h create mode 100644 platform/mcu/xr871/project/common/framework/net_ctrl_debug.h create mode 100644 platform/mcu/xr871/project/common/framework/net_sys.c create mode 100644 platform/mcu/xr871/project/common/framework/platform_init.c create mode 100644 platform/mcu/xr871/project/common/framework/platform_init.h create mode 100644 platform/mcu/xr871/project/common/framework/sys_ctrl/container.c create mode 100644 platform/mcu/xr871/project/common/framework/sys_ctrl/container.h create mode 100644 platform/mcu/xr871/project/common/framework/sys_ctrl/event_queue.c create mode 100644 platform/mcu/xr871/project/common/framework/sys_ctrl/event_queue.h create mode 100644 platform/mcu/xr871/project/common/framework/sys_ctrl/observer.c create mode 100644 platform/mcu/xr871/project/common/framework/sys_ctrl/observer.h create mode 100644 platform/mcu/xr871/project/common/framework/sys_ctrl/publisher.c create mode 100644 platform/mcu/xr871/project/common/framework/sys_ctrl/publisher.h create mode 100644 platform/mcu/xr871/project/common/framework/sys_ctrl/sys_ctrl.c create mode 100644 platform/mcu/xr871/project/common/framework/sys_ctrl/sys_ctrl.h create mode 100644 platform/mcu/xr871/project/common/framework/sysinfo.c create mode 100644 platform/mcu/xr871/project/common/framework/sysinfo.h create mode 100644 platform/mcu/xr871/project/common/framework/sysinfo_debug.h create mode 100644 platform/mcu/xr871/project/common/startup/gcc/exception.c create mode 100644 platform/mcu/xr871/project/common/startup/gcc/retarget.c create mode 100644 platform/mcu/xr871/project/common/startup/gcc/retarget_main.c create mode 100644 platform/mcu/xr871/project/common/startup/gcc/retarget_stdout.c create mode 100644 platform/mcu/xr871/project/common/startup/gcc/startup.S create mode 100644 platform/mcu/xr871/project/main/main.c create mode 100644 platform/mcu/xr871/project/main/prj_config.h create mode 100644 platform/mcu/xr871/project/project.mk create mode 100644 platform/mcu/xr871/src/audio/audio_manager/audio_manager.c create mode 100644 platform/mcu/xr871/src/audio/audio_manager/audio_manager.mk create mode 100644 platform/mcu/xr871/src/audio/audio_pcm/audio_pcm.c create mode 100644 platform/mcu/xr871/src/audio/audio_pcm/audio_pcm.mk create mode 100644 platform/mcu/xr871/src/cedarx/Cdx2.0Plugin/include/AudioDec_Decode.h create mode 100644 platform/mcu/xr871/src/cedarx/Cdx2.0Plugin/include/CDX_Common.h create mode 100644 platform/mcu/xr871/src/cedarx/Cdx2.0Plugin/include/CDX_Fileformat.h create mode 100644 platform/mcu/xr871/src/cedarx/Cdx2.0Plugin/include/GetAudio_format.h create mode 100644 platform/mcu/xr871/src/cedarx/Cdx2.0Plugin/include/PostProcessCom.h create mode 100644 platform/mcu/xr871/src/cedarx/Cdx2.0Plugin/include/STTypes.h create mode 100644 platform/mcu/xr871/src/cedarx/Cdx2.0Plugin/include/TouchToStretch.h create mode 100644 platform/mcu/xr871/src/cedarx/Cdx2.0Plugin/include/ad_cedarlib_com.h create mode 100644 platform/mcu/xr871/src/cedarx/Cdx2.0Plugin/include/adecoder.h create mode 100644 platform/mcu/xr871/src/cedarx/Cdx2.0Plugin/include/auGaincom.h create mode 100644 platform/mcu/xr871/src/cedarx/Cdx2.0Plugin/include/aumixcom.h create mode 100644 platform/mcu/xr871/src/cedarx/Cdx2.0Plugin/include/cedar_abs_packet_hdr.h create mode 100644 platform/mcu/xr871/src/cedarx/Cdx2.0Plugin/include/log.h create mode 100644 platform/mcu/xr871/src/cedarx/Cdx2.0Plugin/include/rw_data_api.h create mode 100644 platform/mcu/xr871/src/cedarx/Cdx2.0Plugin/tools/memcheck/memcheck.h create mode 100644 platform/mcu/xr871/src/cedarx/Cdx2.0Plugin/tools/memcheck/memname.h create mode 100644 platform/mcu/xr871/src/cedarx/CdxEncPlugin/Android.mk create mode 100644 platform/mcu/xr871/src/cedarx/CdxEncPlugin/Makefile create mode 100644 platform/mcu/xr871/src/cedarx/CdxEncPlugin/aacencApi.h create mode 100644 platform/mcu/xr871/src/cedarx/CdxEncPlugin/aenc_sw_lib.h create mode 100644 platform/mcu/xr871/src/cedarx/CdxEncPlugin/amrencApi.h create mode 100644 platform/mcu/xr871/src/cedarx/CdxEncPlugin/config.mk create mode 100644 platform/mcu/xr871/src/cedarx/CdxEncPlugin/include/aencoder.h create mode 100644 platform/mcu/xr871/src/cedarx/CdxEncPlugin/include/log.h create mode 100644 platform/mcu/xr871/src/cedarx/CdxEncPlugin/mp3encApi.h create mode 100644 platform/mcu/xr871/src/cedarx/CdxEncPlugin/pcm_enc.h create mode 100644 platform/mcu/xr871/src/cedarx/Makefile create mode 100644 platform/mcu/xr871/src/cedarx/awrecorder/AudioEncodeComponent.h create mode 100644 platform/mcu/xr871/src/cedarx/awrecorder/EncDataComponent.h create mode 100644 platform/mcu/xr871/src/cedarx/awrecorder/Makefile.am create mode 100644 platform/mcu/xr871/src/cedarx/awrecorder/awencoder.h create mode 100644 platform/mcu/xr871/src/cedarx/base/include/AwMessageQueue.h create mode 100644 platform/mcu/xr871/src/cedarx/base/include/AwPool.h create mode 100644 platform/mcu/xr871/src/cedarx/base/include/CdxAtomic.h create mode 100644 platform/mcu/xr871/src/cedarx/base/include/CdxBase64.h create mode 100644 platform/mcu/xr871/src/cedarx/base/include/CdxBinary.h create mode 100644 platform/mcu/xr871/src/cedarx/base/include/CdxBitReader.h create mode 100644 platform/mcu/xr871/src/cedarx/base/include/CdxBuffer.h create mode 100644 platform/mcu/xr871/src/cedarx/base/include/CdxDebug.h create mode 100644 platform/mcu/xr871/src/cedarx/base/include/CdxEnumCommon.h create mode 100644 platform/mcu/xr871/src/cedarx/base/include/CdxISOLang.h create mode 100644 platform/mcu/xr871/src/cedarx/base/include/CdxKeyedVector.h create mode 100644 platform/mcu/xr871/src/cedarx/base/include/CdxList.h create mode 100644 platform/mcu/xr871/src/cedarx/base/include/CdxList.i create mode 100644 platform/mcu/xr871/src/cedarx/base/include/CdxLock.h create mode 100644 platform/mcu/xr871/src/cedarx/base/include/CdxMemory.h create mode 100644 platform/mcu/xr871/src/cedarx/base/include/CdxMessage.h create mode 100644 platform/mcu/xr871/src/cedarx/base/include/CdxMeta.h create mode 100644 platform/mcu/xr871/src/cedarx/base/include/CdxQueue.h create mode 100644 platform/mcu/xr871/src/cedarx/base/include/CdxSocketUtil.h create mode 100644 platform/mcu/xr871/src/cedarx/base/include/CdxStrUtil.h create mode 100644 platform/mcu/xr871/src/cedarx/base/include/CdxSysinfo.h create mode 100644 platform/mcu/xr871/src/cedarx/base/include/CdxTime.h create mode 100644 platform/mcu/xr871/src/cedarx/base/include/CdxTypes.h create mode 100644 platform/mcu/xr871/src/cedarx/base/include/CdxUrl.h create mode 100644 platform/mcu/xr871/src/cedarx/base/include/SmartDnsService.h create mode 100644 platform/mcu/xr871/src/cedarx/base/include/cdx_log.h create mode 100644 platform/mcu/xr871/src/cedarx/base/include/cdx_malloc_dbg.h create mode 100644 platform/mcu/xr871/src/cedarx/cedarx.a create mode 100644 platform/mcu/xr871/src/cedarx/cedarx.mk create mode 100644 platform/mcu/xr871/src/cedarx/cedarx_version.h create mode 100644 platform/mcu/xr871/src/cedarx/common/iniparser/iniparserapi.h create mode 100644 platform/mcu/xr871/src/cedarx/external/include/adecoder/adecoder.h create mode 100644 platform/mcu/xr871/src/cedarx/external/include/aencoder/aencoder.h create mode 100644 platform/mcu/xr871/src/cedarx/external/include/zlib/zconf.h create mode 100644 platform/mcu/xr871/src/cedarx/external/include/zlib/zlib.h create mode 100644 platform/mcu/xr871/src/cedarx/muxer/Makefile.am create mode 100644 platform/mcu/xr871/src/cedarx/muxer/RecoderCbWriter.h create mode 100644 platform/mcu/xr871/src/cedarx/muxer/RecoderWriter.h create mode 100644 platform/mcu/xr871/src/cedarx/muxer/aac/CdxAacMuxer.h create mode 100644 platform/mcu/xr871/src/cedarx/muxer/aac/Makefile.am create mode 100644 platform/mcu/xr871/src/cedarx/muxer/base/CdxMuxerBaseDef.h create mode 100644 platform/mcu/xr871/src/cedarx/muxer/base/CdxTsemaphore.h create mode 100644 platform/mcu/xr871/src/cedarx/muxer/base/Makefile.am create mode 100644 platform/mcu/xr871/src/cedarx/muxer/include/CdxMuxer.h create mode 100644 platform/mcu/xr871/src/cedarx/muxer/include/CdxWriter.h create mode 100644 platform/mcu/xr871/src/cedarx/os_glue/include/atomic.h create mode 100644 platform/mcu/xr871/src/cedarx/os_glue/include/cedarx_fs.h create mode 100644 platform/mcu/xr871/src/cedarx/os_glue/include/list.h create mode 100644 platform/mcu/xr871/src/cedarx/os_glue/include/pthread.h create mode 100644 platform/mcu/xr871/src/cedarx/os_glue/include/unistd.h create mode 100644 platform/mcu/xr871/src/cedarx/parser/amr/CdxAmrParser.h create mode 100644 platform/mcu/xr871/src/cedarx/parser/base/id3base/BaseUtils.h create mode 100644 platform/mcu/xr871/src/cedarx/parser/base/id3base/CdxMetaData.h create mode 100644 platform/mcu/xr871/src/cedarx/parser/base/id3base/CdxUtfCode.h create mode 100644 platform/mcu/xr871/src/cedarx/parser/base/id3base/Id3Base.h create mode 100644 platform/mcu/xr871/src/cedarx/parser/base/id3base/StringContainer.h create mode 100644 platform/mcu/xr871/src/cedarx/parser/id3v2/CdxId3v2Parser.h create mode 100644 platform/mcu/xr871/src/cedarx/parser/include/CdxParser.h create mode 100644 platform/mcu/xr871/src/cedarx/parser/mp3/CdxMp3Parser.h create mode 100644 platform/mcu/xr871/src/cedarx/playback/audioDecComponent.h create mode 100644 platform/mcu/xr871/src/cedarx/playback/audioRenderComponent.h create mode 100644 platform/mcu/xr871/src/cedarx/playback/avtimer.h create mode 100644 platform/mcu/xr871/src/cedarx/playback/baseComponent.h create mode 100644 platform/mcu/xr871/src/cedarx/playback/bitrateEstimater.h create mode 100644 platform/mcu/xr871/src/cedarx/playback/framerateEstimater.h create mode 100644 platform/mcu/xr871/src/cedarx/playback/include/player.h create mode 100644 platform/mcu/xr871/src/cedarx/playback/include/soundControl.h create mode 100644 platform/mcu/xr871/src/cedarx/playback/player_i.h create mode 100644 platform/mcu/xr871/src/cedarx/playback/streamManager.h create mode 100644 platform/mcu/xr871/src/cedarx/record/include/CaptureControl.h create mode 100644 platform/mcu/xr871/src/cedarx/stream/http/CdxHttpStream.h create mode 100644 platform/mcu/xr871/src/cedarx/stream/include/CdxSeqBuffer.h create mode 100644 platform/mcu/xr871/src/cedarx/stream/include/CdxStream.h create mode 100644 platform/mcu/xr871/src/cedarx/stream/include/CdxStreamErrno.h create mode 100644 platform/mcu/xr871/src/cedarx/xplayer/cache.h create mode 100644 platform/mcu/xr871/src/cedarx/xplayer/demuxComponent.h create mode 100644 platform/mcu/xr871/src/cedarx/xplayer/include/mediaInfo.h create mode 100644 platform/mcu/xr871/src/cedarx/xplayer/include/xplayer.h create mode 100644 platform/mcu/xr871/src/cedarx/xrecoder/include/xrecord.h create mode 100644 platform/mcu/xr871/src/console/Makefile create mode 100644 platform/mcu/xr871/src/console/console.c create mode 100644 platform/mcu/xr871/src/console/console.mk create mode 100644 platform/mcu/xr871/src/console/console_debug.h create mode 100644 platform/mcu/xr871/src/driver/chip/chip.mk create mode 100644 platform/mcu/xr871/src/driver/chip/codec/codec.h create mode 100644 platform/mcu/xr871/src/driver/chip/codec/hal_ac101.c create mode 100644 platform/mcu/xr871/src/driver/chip/codec/hal_ac101.h create mode 100644 platform/mcu/xr871/src/driver/chip/codec/hal_codec.c create mode 100644 platform/mcu/xr871/src/driver/chip/flashchip/flash_chip.c create mode 100644 platform/mcu/xr871/src/driver/chip/flashchip/flash_default.c create mode 100644 platform/mcu/xr871/src/driver/chip/flashchip/flash_default.h create mode 100644 platform/mcu/xr871/src/driver/chip/hal_adc.c create mode 100644 platform/mcu/xr871/src/driver/chip/hal_base.h create mode 100644 platform/mcu/xr871/src/driver/chip/hal_board.c create mode 100644 platform/mcu/xr871/src/driver/chip/hal_ccm.c create mode 100644 platform/mcu/xr871/src/driver/chip/hal_crypto.c create mode 100644 platform/mcu/xr871/src/driver/chip/hal_csi.c create mode 100644 platform/mcu/xr871/src/driver/chip/hal_debug.h create mode 100644 platform/mcu/xr871/src/driver/chip/hal_dma.c create mode 100644 platform/mcu/xr871/src/driver/chip/hal_dmic.c create mode 100644 platform/mcu/xr871/src/driver/chip/hal_efuse.c create mode 100644 platform/mcu/xr871/src/driver/chip/hal_flash.c create mode 100644 platform/mcu/xr871/src/driver/chip/hal_flashcache.c create mode 100644 platform/mcu/xr871/src/driver/chip/hal_flashctrl.c create mode 100644 platform/mcu/xr871/src/driver/chip/hal_global.c create mode 100644 platform/mcu/xr871/src/driver/chip/hal_gpio.c create mode 100644 platform/mcu/xr871/src/driver/chip/hal_i2c.c create mode 100644 platform/mcu/xr871/src/driver/chip/hal_i2s.c create mode 100644 platform/mcu/xr871/src/driver/chip/hal_irrx.c create mode 100644 platform/mcu/xr871/src/driver/chip/hal_irtx.c create mode 100644 platform/mcu/xr871/src/driver/chip/hal_mbox.c create mode 100644 platform/mcu/xr871/src/driver/chip/hal_nvic.c create mode 100644 platform/mcu/xr871/src/driver/chip/hal_os.h create mode 100644 platform/mcu/xr871/src/driver/chip/hal_prcm.c create mode 100644 platform/mcu/xr871/src/driver/chip/hal_pwm.c create mode 100644 platform/mcu/xr871/src/driver/chip/hal_rtc.c create mode 100644 platform/mcu/xr871/src/driver/chip/hal_spi.c create mode 100644 platform/mcu/xr871/src/driver/chip/hal_spinlock.c create mode 100644 platform/mcu/xr871/src/driver/chip/hal_timer.c create mode 100644 platform/mcu/xr871/src/driver/chip/hal_uart.c create mode 100644 platform/mcu/xr871/src/driver/chip/hal_util.c create mode 100644 platform/mcu/xr871/src/driver/chip/hal_wakeup.c create mode 100644 platform/mcu/xr871/src/driver/chip/hal_wdg.c create mode 100644 platform/mcu/xr871/src/driver/chip/hal_xip.c create mode 100644 platform/mcu/xr871/src/driver/chip/ir_nec.c create mode 100644 platform/mcu/xr871/src/driver/chip/sdmmc/_sd.h create mode 100644 platform/mcu/xr871/src/driver/chip/sdmmc/core.c create mode 100644 platform/mcu/xr871/src/driver/chip/sdmmc/core.h create mode 100644 platform/mcu/xr871/src/driver/chip/sdmmc/hal_sdhost.c create mode 100644 platform/mcu/xr871/src/driver/chip/sdmmc/sd.c create mode 100644 platform/mcu/xr871/src/driver/chip/sdmmc/sdhost.h create mode 100644 platform/mcu/xr871/src/driver/chip/sdmmc/test.c create mode 100644 platform/mcu/xr871/src/driver/chip/system_chip.c create mode 100644 platform/mcu/xr871/src/efpg/efpg.c create mode 100644 platform/mcu/xr871/src/efpg/efpg.mk create mode 100644 platform/mcu/xr871/src/efpg/efpg_debug.h create mode 100644 platform/mcu/xr871/src/efpg/efpg_efuse.c create mode 100644 platform/mcu/xr871/src/efpg/efpg_frame.c create mode 100644 platform/mcu/xr871/src/efpg/efpg_i.h create mode 100644 platform/mcu/xr871/src/image/fdcm.c create mode 100644 platform/mcu/xr871/src/image/flash.c create mode 100644 platform/mcu/xr871/src/image/flash.h create mode 100644 platform/mcu/xr871/src/image/image.c create mode 100644 platform/mcu/xr871/src/image/image.mk create mode 100644 platform/mcu/xr871/src/image/image_debug.h create mode 100644 platform/mcu/xr871/src/net/lwip/checksum.c create mode 100644 platform/mcu/xr871/src/net/lwip/lwip.mk create mode 100644 platform/mcu/xr871/src/net/lwip/memcpy.c create mode 100644 platform/mcu/xr871/src/net/udhcp/AUTHORS create mode 100644 platform/mcu/xr871/src/net/udhcp/COPYING create mode 100644 platform/mcu/xr871/src/net/udhcp/ChangeLog create mode 100644 platform/mcu/xr871/src/net/udhcp/Makefile create mode 100644 platform/mcu/xr871/src/net/udhcp/README create mode 100644 platform/mcu/xr871/src/net/udhcp/README.dumpleases create mode 100644 platform/mcu/xr871/src/net/udhcp/README.udhcpc create mode 100644 platform/mcu/xr871/src/net/udhcp/README.udhcpd create mode 100644 platform/mcu/xr871/src/net/udhcp/TODO create mode 100644 platform/mcu/xr871/src/net/udhcp/arpping.c create mode 100644 platform/mcu/xr871/src/net/udhcp/arpping.h create mode 100644 platform/mcu/xr871/src/net/udhcp/clientpacket.c create mode 100644 platform/mcu/xr871/src/net/udhcp/clientpacket.h create mode 100644 platform/mcu/xr871/src/net/udhcp/debug.h create mode 100644 platform/mcu/xr871/src/net/udhcp/dhcp_time.c create mode 100644 platform/mcu/xr871/src/net/udhcp/dhcp_time.h create mode 100644 platform/mcu/xr871/src/net/udhcp/dhcpc.c create mode 100644 platform/mcu/xr871/src/net/udhcp/dhcpc.h create mode 100644 platform/mcu/xr871/src/net/udhcp/dhcpd.c create mode 100644 platform/mcu/xr871/src/net/udhcp/dhcpd.h create mode 100644 platform/mcu/xr871/src/net/udhcp/dhcpd_cfg.h create mode 100644 platform/mcu/xr871/src/net/udhcp/dns.h create mode 100644 platform/mcu/xr871/src/net/udhcp/dns_server.c create mode 100644 platform/mcu/xr871/src/net/udhcp/dumpleases.1 create mode 100644 platform/mcu/xr871/src/net/udhcp/dumpleases.c create mode 100644 platform/mcu/xr871/src/net/udhcp/files.c create mode 100644 platform/mcu/xr871/src/net/udhcp/files.h create mode 100644 platform/mcu/xr871/src/net/udhcp/frontend.c create mode 100644 platform/mcu/xr871/src/net/udhcp/leases.c create mode 100644 platform/mcu/xr871/src/net/udhcp/leases.h create mode 100644 platform/mcu/xr871/src/net/udhcp/libbb_udhcp.h create mode 100644 platform/mcu/xr871/src/net/udhcp/log create mode 100644 platform/mcu/xr871/src/net/udhcp/options.c create mode 100644 platform/mcu/xr871/src/net/udhcp/options.h create mode 100644 platform/mcu/xr871/src/net/udhcp/packet.c create mode 100644 platform/mcu/xr871/src/net/udhcp/packet.h create mode 100644 platform/mcu/xr871/src/net/udhcp/pidfile.c create mode 100644 platform/mcu/xr871/src/net/udhcp/pidfile.h create mode 100644 platform/mcu/xr871/src/net/udhcp/samples/README create mode 100644 platform/mcu/xr871/src/net/udhcp/samples/sample.bound create mode 100644 platform/mcu/xr871/src/net/udhcp/samples/sample.deconfig create mode 100644 platform/mcu/xr871/src/net/udhcp/samples/sample.nak create mode 100644 platform/mcu/xr871/src/net/udhcp/samples/sample.renew create mode 100644 platform/mcu/xr871/src/net/udhcp/samples/sample.script create mode 100644 platform/mcu/xr871/src/net/udhcp/samples/simple.script create mode 100644 platform/mcu/xr871/src/net/udhcp/samples/udhcpd.conf create mode 100644 platform/mcu/xr871/src/net/udhcp/script.c create mode 100644 platform/mcu/xr871/src/net/udhcp/script.h create mode 100644 platform/mcu/xr871/src/net/udhcp/serverpacket.c create mode 100644 platform/mcu/xr871/src/net/udhcp/serverpacket.h create mode 100644 platform/mcu/xr871/src/net/udhcp/socket.c create mode 100644 platform/mcu/xr871/src/net/udhcp/socket.h create mode 100644 platform/mcu/xr871/src/net/udhcp/udhcp.mk create mode 100644 platform/mcu/xr871/src/net/udhcp/udhcpc.8 create mode 100644 platform/mcu/xr871/src/net/udhcp/udhcpd.8 create mode 100644 platform/mcu/xr871/src/net/udhcp/udhcpd.conf.5 create mode 100644 platform/mcu/xr871/src/net/udhcp/usr_dhcpd.c create mode 100644 platform/mcu/xr871/src/net/wlan/airkiss/airkiss.h create mode 100644 platform/mcu/xr871/src/net/wlan/airkiss/airkiss_ack.c create mode 100644 platform/mcu/xr871/src/net/wlan/airkiss/airkiss_discover.c create mode 100644 platform/mcu/xr871/src/net/wlan/airkiss/airkiss_main.c create mode 100644 platform/mcu/xr871/src/net/wlan/ethernetif.c create mode 100644 platform/mcu/xr871/src/net/wlan/smartconfig/smart_config_ack.c create mode 100644 platform/mcu/xr871/src/net/wlan/smartconfig/smart_config_crc.c create mode 100644 platform/mcu/xr871/src/net/wlan/smartconfig/smart_config_crc.h create mode 100644 platform/mcu/xr871/src/net/wlan/smartconfig/smart_config_decode.c create mode 100644 platform/mcu/xr871/src/net/wlan/smartconfig/smart_config_decode.h create mode 100644 platform/mcu/xr871/src/net/wlan/smartconfig/smart_config_main.c create mode 100644 platform/mcu/xr871/src/net/wlan/wlan.c create mode 100644 platform/mcu/xr871/src/net/wlan/wlan.mk create mode 100644 platform/mcu/xr871/src/net/wlan/wlan_ctrl.c create mode 100644 platform/mcu/xr871/src/net/wlan/wlan_debug.h create mode 100644 platform/mcu/xr871/src/net/wlan/wpa_ctrl_req.h create mode 100644 platform/mcu/xr871/src/ota/ota.c create mode 100644 platform/mcu/xr871/src/ota/ota.mk create mode 100644 platform/mcu/xr871/src/ota/ota_debug.h create mode 100644 platform/mcu/xr871/src/ota/ota_file.c create mode 100644 platform/mcu/xr871/src/ota/ota_file.h create mode 100644 platform/mcu/xr871/src/ota/ota_http.c create mode 100644 platform/mcu/xr871/src/ota/ota_http.h create mode 100644 platform/mcu/xr871/src/ota/ota_i.h create mode 100644 platform/mcu/xr871/src/pm/pm.c create mode 100644 platform/mcu/xr871/src/pm/pm.mk create mode 100644 platform/mcu/xr871/src/pm/pm_i.h create mode 100644 platform/mcu/xr871/src/pm/port.c create mode 100644 platform/mcu/xr871/src/pm/port.h create mode 100644 platform/mcu/xr871/src/sys/ducc/ducc.c create mode 100644 platform/mcu/xr871/src/sys/ducc/ducc.h create mode 100644 platform/mcu/xr871/src/sys/ducc/ducc_app.c create mode 100644 platform/mcu/xr871/src/sys/ducc/ducc_debug.h create mode 100644 platform/mcu/xr871/src/sys/ducc/ducc_hw_mbox.c create mode 100644 platform/mcu/xr871/src/sys/ducc/ducc_hw_mbox.h create mode 100644 platform/mcu/xr871/src/sys/ducc/ducc_mbox.c create mode 100644 platform/mcu/xr871/src/sys/ducc/ducc_mbox.h create mode 100644 platform/mcu/xr871/src/sys/ducc/ducc_os.h create mode 100644 platform/mcu/xr871/src/sys/mbuf/mbuf_1.c create mode 100644 platform/mcu/xr871/src/sys/mbuf/mbuf_debug.h create mode 100644 platform/mcu/xr871/src/sys/mbuf/mbuf_util.h create mode 100644 platform/mcu/xr871/src/sys/sys.mk create mode 100644 platform/mcu/xr871/src/xz/decompress.c create mode 100644 platform/mcu/xr871/src/xz/xz.h create mode 100644 platform/mcu/xr871/src/xz/xz.mk create mode 100644 platform/mcu/xr871/src/xz/xz_config.h create mode 100644 platform/mcu/xr871/src/xz/xz_crc32.c create mode 100644 platform/mcu/xr871/src/xz/xz_dec_lzma2.c create mode 100644 platform/mcu/xr871/src/xz/xz_dec_stream.c create mode 100644 platform/mcu/xr871/src/xz/xz_lzma2.h create mode 100644 platform/mcu/xr871/src/xz/xz_private.h create mode 100644 platform/mcu/xr871/src/xz/xz_stream.h create mode 100644 platform/mcu/xr871/tools/calc_flash_offs create mode 100644 platform/mcu/xr871/tools/mkimage create mode 100644 platform/mcu/xr871/tools/mkimage.exe create mode 100644 platform/mcu/xr871/tools/phoenixMC.exe create mode 100644 platform/mcu/xr871/tools/phoenixMC_Linux create mode 100644 platform/mcu/xr871/tools/xz64 create mode 100644 platform/mcu/xr871/xr871-xip.ld create mode 100644 platform/mcu/xr871/xr871.ld create mode 100644 platform/mcu/xr871/xr871.mk diff --git a/board/xr871evb/board.c b/board/xr871evb/board.c new file mode 100644 index 0000000000..b770d832a4 --- /dev/null +++ b/board/xr871evb/board.c @@ -0,0 +1,260 @@ + +#include "driver/chip/hal_def.h" +#include "driver/chip/hal_adc.h" +#include "aos/hal/adc_priv.h" +#include "hal/soc/soc.h" +#include +#include + +/* + 0x0 0x100000 + |--------------------------|--------------------------| + |bootloader(32K) |ota cfg(32K) | + |--------------------------|--------------------------| + |application |ota | + | | | + | | | + | | | + | | | + | | | + | | | + | | | + | | | + |--------------------------|--------------------------| + | |PARAM1(4K) | + |--------------------------|--------------------------| + | |PARAM2(4K) | + |--------------------------|--------------------------| + | |PARAM3(4K) | + |--------------------------|--------------------------| + |SYSINFO(4K) |PARAM4(4K) | + |--------------------------|--------------------------| + */ + +#define SPI_FLASH_CAPACITY 0x200000UL //total capacity +#define OTA_2ND_SEQ_START 0x100000UL //according to image-xip-xz.cfg file + +/* Logic partition on flash devices */ +const hal_logic_partition_t hal_partitions[] = +{ + [HAL_PARTITION_BOOTLOADER] = + { + .partition_owner = HAL_FLASH_EMBEDDED, + .partition_description = "Bootloader", + .partition_start_addr = 0x0, + .partition_length = 0x8000, //32k bytes + .partition_options = PAR_OPT_READ_EN | PAR_OPT_WRITE_DIS, + }, + [HAL_PARTITION_PARAMETER_1] = + { + .partition_owner = HAL_FLASH_EMBEDDED, + .partition_description = "PARAMETER1", + .partition_start_addr = SPI_FLASH_CAPACITY - 4 * 0x1000, + .partition_length = 0x1000, // 4k bytes + .partition_options = PAR_OPT_READ_EN | PAR_OPT_WRITE_EN, + }, + [HAL_PARTITION_PARAMETER_2] = + { + .partition_owner = HAL_FLASH_EMBEDDED, + .partition_description = "PARAMETER2", + .partition_start_addr = SPI_FLASH_CAPACITY - 3 * 0x1000, + .partition_length = 0x1000, //4k bytes + .partition_options = PAR_OPT_READ_EN | PAR_OPT_WRITE_EN, + }, + [HAL_PARTITION_APPLICATION] = + { + .partition_owner = HAL_FLASH_EMBEDDED, + .partition_description = "Application", + .partition_start_addr = 0x8000, // from 32k bytes + .partition_length = (SPI_FLASH_CAPACITY/2) - 0x8000 - 4 * 0x1000, + .partition_options = PAR_OPT_READ_EN | PAR_OPT_WRITE_EN, + }, + [HAL_PARTITION_OTA_TEMP] = + { + .partition_owner = HAL_FLASH_EMBEDDED, + .partition_description = "OTA Storage", + .partition_start_addr = OTA_2ND_SEQ_START + 0x8000, + .partition_length = (SPI_FLASH_CAPACITY/2) - 0x8000 - 4 * 0x1000, + .partition_options = PAR_OPT_READ_EN | PAR_OPT_WRITE_EN, + }, + [HAL_PARTITION_PARAMETER_3] = + { + .partition_owner = HAL_FLASH_EMBEDDED, + .partition_description = "PARAMETER3", + .partition_start_addr = SPI_FLASH_CAPACITY - 2 * 0x1000, + .partition_length = 0x1000, //4k bytes + .partition_options = PAR_OPT_READ_EN | PAR_OPT_WRITE_EN, + }, + [HAL_PARTITION_PARAMETER_4] = + { + .partition_owner = HAL_FLASH_EMBEDDED, + .partition_description = "PARAMETER4", + .partition_start_addr = SPI_FLASH_CAPACITY - 1 * 0x1000, + .partition_length = 0x1000, //4k bytes + .partition_options = PAR_OPT_READ_EN | PAR_OPT_WRITE_EN, + }, +}; + +#define BOARD_AKEY (1) //adc key ch 1 +#define BOARD_DKEY0 (6) //dkey 0 +#define BOARD_DKEY1 (7) //dkey 1 + +struct board_dkey { + gpio_dev_t gpio; + uint64_t time; +}; +static struct board_dkey board_dkeys[2]; + +static void board_dkey_irq_callback(void* arg) +{ + struct board_dkey *key = arg; + uint32_t level; + uint64_t diff; + uint16_t key_code; + + hal_gpio_input_get(key, &level); + //printf("key[%d] event: %d\n", key->gpio.port, level); + + if (level == 0 && key->time == 0) { + key->time = aos_now_ms(); + } else { + diff = aos_now_ms() - key->time; + if (key->gpio.port == BOARD_DKEY0) + key_code = CODE_BOOT; + if (key->gpio.port == BOARD_DKEY1) + key_code = CODE_VOLUME; + + if (diff > 6000) { /*long long press */ + key->time = 0; + aos_post_event(EV_KEY, key_code, VALUE_KEY_LLTCLICK); + } else if (diff > 2000) { /* long press */ + key->time = 0; + aos_post_event(EV_KEY, key_code, VALUE_KEY_LTCLICK); + } else if (diff > 40) { /* short press */ + key->time = 0; + aos_post_event(EV_KEY, key_code, VALUE_KEY_CLICK); + } + } +} + +void dkey_init(void) +{ + printf("gpio key init...\n"); + memset(&board_dkeys[0], 0, sizeof(struct board_dkey)); + board_dkeys[0].gpio.port = BOARD_DKEY0; + hal_gpio_enable_irq(&board_dkeys[0].gpio, IRQ_TRIGGER_BOTH_EDGES, board_dkey_irq_callback, &board_dkeys[0]); + + memset(&board_dkeys[1], 0, sizeof(struct board_dkey)); + board_dkeys[1].gpio.port = BOARD_DKEY1; + hal_gpio_enable_irq(&board_dkeys[1].gpio, IRQ_TRIGGER_BOTH_EDGES, board_dkey_irq_callback, &board_dkeys[1]); +} + +struct board_akey { + adc_dev_t adc; + uint64_t time; + uint32_t sample_count; + uint32_t avg_val; +}; +static struct board_akey board_adc_key; +#define AKEY12_DOWN_TOGETHER_VOL (300) +#define AKEY1_DOWN_VOL (1000) // 0.5V +#define AKEY2_DOWN_VOL (2000) // 1.05V +#define AKEY3_DOWN_VOL (3000) // 1.65V +#define AKEY_UP_VOL (3800) + +static void board_akey_irq_callback(void* arg) +{ + struct board_akey *akey = arg; + adc_priv_config_t *adc_priv_cfg = (adc_priv_config_t *)akey->adc.priv; + uint32_t level; + uint64_t diff; + uint16_t key_code; + + hal_adc_value_get(&akey->adc, &level, 0xffffffff); + + if (level < AKEY3_DOWN_VOL) { + if (akey->sample_count == 0 && akey->time == 0) { + akey->avg_val = 0; + akey->time = aos_now_ms(); + } + akey->avg_val += level; + akey->sample_count++; + if (akey->sample_count == 10) { + HAL_ADC_ConfigChannel(akey->adc.port, ADC_SELECT_ENABLE, + ADC_IRQMODE_HIGH, adc_priv_cfg->irqlow, adc_priv_cfg->irqhigh); + } + } else if (level > AKEY_UP_VOL) { + HAL_ADC_ConfigChannel(akey->adc.port, ADC_SELECT_ENABLE, + ADC_IRQMODE_LOW, adc_priv_cfg->irqlow, adc_priv_cfg->irqhigh); + diff = aos_now_ms() - akey->time; + // akey up, calc the average voltage + akey->avg_val = akey->avg_val / akey->sample_count; + printf("avg %d, count %d\n", akey->avg_val, akey->sample_count); + if (akey->avg_val < AKEY12_DOWN_TOGETHER_VOL) { + key_code = CODE_ELINK; + } else if (akey->avg_val < AKEY1_DOWN_VOL) { + key_code = CODE_NEXT; + } else if (akey->avg_val < AKEY2_DOWN_VOL) { + key_code = CODE_RECORD_PRE; + } else if (akey->avg_val < AKEY3_DOWN_VOL) { + key_code = CODE_PLAY_PAUSE; + } + + if (diff > 6000) { /*long long press */ + akey->time = 0; + akey->sample_count = 0; + aos_post_event(EV_KEY, key_code, VALUE_KEY_LLTCLICK); + } else if (diff > 2000) { /* long press */ + akey->time = 0; + akey->sample_count = 0; + aos_post_event(EV_KEY, key_code, VALUE_KEY_LTCLICK); + } else if (diff > 40) { /* short press */ + akey->time = 0; + akey->sample_count = 0; + aos_post_event(EV_KEY, key_code, VALUE_KEY_CLICK); + } + } +} + +int adc_key_init(void) +{ + HAL_Status status = HAL_ERROR; + ADC_InitParam initParam; + uint32_t akey_low_vol = 1650 + 100; // 1650mV + uint32_t akey_high_vol = 3000; // 3000mV + static adc_priv_config_t adc_priv_cfg = {0}; + + initParam.delay = 10; + initParam.freq = 500000; // default 500K clock for adc controller + status = HAL_ADC_Init(&initParam); + if (status != HAL_OK) { + printf("[adc_init]: adc init error\n"); + return -1; + } + + memset(&board_adc_key, 0, sizeof(struct board_akey)); + board_adc_key.adc.port = BOARD_AKEY; + board_adc_key.adc.config.sampling_cycle = 500000; // nothing + board_adc_key.adc.priv = &adc_priv_cfg; + adc_priv_cfg.irqmode = ADC_IRQMODE_LOW; + adc_priv_cfg.irqlow = AKEY3_DOWN_VOL; // x/4096 == vol / 2.5 + adc_priv_cfg.irqhigh = AKEY_UP_VOL; + adc_priv_cfg.irq_callback = board_akey_irq_callback; + adc_priv_cfg.arg = &board_adc_key; + hal_adc_init(&board_adc_key.adc); + + status = HAL_ADC_Start_Conv_IT(); + if (status != HAL_OK) { + printf("[adc_init]: ADC it mode start error %d\n", status); + return; + } + return 0; +} + +void board_init(void) +{ + printf("xr871evb board init...\n"); + dkey_init(); + adc_key_init(); + return; +} diff --git a/board/xr871evb/board.h b/board/xr871evb/board.h new file mode 100644 index 0000000000..64377dfbc3 --- /dev/null +++ b/board/xr871evb/board.h @@ -0,0 +1,10 @@ +#define HARDWARE_REVISION "V1.0" +#define MODEL "XR871" + +#ifdef BOOTLOADER +#define STDIO_UART 0 +#define STDIO_UART_BUADRATE 115200 +#else +#define STDIO_UART 0 +#define STDIO_UART_BUADRATE 115200 +#endif diff --git a/board/xr871evb/k_config.h b/board/xr871evb/k_config.h new file mode 100644 index 0000000000..910a2a56fa --- /dev/null +++ b/board/xr871evb/k_config.h @@ -0,0 +1,224 @@ +/* + * Copyright (C) 2015-2017 Alibaba Group Holding Limited + */ + +#ifndef CONFIG_H +#define CONFIG_H + +/* chip level conf */ +#ifndef RHINO_CONFIG_LITTLE_ENDIAN +#define RHINO_CONFIG_LITTLE_ENDIAN 1 +#endif +#ifndef RHINO_CONFIG_CPU_STACK_DOWN +#define RHINO_CONFIG_CPU_STACK_DOWN 1 +#endif + +/* kernel feature conf */ +#ifndef RHINO_CONFIG_SEM +#define RHINO_CONFIG_SEM 1 +#endif +#ifndef RHINO_CONFIG_QUEUE +#define RHINO_CONFIG_QUEUE 1 +#endif +#ifndef RHINO_CONFIG_TASK_SEM +#define RHINO_CONFIG_TASK_SEM 1 +#endif +#ifndef RHINO_CONFIG_EVENT_FLAG +#define RHINO_CONFIG_EVENT_FLAG 1 +#endif +#ifndef RHINO_CONFIG_TIMER +#define RHINO_CONFIG_TIMER 1 +#endif +#ifndef RHINO_CONFIG_BUF_QUEUE +#define RHINO_CONFIG_BUF_QUEUE 1 +#endif +#ifndef RHINO_CONFIG_MM_BLK +#define RHINO_CONFIG_MM_BLK 1 +#endif +#ifndef RHINO_CONFIG_MM_DEBUG +#define RHINO_CONFIG_MM_DEBUG 1 +#endif +#ifndef RHINO_CONFIG_MM_TLF +#define RHINO_CONFIG_MM_TLF 1 +#endif +#ifndef RHINO_CONFIG_MM_TLF_BLK_SIZE +#define RHINO_CONFIG_MM_TLF_BLK_SIZE 8192 +#endif +#define K_MM_STATISTIC 1 +#ifndef RHINO_CONFIG_MM_MAXMSIZEBIT +#define RHINO_CONFIG_MM_MAXMSIZEBIT 19 +#endif +#ifndef RHINO_CONFIG_GCC_RETADDR +#define RHINO_CONFIG_GCC_RETADDR 1 +#endif +#ifndef RHINO_CONFIG_MM_LEAKCHECK +#define RHINO_CONFIG_MM_LEAKCHECK 0 +#endif +#ifndef RHINO_CONFIG_RINGBUF_VENDOR +#define RHINO_CONFIG_RINGBUF_VENDOR 0 +#endif + +#ifndef RHINO_CONFIG_KOBJ_SET +#define RHINO_CONFIG_KOBJ_SET 1 +#endif + +/* kernel task conf */ +#ifndef RHINO_CONFIG_TASK_SUSPEND +#define RHINO_CONFIG_TASK_SUSPEND 1 +#endif +#ifndef RHINO_CONFIG_TASK_INFO +#define RHINO_CONFIG_TASK_INFO 1 +#endif +#ifndef RHINO_CONFIG_TASK_DEL +#define RHINO_CONFIG_TASK_DEL 1 +#endif + +#ifndef RHINO_CONFIG_TASK_STACK_CUR_CHECK +#define RHINO_CONFIG_TASK_STACK_CUR_CHECK 1 +#endif + +#ifndef RHINO_CONFIG_TASK_WAIT_ABORT +#define RHINO_CONFIG_TASK_WAIT_ABORT 1 +#endif +#ifndef RHINO_CONFIG_TASK_STACK_OVF_CHECK +#define RHINO_CONFIG_TASK_STACK_OVF_CHECK 1 +#endif +#ifndef RHINO_CONFIG_SCHED_RR +#define RHINO_CONFIG_SCHED_RR 1 +#endif +#ifndef RHINO_CONFIG_TIME_SLICE_DEFAULT +#define RHINO_CONFIG_TIME_SLICE_DEFAULT 50 +#endif +#ifndef RHINO_CONFIG_PRI_MAX +#define RHINO_CONFIG_PRI_MAX 62 +#endif +#ifndef RHINO_CONFIG_USER_PRI_MAX +#define RHINO_CONFIG_USER_PRI_MAX (RHINO_CONFIG_PRI_MAX - 2) +#endif + +/* kernel workqueue conf */ +//#ifndef RHINO_CONFIG_WORKQUEUE +#define RHINO_CONFIG_WORKQUEUE 1 +//#endif +#ifndef RHINO_CONFIG_WORKQUEUE_STACK_SIZE +#define RHINO_CONFIG_WORKQUEUE_STACK_SIZE 768 +#endif + +/* kernel mm_region conf */ +#ifndef RHINO_CONFIG_MM_REGION_MUTEX +#define RHINO_CONFIG_MM_REGION_MUTEX 0 +#endif + +/* kernel timer&tick conf */ +#ifndef RHINO_CONFIG_HW_COUNT +#define RHINO_CONFIG_HW_COUNT 0 +#endif +#ifndef RHINO_CONFIG_TICK_TASK +#define RHINO_CONFIG_TICK_TASK 0 +#endif + +#if (RHINO_CONFIG_TICK_TASK > 0) +#ifndef RHINO_CONFIG_TICK_TASK_STACK_SIZE +#define RHINO_CONFIG_TICK_TASK_STACK_SIZE 256 +#endif +#ifndef RHINO_CONFIG_TICK_TASK_PRI +#define RHINO_CONFIG_TICK_TASK_PRI 1 +#endif +#endif + +#ifndef RHINO_CONFIG_TICKLESS +#define RHINO_CONFIG_TICKLESS 0 +#endif +#ifndef RHINO_CONFIG_TICKS_PER_SECOND +#define RHINO_CONFIG_TICKS_PER_SECOND 100 +#endif +/* must be 2^n size!, such as 1, 2, 4, 8, 16,32, etc....... */ +#ifndef RHINO_CONFIG_TICK_HEAD_ARRAY +#define RHINO_CONFIG_TICK_HEAD_ARRAY 8 +#endif + +/*must reserve enough stack size for timer cb will consume*/ +#ifndef RHINO_CONFIG_TIMER_TASK_STACK_SIZE +#define RHINO_CONFIG_TIMER_TASK_STACK_SIZE 300 +#endif +#ifndef RHINO_CONFIG_TIMER_RATE +#define RHINO_CONFIG_TIMER_RATE 1 +#endif +#ifndef RHINO_CONFIG_TIMER_TASK_PRI +#define RHINO_CONFIG_TIMER_TASK_PRI 5 +#endif + +/* kernel intrpt conf */ +#ifndef RHINO_CONFIG_INTRPT_STACK_REMAIN_GET +#define RHINO_CONFIG_INTRPT_STACK_REMAIN_GET 0 +#endif +#ifndef RHINO_CONFIG_INTRPT_STACK_OVF_CHECK +#define RHINO_CONFIG_INTRPT_STACK_OVF_CHECK 0 +#endif +#ifndef RHINO_CONFIG_INTRPT_MAX_NESTED_LEVEL +#define RHINO_CONFIG_INTRPT_MAX_NESTED_LEVEL 188u +#endif +#ifndef RHINO_CONFIG_INTRPT_GUARD +#define RHINO_CONFIG_INTRPT_GUARD 0 +#endif + +/* kernel dyn alloc conf */ +#ifndef RHINO_CONFIG_KOBJ_DYN_ALLOC +#define RHINO_CONFIG_KOBJ_DYN_ALLOC 1 +#endif + +#if (RHINO_CONFIG_KOBJ_DYN_ALLOC > 0) +#ifndef RHINO_CONFIG_K_DYN_QUEUE_MSG +#define RHINO_CONFIG_K_DYN_QUEUE_MSG 30 +#endif +#ifndef RHINO_CONFIG_K_DYN_TASK_STACK +#define RHINO_CONFIG_K_DYN_TASK_STACK 256 +#endif +#ifndef RHINO_CONFIG_K_DYN_MEM_TASK_PRI +#define RHINO_CONFIG_K_DYN_MEM_TASK_PRI 6 +#endif +#endif + +/* kernel idle conf */ +#ifndef RHINO_CONFIG_IDLE_TASK_STACK_SIZE +#define RHINO_CONFIG_IDLE_TASK_STACK_SIZE 200 +#endif + +/* kernel hook conf */ +#ifndef RHINO_CONFIG_USER_HOOK +#define RHINO_CONFIG_USER_HOOK 0 +#endif + +/* kernel stats conf */ +#ifndef RHINO_CONFIG_SYSTEM_STATS +#define RHINO_CONFIG_SYSTEM_STATS 1 +#endif +#ifndef RHINO_CONFIG_DISABLE_SCHED_STATS +#define RHINO_CONFIG_DISABLE_SCHED_STATS 0 +#endif +#ifndef RHINO_CONFIG_DISABLE_INTRPT_STATS +#define RHINO_CONFIG_DISABLE_INTRPT_STATS 0 +#endif +#ifndef RHINO_CONFIG_CPU_USAGE_STATS +#define RHINO_CONFIG_CPU_USAGE_STATS 0 +#endif +#ifndef RHINO_CONFIG_CPU_USAGE_TASK_PRI +#define RHINO_CONFIG_CPU_USAGE_TASK_PRI (RHINO_CONFIG_PRI_MAX - 2) +#endif +#ifndef RHINO_CONFIG_TASK_SCHED_STATS +#define RHINO_CONFIG_TASK_SCHED_STATS 0 +#endif +#ifndef RHINO_CONFIG_CPU_USAGE_TASK_STACK +#define RHINO_CONFIG_CPU_USAGE_TASK_STACK 256 +#endif + +#ifndef RHINO_CONFIG_CPU_NUM +#define RHINO_CONFIG_CPU_NUM 1 +#endif + +/* kernel trace conf */ +#ifndef RHINO_CONFIG_TRACE +#define RHINO_CONFIG_TRACE 0 +#endif + +#endif /* CONFIG_H */ diff --git a/board/xr871evb/xr871evb.mk b/board/xr871evb/xr871evb.mk new file mode 100644 index 0000000000..609fb8894e --- /dev/null +++ b/board/xr871evb/xr871evb.mk @@ -0,0 +1,61 @@ +############################################################################### +# +# The MIT License +# Copyright (c) 2016 MXCHIP Inc. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is furnished +# to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +# IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +############################################################################### + +NAME := board_xr871evb + +JTAG := jlink + +MODULE := XR871EVB +HOST_ARCH := Cortex-M4 +HOST_MCU_FAMILY := xr871 + +$(NAME)_SOURCES := board.c + +GLOBAL_INCLUDES += . +GLOBAL_DEFINES += STDIO_UART=0 + +CURRENT_TIME = $(shell /bin/date +%Y%m%d.%H%M) +define get-os-version +"AOS-R"-$(CURRENT_TIME) +endef + +CONFIG_SYSINFO_OS_VERSION := $(call get-os-version) + +$(warning $(CONFIG_SYSINFO_OS_VERSION)) + +CONFIG_SYSINFO_PRODUCT_MODEL := ALI_AOS_XR871 +CONFIG_SYSINFO_DEVICE_NAME := XR871 +$(warning ${CONFIG_SYSINFO_OS_VERSION}) + +GLOBAL_CFLAGS += -DSYSINFO_OS_VERSION=\"$(CONFIG_SYSINFO_OS_VERSION)\" +GLOBAL_CFLAGS += -DSYSINFO_PRODUCT_MODEL=\"$(CONFIG_SYSINFO_PRODUCT_MODEL)\" +GLOBAL_CFLAGS += -DSYSINFO_DEVICE_NAME=\"$(CONFIG_SYSINFO_DEVICE_NAME)\" +GLOBAL_CFLAGS += -DSYSINFO_ARCH=\"$(HOST_ARCH)\" +GLOBAL_CFLAGS += -DSYSINFO_MCU=\"$(HOST_MCU_FAMILY)\" +GLOBAL_CFLAGS += -DSYSINFO_BOARD=\"$(MODULE)\" + +GLOBAL_LDFLAGS += -L $(SOURCE_ROOT)/board/xr871evb + +# Extra build image +EXTRA_TARGET_MAKEFILES += $(MAKEFILES_PATH)/aos_standard_targets.mk +EXTRA_TARGET_MAKEFILES += $(SOURCE_ROOT)/platform/mcu/$(HOST_MCU_FAMILY)/mkimage.mk diff --git a/example/alinkapp/alinkapp.mk b/example/alinkapp/alinkapp.mk index c687540fa5..0d951695fb 100644 --- a/example/alinkapp/alinkapp.mk +++ b/example/alinkapp/alinkapp.mk @@ -19,7 +19,7 @@ endif ifneq (,$(filter linux,$(HOST_MCU_FAMILY))) gateway ?= 0 else -ifneq (,$(filter stm32f4xx,$(HOST_MCU_FAMILY))) +ifneq (,$(filter stm32f4xx xr871,$(HOST_MCU_FAMILY))) gateway ?= 0 else gateway ?= 1 diff --git a/platform/arch/arm/armv7m/gcc/m4/port_s.S b/platform/arch/arm/armv7m/gcc/m4/port_s.S index d4c8c5404a..00513d99ed 100644 --- a/platform/arch/arm/armv7m/gcc/m4/port_s.S +++ b/platform/arch/arm/armv7m/gcc/m4/port_s.S @@ -123,7 +123,7 @@ PendSV_Handler: @save context #if (defined(__VFP_FP__) && !defined(__SOFTFP__)) - VSTMFD R0!, {D8 - D15} + VSTMDB R0!, {D8 - D15} #endif SUBS R0, R0, #0x24 @@ -149,7 +149,7 @@ _pendsv_handler_nosave: ADDS R0, R0, #0x24 #if (defined(__VFP_FP__) && !defined(__SOFTFP__)) - VLDMFD R0!, {D8 - D15} + VLDMIA R0!, {D8 - D15} #endif @return stack = PSP diff --git a/platform/mcu/xr871/aos/aos.c b/platform/mcu/xr871/aos/aos.c new file mode 100644 index 0000000000..b2efaa11ce --- /dev/null +++ b/platform/mcu/xr871/aos/aos.c @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2015-2017 Alibaba Group Holding Limited + */ + +#include +#include +#include +#include +#include + +#define AOS_START_STACK 2048 + +ktask_t *g_aos_init; + +extern void board_init(void); +extern void aos_cli_ext_init(void); +extern int application_start(int argc, char **argv); + +#include "net/wlan/wlan.h" + +static void wifi_join(char* ssid, char* psk) +{ + wlan_sta_set(ssid, strlen(psk), psk); + wlan_sta_enable(); +} + +static void aos_system_init(void) +{ + int i; + + soc_driver_init(); + + printf("aos_init now...\n"); + + soc_system_init(); + + hal_init_post(); + + //wifi_join("\"AW2\"", "\"1qaz@WSX\""); + //wifi_join("\"MI\"", "\"xiaomi123\""); + //wifi_join("\"mao\"", "\"11111111\""); + //wifi_join("\"mao\"", NULL); + + board_init(); + + vfs_init(); + vfs_device_init(); + +#ifdef CONFIG_AOS_FATFS_SUPPORT + /* if enable the macro, please also enable CONFIG_AOS_FATFS_SUPPORT_MMC + * and include , add modules.fs.fatfs to component dependence + */ + fatfs_register(); +#endif + + aos_cli_init(); + aos_cli_ext_init(); + aos_kv_init(); + aos_loop_init(); + + aos_framework_init(); + + application_start(0, NULL); +} + +void aos_startup(void) +{ + soc_sys_mem_init(); + + soc_systick_init(); + + krhino_init(); + + krhino_task_dyn_create(&g_aos_init, "aos-init", 0, 10, 0, AOS_START_STACK, aos_system_init, 1); + + krhino_start(); +} diff --git a/platform/mcu/xr871/aos/aos.h b/platform/mcu/xr871/aos/aos.h new file mode 100644 index 0000000000..0238422356 --- /dev/null +++ b/platform/mcu/xr871/aos/aos.h @@ -0,0 +1,14 @@ +/* + * Copyright (C) 2015-2017 Alibaba Group Holding Limited + */ + +#ifndef AOS_H +#define AOS_H + +void soc_driver_init(void); + +void soc_system_init(void); + +void aos_startup(void); + +#endif /* AOS_H */ diff --git a/platform/mcu/xr871/aos/aos.mk b/platform/mcu/xr871/aos/aos.mk new file mode 100644 index 0000000000..c3657febb2 --- /dev/null +++ b/platform/mcu/xr871/aos/aos.mk @@ -0,0 +1,23 @@ +NAME := aos + +$(NAME)_TYPE := kernel +$(NAME)_SOURCES := aos.c \ + aos_impl.c \ + soc_impl.c \ + hal/soc/adc.c \ + hal/soc/flash.c \ + hal/soc/gpio.c \ + hal/soc/i2c.c \ + hal/soc/pwm.c \ + hal/soc/rtc.c \ + hal/soc/sd.c \ + hal/soc/timer.c \ + hal/soc/uart.c \ + hal/soc/wdg.c \ + hal/fota_port.c \ + hal/wifi_port.c \ + hal/wifi.c \ + hal/hal.c \ + cli_ext/cli_ext.c \ + cli_ext/cli_mem.c \ + cli_ext/cli_upgrade.c \ No newline at end of file diff --git a/platform/mcu/xr871/aos/aos_impl.c b/platform/mcu/xr871/aos/aos_impl.c new file mode 100644 index 0000000000..8358d3beb3 --- /dev/null +++ b/platform/mcu/xr871/aos/aos_impl.c @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2015-2017 Alibaba Group Holding Limited + */ + +#include + +#if (RHINO_CONFIG_TASK_STACK_CUR_CHECK > 0) +size_t soc_get_cur_sp(void) +{ + volatile size_t dummy = (size_t)&dummy; + return dummy; +} +#endif + +#if (RHINO_CONFIG_HW_COUNT > 0) +void soc_hw_timer_init(void) +{ +} + +hr_timer_t soc_hr_hw_cnt_get(void) +{ + return 0; +} + +lr_timer_t soc_lr_hw_cnt_get(void) +{ + return 0; +} +#endif /* RHINO_CONFIG_HW_COUNT */ + +#if (RHINO_CONFIG_INTRPT_GUARD > 0) +void soc_intrpt_guard(void) +{ +} +#endif + +#if (RHINO_CONFIG_INTRPT_STACK_REMAIN_GET > 0) +size_t soc_intrpt_stack_remain_get(void) +{ + return 0; +} +#endif + +#if (RHINO_CONFIG_INTRPT_STACK_OVF_CHECK > 0) +void soc_intrpt_stack_ovf_check(void) +{ +} +#endif + +#if (RHINO_CONFIG_DYNTICKLESS > 0) +void soc_tick_interrupt_set(tick_t next_ticks,tick_t elapsed_ticks) +{ +} + +tick_t soc_elapsed_ticks_get(void) +{ + return 0; +} +#endif + +#if (RHINO_CONFIG_KOBJ_DYN_ALLOC > 0) +extern void *heap_start; +extern void *heap_len; +k_mm_region_t g_mm_region[] = {{(uint8_t *)&heap_start, (size_t)&heap_len}}; +int g_region_num = sizeof(g_mm_region) / sizeof(k_mm_region_t); + + +void soc_sys_mem_init(void) +{ + extern void *heap_start; + extern void *heap_len; + + g_mm_region[0].start = &heap_start; + g_mm_region[0].len = &heap_len; + g_region_num = 1; +} + + +#endif + +void soc_err_proc(kstat_t err) +{ +} + +krhino_err_proc_t g_err_proc = soc_err_proc; diff --git a/platform/mcu/xr871/aos/cli_ext/cli_ext.c b/platform/mcu/xr871/aos/cli_ext/cli_ext.c new file mode 100644 index 0000000000..d5c9731292 --- /dev/null +++ b/platform/mcu/xr871/aos/cli_ext/cli_ext.c @@ -0,0 +1,9 @@ + +extern void cli_cmd_add_upgrade(void); +extern void cli_cmd_add_mem(void); + +void aos_cli_ext_init(void) +{ + cli_cmd_add_upgrade(); + cli_cmd_add_mem(); +} diff --git a/platform/mcu/xr871/aos/cli_ext/cli_mem.c b/platform/mcu/xr871/aos/cli_ext/cli_mem.c new file mode 100644 index 0000000000..998e6105fd --- /dev/null +++ b/platform/mcu/xr871/aos/cli_ext/cli_mem.c @@ -0,0 +1,80 @@ + +#include +#include "driver/chip/hal_chip.h" + +#define readl(addr) (*((volatile unsigned long *)(addr))) +#define writel(v, addr) (*((volatile unsigned long *)(addr)) = (unsigned long)(v)) + +static void hexdump(char* msg, uint32_t addr, uint32_t len) +{ + int i; + printf("%s:", msg); + for (i = 0; i < len; i += 4) { + if (i % 16 == 0) { + printf("\n"); + printf("%08x: ", addr + i); + } + printf("%08x ", readl(addr + i)); + } + printf("\n"); +} + +static void handle_mem_cmd(char *pwbuf, int blen, int argc, char **argv) +{ + uint32_t addr; + uint32_t len; + uint32_t val; + uint32_t op = 0; + + if (argc < 3) { + printf("cmd_mem: Wrong Arguments\n"); + return; + } + if (strcmp(argv[1], "read") == 0) { + op = 0; + } else if (strcmp(argv[1], "write") == 0) { + op = 1; + } else if (strcmp(argv[1], "dump") == 0) { + op = 2; + } + + addr = strtoul(argv[2], NULL, 16); + /* check alignment */ + if (addr & 0x1) { + printf("** addr is not aligned !!\n"); + return; + } + + if (op == 0) { + val = readl(addr); + printf("read: [0x%x]:: 0x%08x", addr, val); + } else if (op == 1) { + if (argc < 4) { + printf("** Wrong Arguments !!\n"); + return; + } + val = strtoul(argv[3], NULL, 16); + writel(val, addr); + printf("write: [0x%x]: 0x%08x", addr, val); + } else { + if (argc < 4) { + len = 256; + } else { + len = strtoul(argv[3], NULL, 10); + } + hexdump("dump", addr, len); + } + printf("\n"); + return ; +} + +static struct cli_command ncmd = { + .name = "mem", + .help = "mem [read addr | write addr val | dump addr len]", + .function = handle_mem_cmd, +}; + +void cli_cmd_add_mem(void) +{ + aos_cli_register_command(&ncmd); +} diff --git a/platform/mcu/xr871/aos/cli_ext/cli_upgrade.c b/platform/mcu/xr871/aos/cli_ext/cli_upgrade.c new file mode 100644 index 0000000000..fa3ee73405 --- /dev/null +++ b/platform/mcu/xr871/aos/cli_ext/cli_upgrade.c @@ -0,0 +1,25 @@ + +#include +#include "driver/chip/hal_chip.h" +#include "driver/chip/system_chip.h" +#include "net/wlan/wlan.h" + +static void handle_upgrade_cmd(char *pwbuf, int blen, int argc, char **argv) +{ + net_sys_stop(); + SystemDeInit(SYSTEM_DEINIT_FLAG_RESET_CLK); + HAL_PRCM_SetCPUABootFlag(PRCM_CPUA_BOOT_FROM_SYS_UPDATE); + *((volatile unsigned long*)0xE000ED08) = 0; + *((volatile unsigned long*)0xE000ED0C) = 0x05fa0001; +} + +static struct cli_command ncmd = { + .name = "upgrade", + .help = "upgrade", + .function = handle_upgrade_cmd, +}; + +void cli_cmd_add_upgrade(void) +{ + aos_cli_register_command(&ncmd); +} diff --git a/platform/mcu/xr871/aos/hal/fota_port.c b/platform/mcu/xr871/aos/hal/fota_port.c new file mode 100644 index 0000000000..16b9a67d81 --- /dev/null +++ b/platform/mcu/xr871/aos/hal/fota_port.c @@ -0,0 +1,186 @@ +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include "sys/image.h" +#include "sys/ota.h" + +#define OTA_DEBUG_ON 0 +#define OTA_ERROR_ON 0 + +#if OTA_DEBUG_ON +#define OTA_DEBUG(...) do { \ + printf("[ota debug]: "); \ + printf(__VA_ARGS__); \ + } while (0) +#else +#define OTA_DEBUG(...) +#endif + +#if OTA_ERROR_ON +#define OTA_ERROR(...) do { \ + printf("[ota error]: "); \ + printf(__VA_ARGS__); \ + } while (0) +#else +#define OTA_ERROR(...) +#endif + +typedef struct +{ + uint32_t dst_adr; + uint32_t src_adr; + uint32_t siz; + uint16_t crc; +} __attribute__((packed)) ota_hdl_t; + +typedef struct +{ + uint32_t ota_len; + uint32_t ota_crc; +} ota_reboot_info_t; + +static ota_reboot_info_t ota_info; + +static CRC16_Context contex; +hal_partition_t ota_partition = HAL_PARTITION_OTA_TEMP; + +static int xr871_ota_init(hal_ota_module_t *m, void *something) +{ + hal_logic_partition_t *partition_info; + + image_seq_t seq; + ota_cfg_t cfg; + + OTA_DEBUG("xr871_ota_init enter\n"); + + if (ota_read_cfg(&cfg) != OTA_STATUS_OK) + OTA_ERROR("ota read cfg failed\n"); + + if (((cfg.image == OTA_IMAGE_1ST) && (cfg.state == OTA_STATE_VERIFIED)) + || ((cfg.image == OTA_IMAGE_2ND) && (cfg.state == OTA_STATE_UNVERIFIED))) { + seq = IMAGE_SEQ_1ST; + } else if (((cfg.image == OTA_IMAGE_2ND) && (cfg.state == OTA_STATE_VERIFIED)) + || ((cfg.image == OTA_IMAGE_1ST) && (cfg.state == OTA_STATE_UNVERIFIED))) { + seq = IMAGE_SEQ_2ND; + } else { + OTA_ERROR("invalid image %d, state %d\n", cfg.image, cfg.state); + seq = IMAGE_SEQ_1ST; + } + + OTA_DEBUG("image seq %d\n", seq); + + if (seq == IMAGE_SEQ_1ST) { + ota_partition = HAL_PARTITION_OTA_TEMP; + partition_info = hal_flash_get_info( HAL_PARTITION_OTA_TEMP ); + hal_flash_erase(HAL_PARTITION_OTA_TEMP, 0 ,partition_info->partition_length); + } + else if (seq == OTA_IMAGE_2ND) { + ota_partition = HAL_PARTITION_APPLICATION; + partition_info = hal_flash_get_info( HAL_PARTITION_APPLICATION); + hal_flash_erase(HAL_PARTITION_APPLICATION, 0 ,partition_info->partition_length); + } + ota_info.ota_len = 0; + + //CRC16_Init( &contex ); + memset(&ota_info, 0 , sizeof ota_info); + + OTA_DEBUG("xr871_ota_init exit\n"); + return 0; +} + + +static int xr871_ota_write(hal_ota_module_t *m, volatile uint32_t* off_set, uint8_t* in_buf ,uint32_t in_buf_len) +{ + int ret = 0; + unsigned int _off_set = 0; + + OTA_DEBUG("xr871_ota_write enter\n"); + + if (ota_info.ota_len + in_buf_len < 0x8000) { + //do nothing + } + else if(ota_info.ota_len >= 0x8000) + { + _off_set = ota_info.ota_len -0x8000; + ret = hal_flash_write(ota_partition, &_off_set, in_buf, in_buf_len); + } + else { + int rem; + + rem = ota_info.ota_len + in_buf_len - 0x8000; + _off_set = 0; + + ret = hal_flash_write(ota_partition, &_off_set, &in_buf[in_buf_len-rem], rem); + } + + ota_info.ota_len += in_buf_len; + + //CRC16_Update( &contex, in_buf, in_buf_len); + + OTA_DEBUG(" &_off_set 0x %08x, %d\n", _off_set ,ota_info.ota_len); + + OTA_DEBUG("xr871_ota_write exit\n"); + + return ret; +} + +static int xr871_ota_read(hal_ota_module_t *m, volatile uint32_t* off_set, uint8_t* out_buf, uint32_t out_buf_len) +{ + uint32_t temp; + + OTA_DEBUG("xr871_ota_read enter\n"); + + temp = *off_set; + + if (ota_partition == HAL_PARTITION_OTA_TEMP) { + temp += 0x8000; + } + + hal_flash_read(ota_partition, &temp, out_buf, out_buf_len); + + *off_set += out_buf_len; + OTA_DEBUG("xr871_ota_read exit\n"); + + return 0; +} + +static int xr871_ota_set_boot(hal_ota_module_t *m, void *something) +{ + ota_cfg_t ota_cfg; + uint8_t parti = *(uint8_t *)something; + //CRC16_Final( &contex, &ota_info.ota_crc ); + OTA_DEBUG("xr871 set boot\n"); + + if (ota_partition == HAL_PARTITION_OTA_TEMP) { + ota_cfg.image = 2; + } + else { + ota_cfg.image = 1; + } + + ota_cfg.state = OTA_STATE_UNVERIFIED; + ota_write_cfg(&ota_cfg); + ota_set_size(ota_info.ota_len); + if (ota_verify_image(OTA_VERIFY_NONE, NULL) != OTA_STATUS_OK) { + OTA_DEBUG("xr871 set boot fail\n"); + } + + /* reboot */ + hal_reboot(); + + return 0; +} + +struct hal_ota_module_s xr871_ota_module = { + .init = xr871_ota_init, + .ota_write = xr871_ota_write, + .ota_read = xr871_ota_read, + .ota_set_boot = xr871_ota_set_boot, +}; diff --git a/platform/mcu/xr871/aos/hal/hal.c b/platform/mcu/xr871/aos/hal/hal.c new file mode 100644 index 0000000000..e90730bacc --- /dev/null +++ b/platform/mcu/xr871/aos/hal/hal.c @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include "hal/soc/soc.h" +#include "hal/wifi.h" +#include "hal/ota.h" + +#include "sys/interrupt.h" + +#include "board.h" + +extern int errno; +wdg_dev_t wdg; +uart_dev_t uart_0; +sd_dev_t sd_dev; + +extern hal_wifi_module_t sim_aos_wifi_xr871; +extern hal_ota_module_t xr871_ota_module; + +void hal_init(void) +{ + // init uart + uart_0.port = STDIO_UART; + uart_0.config.baud_rate = STDIO_UART_BUADRATE; + uart_0.config.data_width = DATA_WIDTH_8BIT; + uart_0.config.parity = NO_PARITY; + uart_0.config.stop_bits = STOP_BITS_1; + uart_0.config.flow_control = FLOW_CONTROL_DISABLED; + printf("uart_0.port:%d\n",uart_0.port); + hal_uart_init(&uart_0); +} + +void hal_init_post(void) +{ + // init wifi + hal_wifi_register_module(&sim_aos_wifi_xr871); + hal_wifi_init(); + + hal_ota_register_module(&xr871_ota_module); +} + +void hal_boot(hal_partition_t partition) +{ + uint32_t addr; + + intc_deinit(); + + addr = hal_flash_get_info(partition)->partition_start_addr; + __asm volatile ("BX %0" : : "r" (addr) ); +} + +void hal_reboot(void) +{ + HAL_WDG_Reboot(); +} + diff --git a/platform/mcu/xr871/aos/hal/soc/adc.c b/platform/mcu/xr871/aos/hal/soc/adc.c new file mode 100644 index 0000000000..b02156e99a --- /dev/null +++ b/platform/mcu/xr871/aos/hal/soc/adc.c @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file hal/soc/adc.h + * @brief adc HAL + * @version since 5.5.0 + */ + +#include "hal/soc/soc.h" +#include "driver/chip/hal_adc.h" +#include "aos/hal/adc_priv.h" + +/**@biref Initialises an ADC interface + * + * Prepares an ADC hardware interface for sampling + * + * @param adc : the interface which should be initialised + * + * @return 0 : on success. + * @return EIO : if an error occurred with any step + */ +int32_t hal_adc_init(adc_dev_t *adc) +{ + HAL_Status status = HAL_ERROR; + adc_priv_config_t *adc_priv_cfg = (adc_priv_config_t*)adc->priv; + + status = HAL_ADC_ConfigChannel(adc->port, ADC_SELECT_ENABLE, + adc_priv_cfg->irqmode, adc_priv_cfg->irqlow, adc_priv_cfg->irqhigh); + if (status != HAL_OK) { + printf("[hal_adc]: ADC ch %d config error %d\n", adc->port, status); + return -1; + } + + status = HAL_ADC_EnableIRQCallback(adc->port, adc_priv_cfg->irq_callback, adc_priv_cfg->arg); + if (status != HAL_OK) { + printf("[hal_adc]: ADC ch %d IRQ Enable error %d\n", adc->port, status); + return -1; + } + + return 0; +} + + +/**@biref Takes a single sample from an ADC interface + * + * Takes a single sample from an ADC interface + * + * @param adc : the interface which should be sampled + * @param output : pointer to a variable which will receive the sample + * @param timeout : ms timeout + * @return 0 : on success. + * @return EIO : if an error occurred with any step + */ +int32_t hal_adc_value_get(adc_dev_t *adc, void *output, uint32_t timeout) +{ + *(uint32_t*)output = HAL_ADC_GetValue(adc->port); + return 0; +} + + +/**@biref De-initialises an ADC interface + * + * @abstract Turns off an ADC hardware interface + * + * @param adc : the interface which should be de-initialised + * + * @return 0 : on success. + * @return EIO : if an error occurred with any step + */ +int32_t hal_adc_finalize(adc_dev_t *adc) +{ + HAL_Status status = HAL_ERROR; + + status = HAL_ADC_ConfigChannel(adc->port, ADC_SELECT_DISABLE, 0, 0, 0); + if (status != HAL_OK) { + printf("[hal_adc]: ADC ch config error %d\n", status); + } + + status = HAL_ADC_DisableIRQCallback(adc->port); + if (status != HAL_OK) { + printf("[hal_adc]: ADC ch cb disable error %d\n", status); + } + return 0; +} + diff --git a/platform/mcu/xr871/aos/hal/soc/flash.c b/platform/mcu/xr871/aos/hal/soc/flash.c new file mode 100644 index 0000000000..69763d7f7b --- /dev/null +++ b/platform/mcu/xr871/aos/hal/soc/flash.c @@ -0,0 +1,221 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#include "hal/soc/soc.h" +#include "mico_rtos.h" + +#include "driver/chip/hal_chip.h" + +#define XR871_FLASH PRJCONF_IMG_FLASH + +extern wdg_dev_t wdg; + +#if 0 + +#define FIQMASK_REG_NAME_R "faultmask" +#define FIQMASK_REG_NAME_W "faultmask" + +/* + * Save the current interrupt enable state & disable FIQs + */ +static __always_inline unsigned long xr_fiq_save(void) +{ + unsigned long flags; + + __asm volatile( + "mrs %0, " FIQMASK_REG_NAME_R "\n" + "cpsid f" + : "=r" (flags) : : "memory", "cc"); + return flags; +} + +/* + * restore saved FIQ state + */ +static __always_inline void xr_fiq_restore(unsigned long flags) +{ + __asm volatile( + "msr " FIQMASK_REG_NAME_W ", %0" + : + : "r" (flags) + : "memory", "cc"); +} + +#define GLOBAL_INT_DECLARATION() uint32_t fiq_tmp, irq_tmp +#define GLOBAL_INT_DISABLE() do{\ + fiq_tmp = xr_fiq_save();\ + irq_tmp = xr_irq_save();\ + }while(0) + + +#define GLOBAL_INT_RESTORE() do{ \ + xr_fiq_restore(fiq_tmp);\ + xr_irq_restore(irq_tmp);\ + }while(0) +#else +#define GLOBAL_INT_DECLARATION() +#define GLOBAL_INT_DISABLE() +#define GLOBAL_INT_RESTORE() +#endif + +#define SECTOR_SIZE 0x1000 /* 4 K/sector */ + +extern const hal_logic_partition_t hal_partitions[]; + +hal_logic_partition_t *hal_flash_get_info(hal_partition_t in_partition) +{ + hal_logic_partition_t *logic_partition; + + logic_partition = (hal_logic_partition_t *)&hal_partitions[ in_partition ]; + + return logic_partition; +} + +int32_t hal_flash_erase(hal_partition_t in_partition, uint32_t off_set, uint32_t size) +{ + uint32_t addr; + uint32_t start_addr, end_addr; + hal_logic_partition_t *partition_info; + +#ifdef CONFIG_AOS_KV_MULTIPTN_MODE + if (in_partition == CONFIG_AOS_KV_PTN) { + if (off_set >= CONFIG_AOS_KV_PTN_SIZE) { + in_partition = CONFIG_AOS_KV_SECOND_PTN; + off_set -= CONFIG_AOS_KV_PTN_SIZE; + } + } +#endif + + + GLOBAL_INT_DECLARATION(); + + partition_info = hal_flash_get_info( in_partition ); + + if(size + off_set > partition_info->partition_length) + return -1; + + start_addr = (partition_info->partition_start_addr + off_set) & (~0xFFF); + end_addr = (partition_info->partition_start_addr + off_set + size - 1) & (~0xFFF); + + for(addr = start_addr; addr <= end_addr; addr += SECTOR_SIZE) + { + hal_wdg_reload(&wdg); + GLOBAL_INT_DISABLE(); + //flash_ctrl(CMD_FLASH_ERASE_SECTOR, &addr); + //printf("flash_ctrl: addr=0x%08x\n", addr); + flash_erase(XR871_FLASH, addr, SECTOR_SIZE); + GLOBAL_INT_RESTORE(); + } + hal_wdg_reload(&wdg); + + return 0; +} + +int32_t hal_flash_write(hal_partition_t in_partition, uint32_t *off_set, const void *in_buf , uint32_t in_buf_len) +{ + uint32_t start_addr; + hal_logic_partition_t *partition_info; + +#ifdef CONFIG_AOS_KV_MULTIPTN_MODE + if (in_partition == CONFIG_AOS_KV_PTN) { + if ((*off_set) >= CONFIG_AOS_KV_PTN_SIZE) { + in_partition = CONFIG_AOS_KV_SECOND_PTN; + *off_set = (*off_set) - CONFIG_AOS_KV_PTN_SIZE; + } + } +#endif + + GLOBAL_INT_DECLARATION(); + + partition_info = hal_flash_get_info( in_partition ); + + if(off_set == NULL || in_buf == NULL || *off_set + in_buf_len > partition_info->partition_length) + return -1; + + start_addr = partition_info->partition_start_addr + *off_set; + + hal_wdg_reload(&wdg); + GLOBAL_INT_DISABLE(); + //flash_write(in_buf, in_buf_len, start_addr); + //printf("flash_write: addr=0x%08x,len=%d\n", start_addr, in_buf_len); + flash_write(XR871_FLASH, start_addr, in_buf, in_buf_len); + GLOBAL_INT_RESTORE(); + hal_wdg_reload(&wdg); + + *off_set += in_buf_len; + + return 0; +} + +int32_t hal_flash_read(hal_partition_t in_partition, uint32_t *off_set, void *out_buf, uint32_t out_buf_len) +{ + uint32_t start_addr; + hal_logic_partition_t *partition_info; + +#ifdef CONFIG_AOS_KV_MULTIPTN_MODE + if (in_partition == CONFIG_AOS_KV_PTN) { + if ((*off_set) >= CONFIG_AOS_KV_PTN_SIZE) { + in_partition = CONFIG_AOS_KV_SECOND_PTN; + *off_set = (*off_set) - CONFIG_AOS_KV_PTN_SIZE; + } + } +#endif + + GLOBAL_INT_DECLARATION(); + + partition_info = hal_flash_get_info( in_partition ); + + if(off_set == NULL || out_buf == NULL || *off_set + out_buf_len > partition_info->partition_length) + return -1; + + start_addr = partition_info->partition_start_addr + *off_set; + + hal_wdg_reload(&wdg); + GLOBAL_INT_DISABLE(); + //flash_read(out_buf, out_buf_len, start_addr); + //printf("flash_read: addr=0x%08x,len=%d\n", start_addr, out_buf_len); + flash_read(XR871_FLASH, start_addr, out_buf, out_buf_len); + GLOBAL_INT_RESTORE(); + hal_wdg_reload(&wdg); + + *off_set += out_buf_len; + + return 0; +} + +int32_t hal_flash_enable_secure(hal_partition_t partition, uint32_t off_set, uint32_t size) +{ + return 0; +} + +int32_t hal_flash_dis_secure(hal_partition_t partition, uint32_t off_set, uint32_t size) +{ + return 0; +} diff --git a/platform/mcu/xr871/aos/hal/soc/gpio.c b/platform/mcu/xr871/aos/hal/soc/gpio.c new file mode 100644 index 0000000000..e27718c355 --- /dev/null +++ b/platform/mcu/xr871/aos/hal/soc/gpio.c @@ -0,0 +1,263 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include "hal/soc/soc.h" +#include "driver/chip/hal_gpio.h" + +static GPIO_InitParam gpios[GPIOA_PIN_NUM]; +static GPIO_IrqParam eints[GPIOA_PIN_NUM]; +/**@brief Initialises a GPIO pin + * + * @note Prepares a GPIO pin for use. + * + * @param gpio : the gpio pin which should be initialised + * @param configuration : A structure containing the required + * gpio configuration + * + * @return 0 : on success. + * @return EIO : if an error occurred with any step + */ +int32_t hal_gpio_init(gpio_dev_t *gpio) +{ + if (gpio && gpio->port < GPIOA_PIN_NUM) { + memset(&gpios[gpio->port], 0, sizeof(GPIO_InitParam)); + gpios[gpio->port].driving = GPIO_DRIVING_LEVEL_2; + switch (gpio->config) { + case INPUT_HIGH_IMPEDANCE: + gpios[gpio->port].mode = GPIOx_Pn_F7_DISABLE; + case INPUT_PULL_UP: + gpios[gpio->port].mode = GPIOx_Pn_F0_INPUT; + gpios[gpio->port].pull = GPIO_PULL_UP; + case INPUT_PULL_DOWN: + gpios[gpio->port].mode = GPIOx_Pn_F0_INPUT; + gpios[gpio->port].pull = GPIO_PULL_DOWN; + break; + case OUTPUT_OPEN_DRAIN_PULL_UP: + gpios[gpio->port].mode = GPIOx_Pn_F1_OUTPUT; + gpios[gpio->port].pull = GPIO_PULL_UP; + break; + case OUTPUT_PUSH_PULL: + gpios[gpio->port].mode = GPIOx_Pn_F1_OUTPUT;; + gpios[gpio->port].pull = GPIO_PULL_NONE; + break; + case OUTPUT_OPEN_DRAIN_NO_PULL: + gpios[gpio->port].pull = GPIO_PULL_NONE; + gpios[gpio->port].mode = GPIOx_Pn_F1_OUTPUT;; + break; + default: + break; + } + HAL_GPIO_Init(GPIO_PORT_A, gpio->port, &gpios[gpio->port]); + return 0; + } + printf("[hal_gpio]: gpio[%d] init failed\n", gpio ? gpio->port : -1); + return -1; +} + +/**@brief Sets an output GPIO pin high + * + * @note Using this function on a gpio pin which is set to input mode is undefined. + * + * @param gpio : the gpio pin which should be set high + * + * @return 0 : on success. + * @return EIO : if an error occurred with any step + */ +int32_t hal_gpio_output_high(gpio_dev_t *gpio) +{ + if (gpio && gpio->port < GPIOA_PIN_NUM) { + HAL_GPIO_WritePin(GPIO_PORT_A, gpio->port, GPIO_PIN_HIGH); + return 0; + } + printf("[hal_gpio]: gpio[%d] output highlevel failed\n", gpio ? gpio->port : -1); + return -1; +} + + +/**@brief Sets an output GPIO pin low + * + * @note Using this function on a gpio pin which is set to input mode is undefined. + * + * @param gpio : the gpio pin which should be set low + * + * @return 0 : on success. + * @return EIO : if an error occurred with any step + */ +int32_t hal_gpio_output_low(gpio_dev_t *gpio) +{ + if (gpio && gpio->port < GPIOA_PIN_NUM) { + HAL_GPIO_WritePin(GPIO_PORT_A, gpio->port, GPIO_PIN_LOW); + return 0; + } + printf("[hal_gpio]: gpio[%d] output lowlevel failed\n", gpio ? gpio->port : -1); + return -1; +} + +/** Trigger an output GPIO pin + * + * Trigger an output GPIO pin's output. Using this function on a + * gpio pin which is set to input mode is undefined. + * + * @param gpio : the gpio pin which should be set low + * + * @return 0 : on success. + * @return EIO : if an error occurred with any step + */ +int32_t hal_gpio_output_toggle(gpio_dev_t *gpio) +{ + // TODO: + return -1; +} + +/**@brief Get the state of an input GPIO pin + * + * @note Get the state of an input GPIO pin. Using this function on a + * gpio pin which is set to output mode will return an undefined value. + * + * @param gpio : the gpio pin which should be read + * @param value : gpio value + * @return 0 : on success + * @return EIO : if an error occurred with any step + */ +int32_t hal_gpio_input_get(gpio_dev_t *gpio, uint32_t *value) +{ + if (gpio && gpio->port < GPIOA_PIN_NUM) { + *value = HAL_GPIO_ReadPin(GPIO_PORT_A, gpio->port); + return 0; + } + printf("[hal_gpio]: gpio[%d] get input value failed\n", gpio ? gpio->port : -1); + return -1; +} + + +/**@brief Enables an interrupt trigger for an input GPIO pin + * + * @note Enables an interrupt trigger for an input GPIO pin. + * Using this function on a gpio pin which is set to + * output mode is undefined. + * + * @param gpio : the gpio pin which will provide the interrupt trigger + * @param trigger : the type of trigger (rising/falling edge) + * @param handler : a function pointer to the interrupt handler + * @param arg : an argument that will be passed to the + * interrupt handler + * + * @return 0 : on success. + * @return EIO : if an error occurred with any step + */ +int32_t hal_gpio_enable_irq(gpio_dev_t *gpio, gpio_irq_trigger_t trigger, + gpio_irq_handler_t handler, void *arg) +{ + if (gpio && gpio->port < GPIOA_PIN_NUM) { + GPIO_InitParam param = {0}; + + memset(&eints[gpio->port], 0, sizeof(GPIO_IrqParam)); + + param.driving = GPIO_DRIVING_LEVEL_1; + param.mode = GPIOx_Pn_F6_EINT; + param.pull = GPIO_PULL_UP; + HAL_GPIO_Init(GPIO_PORT_A, gpio->port, ¶m); + + if (trigger == IRQ_TRIGGER_RISING_EDGE) { + eints[gpio->port].event = GPIO_IRQ_EVT_RISING_EDGE; + } else if (trigger == IRQ_TRIGGER_FALLING_EDGE) { + eints[gpio->port].event = GPIO_IRQ_EVT_FALLING_EDGE; + } else if (trigger == IRQ_TRIGGER_BOTH_EDGES) { + eints[gpio->port].event = GPIO_IRQ_EVT_BOTH_EDGE; + } + eints[gpio->port].callback = handler; + eints[gpio->port].arg = arg; + HAL_GPIO_EnableIRQ(GPIO_PORT_A, gpio->port, &eints[gpio->port]); + return 0; + } + printf("[hal_gpio]: gpio[%d] enable irq failed\n", gpio ? gpio->port : -1); + return -1; +} + + +/**@brief Disables an interrupt trigger for an input GPIO pin + * + * @note Disables an interrupt trigger for an input GPIO pin. + * Using this function on a gpio pin which has not been set up + * using @ref hal_gpio_input_irq_enable is undefined. + * + * @param gpio : the gpio pin which provided the interrupt trigger + * + * @return 0 : on success. + * @return EIO : if an error occurred with any step + */ +int32_t hal_gpio_disable_irq(gpio_dev_t *gpio) +{ + if (gpio && gpio->port < GPIOA_PIN_NUM) { + HAL_GPIO_DisableIRQ(GPIO_PORT_A, gpio->port); + memset(&eints[gpio->port], 0, sizeof(GPIO_IrqParam)); + return 0; + } + + printf("[hal_gpio]: gpio[%d] disable irq failed\n", gpio ? gpio->port : -1); + return -1; +} + + +/**@brief Disables an interrupt trigger for an input GPIO pin + * + * @note Disables an interrupt trigger for an input GPIO pin. + * Using this function on a gpio pin which has not been set up + * using @ref hal_gpio_input_irq_enable is undefined. + * + * @param gpio : the gpio pin which provided the interrupt trigger + * + * @return 0 : on success. + * @return EIO : if an error occurred with any step + */ +int32_t hal_gpio_clear_irq(gpio_dev_t *gpio) +{ + return 0; +} + + +/**@brief DeInitialises a GPIO pin + * + * @note Set a GPIO pin in default state. + * + * @param gpio : the gpio pin which should be deinitialised + * + * @return 0 : on success. + * @return EIO : if an error occurred with any step + */ +int32_t hal_gpio_finalize(gpio_dev_t *gpio) +{ + if (gpio && gpio->port < GPIOA_PIN_NUM) { + HAL_GPIO_DeInit(GPIO_PORT_A, gpio->port); + memset(&gpios[gpio->port], 0, sizeof(GPIO_InitParam)); + return 0; + } + return -1; +} diff --git a/platform/mcu/xr871/aos/hal/soc/i2c.c b/platform/mcu/xr871/aos/hal/soc/i2c.c new file mode 100644 index 0000000000..5f05660872 --- /dev/null +++ b/platform/mcu/xr871/aos/hal/soc/i2c.c @@ -0,0 +1,249 @@ +/* + * Copyright (C) 2015-2017 Alibaba Group Holding Limited + */ + +#include +#include "hal/soc/soc.h" +#include "driver/chip/hal_i2c.h" + +/**@brief Initialises an I2C interface + * @note Prepares an I2C hardware interface for communication as a master or slave + * + * @param device : the device for which the i2c port should be initialised + * @return 0 : on success. + * @return EIO : if an error occurred during initialisation + */ +int32_t hal_i2c_init(i2c_dev_t *i2c) +{ + I2C_InitParam initParam = {0}; + HAL_Status status = HAL_ERROR; + I2C_ID id; + + if (i2c == NULL) { + return -1; + } + if (i2c->config.address_width == 7) { + initParam.addrMode = I2C_ADDR_MODE_7BIT; + } else if (i2c->config.address_width == 10) { + initParam.addrMode = I2C_ADDR_MODE_10BIT; + } else { + return -1; + } + + initParam.clockFreq = i2c->config.freq; + if (i2c->port == 0) { + id = I2C0_ID; + } else if (i2c->port == 1) { + id = I2C1_ID; + } else { + return -1; + } + + status = HAL_I2C_Init(id, &initParam); + if (status != HAL_OK) { + printf("IIC init error %d\n", status); + return -1; + } + return 0; +} + + +/**@brief i2c master send + * @param i2c : the i2c device + * @param dev_addr : device address + * @param data : i2c send data + * @param size : i2c send data size + * @param timeout : timeout in ms + * @return 0 : on success. + * @return EIO : if an error occurred during initialisation + */ +int32_t hal_i2c_master_send(i2c_dev_t *i2c, uint16_t dev_addr, const uint8_t *data, uint16_t size, uint32_t timeout) +{ + HAL_Status status = HAL_ERROR; + I2C_ID id; + + if (i2c == NULL) { + return -1; + } + if (i2c->port == 0) { + id = I2C0_ID; + } else if (i2c->port == 1) { + id = I2C1_ID; + } else { + return -1; + } + status = HAL_I2C_Master_Transmit_IT(id, (uint16_t)dev_addr, data, (uint32_t)size); + if (status != HAL_OK) { + printf("hal_i2c_master_send error %d\n", status); + return -1; + } + return 0; +} + + +/**@brief i2c master recv + * + * @param i2c : the i2c device + * @param dev_addr : device address + * @param data : i2c receive data + * @param size : i2c receive data size + * @param timeout : timeout in ms + * @return 0 : on success. + * @return EIO : if an error occurred during initialisation + */ +int32_t hal_i2c_master_recv(i2c_dev_t *i2c, uint16_t dev_addr, uint8_t *data, uint16_t size, uint32_t timeout) +{ + HAL_Status status = HAL_ERROR; + I2C_ID id; + + if (i2c == NULL) { + return -1; + } + if (i2c->port == 0) { + id = I2C0_ID; + } else if (i2c->port == 1) { + id = I2C1_ID; + } else { + return -1; + } + status = HAL_I2C_Master_Receive_IT(id, (uint16_t)dev_addr, data, (uint32_t)size); + if (status != HAL_OK) { + printf("hal_i2c_master_recv error %d\n", status); + return -1; + } + return 0; +} + +/**@brief hal_i2C_slave_send + * + * @param i2c : the i2c device + * @param data : i2c slave send data + * @param size : i2c slave send data size + * @param timeout : timeout in ms + * @return 0 : on success. + * @return EIO : if an error occurred during initialisation + */ +int32_t hal_i2C_slave_send(i2c_dev_t *i2c, uint8_t *data, uint16_t size, uint32_t timeout) +{ + //TODO: + return -1; +} + + +/**@brief Initialises an I2C interface + * + * @param i2c : tthe i2c device + * @param data : i2c slave receive data + * @param size : i2c slave receive data size + * @param timeout : timeout in ms + * @return 0 : on success. + * @return EIO : if an error occurred during initialisation + */ +int32_t hal_i2c_slave_recv(i2c_dev_t *i2c, uint8_t *data, uint16_t size, uint32_t timeout) +{ + //TODO: + return -1; +} + + +/**@brief i2c mem write + * + * @param i2c : the i2c device + * @param dev_addr : device address + * @param mem_addr : mem address + * @param mem_addr_size : mem address + * @param data : i2c master send data + * @param size : i2c master send data size + * @param timeout : timeout in ms + * @return 0 : on success. + * @return EIO : if an error occurred during initialisation + */ +int32_t hal_i2c_mem_write(i2c_dev_t *i2c, uint16_t dev_addr, uint16_t mem_addr, uint16_t mem_addr_size, const uint8_t *data, + uint16_t size, uint32_t timeout) +{ + HAL_Status status = HAL_ERROR; + I2C_ID id; + + if (i2c == NULL) { + return -1; + } + if (i2c->port == 0) { + id = I2C0_ID; + } else if (i2c->port == 1) { + id = I2C1_ID; + } else { + return -1; + } + //FIXUP: mem addr from uint16_t to uint8_t + status = HAL_I2C_Master_Transmit_Mem_IT(id, dev_addr, (uint8_t)mem_addr & 0xff, data, (uint32_t)size); + if (status != HAL_OK) { + printf("hal_i2c_mem_write error %d\n", status); + return -1; + } + return 0; +} + +/**@brief i2c master mem read + * @param i2c : the i2c device + * @param dev_addr : device address + * @param mem_addr : mem address + * @param mem_addr_size : mem address + * @param data : i2c master send data + * @param size : i2c master send data size + * @param timeout : timeout in ms + * @return 0 : on success. + * @return EIO : if an error occurred during initialisation + */ +int32_t hal_i2c_mem_read(i2c_dev_t *i2c, uint16_t dev_addr, uint16_t mem_addr, uint16_t mem_addr_size, uint8_t *data, + uint16_t size, uint32_t timeout) +{ + HAL_Status status = HAL_ERROR; + I2C_ID id; + + if (i2c == NULL) { + return -1; + } + if (i2c->port == 0) { + id = I2C0_ID; + } else if (i2c->port == 1) { + id = I2C1_ID; + } else { + return -1; + } + //FIXUP: mem addr from uint16_t to uint8_t + status = HAL_I2C_Master_Receive_Mem_IT(id, dev_addr, (uint8_t)mem_addr & 0xff, data, (uint32_t)size); + if (status != HAL_OK) { + printf("hal_i2c_mem_read error %d\n", status); + return -1; + } + return 0; +} + +/**@brief Deinitialises an I2C device + * + * @param device : the i2c device + * @return 0 : on success. + * @return EIO : if an error occurred during deinitialisation + */ +int32_t hal_i2c_finalize(i2c_dev_t *i2c) +{ + HAL_Status status = HAL_ERROR; + I2C_ID id; + + if (i2c == NULL) { + return -1; + } + if (i2c->port == 0) { + id = I2C0_ID; + } else if (i2c->port == 1) { + id = I2C1_ID; + } else { + return -1; + } + status = HAL_I2C_DeInit(id); + if (status != HAL_OK) { + printf("IIC deinit error %d\n", status); + return -1; + } + return 0; +} diff --git a/platform/mcu/xr871/aos/hal/soc/pwm.c b/platform/mcu/xr871/aos/hal/soc/pwm.c new file mode 100644 index 0000000000..4214a5d84d --- /dev/null +++ b/platform/mcu/xr871/aos/hal/soc/pwm.c @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2015-2017 Alibaba Group Holding Limited + */ + +/** + * @file hal/soc/pwm.c + * @brief PWM HAL + * @version since 5.5.0 + */ + +#include "hal/soc/soc.h" +#include "driver/chip/hal_pwm.h" + +/**@brief Initialises a PWM pin + * + * @note Prepares a Pulse-Width Modulation pin for use. + * Does not start the PWM output (use @ref MicoPwmStart). + * + * @param pwm : the PWM device + * @return 0 : on success. + * @return EIO : if an error occurred with any step + */ +int32_t hal_pwm_init(pwm_dev_t *pwm) +{ + HAL_Status status = HAL_ERROR; + PWM_ClkParam clk_param = {0}; + PWM_ChInitParam ch_param = {0}; + int max_duty_ratio = 0; + + clk_param.clk = PWM_CLK_HOSC; + clk_param.div = PWM_SRC_CLK_DIV_1; + status = HAL_PWM_GroupClkCfg(pwm->port/2, &clk_param); + if (status != HAL_OK) + printf("[hal_pwm]: PWM ch %d clk config error\n", pwm->port); + + ch_param.hz = pwm->config.freq; + ch_param.mode = PWM_CYCLE_MODE; + ch_param.polarity = PWM_HIGHLEVE; + max_duty_ratio = HAL_PWM_ChInit(pwm->port, &ch_param); + if (max_duty_ratio == -1) + printf("[hal_pwm]: PWM ch %d init error\n", pwm->port); + + status = HAL_PWM_ChSetDutyRatio(pwm->port, pwm->config.duty_cycle); + if (status != HAL_OK) + printf("[hal_pwm]: PWM ch %d set duty ratio error\n", pwm->port); + + return 0; +} + + +/**@brief Starts PWM output on a PWM interface + * + * @note Starts Pulse-Width Modulation signal output on a PWM pin + * + * @param pwm : the PWM device + * @return 0 : on success. + * @return EIO : if an error occurred with any step + */ +int32_t hal_pwm_start(pwm_dev_t *pwm) +{ + HAL_Status status = HAL_ERROR; + status = HAL_PWM_EnableCh(pwm->port, PWM_CYCLE_MODE, 1); + if (status != HAL_OK) { + printf("[hal_pwm]: PWM ch %d start error\n", pwm->port); + return -1; + } + return 0; +} + + +/**@brief Stops output on a PWM pin + * + * @note Stops Pulse-Width Modulation signal output on a PWM pin + * + * @param pwm : the PWM device + * @return 0 : on success. + * @return EIO : if an error occurred with any step + */ +int32_t hal_pwm_stop(pwm_dev_t *pwm) +{ + HAL_PWM_ChDeinit(pwm->port); + return -1; +} + diff --git a/platform/mcu/xr871/aos/hal/soc/rtc.c b/platform/mcu/xr871/aos/hal/soc/rtc.c new file mode 100644 index 0000000000..76c9c6b391 --- /dev/null +++ b/platform/mcu/xr871/aos/hal/soc/rtc.c @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "hal/soc/soc.h" +#include "mico_rtos.h" +#include "driver/chip/hal_rtc.h" + +#define RTC_START_YEAR (1970) +/**@brief This function will initialize the on board CPU real time clock + * + * @note This function should be called by MICO system when initializing clocks, so + * It is not needed to be called by application + * @param rtc : rtc device + * @return 0 : on success. + * @return EIO : if an error occurred with any step + */ +int32_t hal_rtc_init(rtc_dev_t *rtc) +{ + +} + +/**@brief This function will return the value of time read from the on board CPU real time clock. Time value must be given in the format of + * the structure hal_rtc_time_t + * @param rtc : rtc device + * @param time : pointer to a time structure + * + * @return 0 : on success. + * @return EIO : if an error occurred with any step + */ +int32_t hal_rtc_get_time(rtc_dev_t *rtc, rtc_time_t *time) +{ + if (time != NULL) { + uint8_t isLeapYear, year, month, mday; + RTC_WeekDay wday; + uint8_t hour, minute, second; + + HAL_RTC_GetYYMMDD(&isLeapYear, &year, &month, &mday); + HAL_RTC_GetDDHHMMSS(&wday, &hour, &minute, &second); + + time->year = year; + time->month = month; + time->date = mday; + time->hr = hour; + time->min = minute; + time->sec = second; + printf("[hal_rtc]: get time %d-%d-%d, %d:%d:%d\n", + year, month, mday, hour, minute, second); + return 0; + } else + return -1; +} + +/**@brief This function will set MCU RTC time to a new value. Time value must be given in the format of + * the structure hal_rtc_time_t + * @param rtc : rtc device + * @param time : pointer to a time structure + * + * @return 0 : on success. + * @return EIO : if an error occurred with any step + */ +int32_t hal_rtc_set_time(rtc_dev_t *rtc, const rtc_time_t *time) +{ + if (time != NULL) { + uint8_t isLeapYear = !(time->year % 400) || (!(time->year % 4) && (time->year % 100)); + HAL_RTC_SetYYMMDD(isLeapYear, time->year, time->month, time->date); + HAL_RTC_SetDDHHMMSS(RTC_WDAY_TUESDAY, time->hr, time->min, time->sec); // Fixup: weakday + printf("[hal_rtc]: set time %d-%d-%d, %d:%d:%d\n", + time->year + RTC_START_YEAR, time->month, time->date, + time->hr, time->min, time->sec); + } else + return -1; +} + diff --git a/platform/mcu/xr871/aos/hal/soc/sd.c b/platform/mcu/xr871/aos/hal/soc/sd.c new file mode 100644 index 0000000000..23445757e3 --- /dev/null +++ b/platform/mcu/xr871/aos/hal/soc/sd.c @@ -0,0 +1,174 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include "hal/soc/soc.h" +#include "hal/soc/sd.h" +#include "driver/chip/hal_def.h" +#include "driver/chip/sdmmc/hal_sdhost.h" +#include "driver/chip/sdmmc/sdmmc.h" + +static struct mmc_card card; +static hal_sd_info_t sd_info; +static int sd_initialize = 0; + +/**@brief Initialises a sd interface +* +* @param sd : the interface which should be initialised +* @param config : sd configuration structure +* +* @return 0 : on success. +* @return EIO : if an error occurred with any step +*/ +int32_t hal_sd_init(sd_dev_t *sd) +{ + int32_t ret; + SDC_InitTypeDef sdc_param = {0}; + + if (!sd_initialize) { + memset(&card, 0, sizeof(struct mmc_card)); + memset(&sd_info, 0, sizeof(hal_sd_info_t)); + + sd->port = 0; + HAL_SDC_Init(sd->port, &sdc_param); + + ret = mmc_rescan(&card, sd->port); + if (ret != 0) { + printf("[hal_sd]: init sd failed\n"); + return -1; + } + sd_initialize = 1; + } + + sd->config.bus_wide = card.bus_width; + if (card.state & MMC_STATE_HIGHSPEED) + sd->config.freq = 50000000; + else + sd->config.freq = 25000000; + sd->priv = (void*)&card; + sd_info.blk_size = 512; + sd_info.blk_nums = card.csd.capacity/512; + + return 0; +} + + +/**@brief read sd blocks +* +* @param sd : the interface which should be initialised +* @param data : pointer to the buffer which will store incoming data +* @param blk_addr : sd blk addr +* @param blks : sd blks +* @param timeout : timeout in milisecond +* @return 0 : on success. +* @return EIO : if an error occurred with any step +*/ +int32_t hal_sd_blks_read(sd_dev_t *sd, uint8_t *data, uint32_t blk_addr, uint32_t blks, uint32_t timeout) +{ + int32_t ret; + ret = mmc_block_read(sd->priv, data, blk_addr, blks); + + return ret; +} + +/**@brief write sd blocks +* +* @param sd : the interface which should be initialised +* @param data : pointer to the buffer which will store incoming data +* @param blk_addr : sd blk addr +* @param blks : sd blks +* @param timeout : timeout in milisecond +* @return 0 : on success. +* @return EIO : if an error occurred with any step +*/ +int32_t hal_sd_blks_write(sd_dev_t *sd, uint8_t *data, uint32_t blk_addr, uint32_t blks, uint32_t timeout) +{ + int32_t ret; + ret = mmc_block_write(sd->priv, data, blk_addr, blks); + + return ret; +} + +/**@brief erase sd blocks +* +* @param sd : the interface which should be initialised +* @param blk_start_addr : sd blocks start addr +* @param blk_end_addr : sd blocks end addr +* @return 0 : on success. +* @return EIO : if an error occurred with any step +*/ +int32_t hal_sd_erase(sd_dev_t *sd, uint32_t blk_start_addr, uint32_t blk_end_addr) +{ + //TODO: + return 0; +} + +/**@brief get sd state +* +* @param sd : the interface which should be initialised +* @param stat : pointer to the buffer which will store incoming data +* @return 0 : on success. +* @return EIO : if an error occurred with any step +*/ +int32_t hal_sd_stat_get(sd_dev_t *sd, hal_sd_stat *stat) +{ + *stat = SD_STAT_TRANSFER; + return 0; +} + +/**@brief get sd info +* +* @param sd : the interface which should be initialised +* @param stat : pointer to the buffer which will store incoming data +* @return 0 : on success. +* @return EIO : if an error occurred with any step +*/ +int32_t hal_sd_info_get(sd_dev_t *sd, hal_sd_info_t *info) +{ + info->blk_size = sd_info.blk_size; + info->blk_nums = sd_info.blk_nums; + return 0; +} + +/**@brief Deinitialises a sd interface +* +* @param sd : the interface which should be initialised +* @return 0 : on success. +* @return EIO : if an error occurred with any step +*/ +int32_t hal_sd_finalize(sd_dev_t *sd) +{ + sd_initialize = 0; + if (!card.host) + return 0; + + mmc_card_deinit(&card); + HAL_SDC_Deinit(0); + return 0; +} diff --git a/platform/mcu/xr871/aos/hal/soc/timer.c b/platform/mcu/xr871/aos/hal/soc/timer.c new file mode 100644 index 0000000000..240362afc4 --- /dev/null +++ b/platform/mcu/xr871/aos/hal/soc/timer.c @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include "driver/chip/hal_timer.h" + +/** + * @brief init a hardware timer + * @param tmr timer struct + * @param period micro seconds for repeat timer trigger + * @param auto_reoad set to 0, if you just need oneshot timer + * @param cb callback to be triggered after useconds + * @ch timer channel + * @param arg passed to cb + * @note period auto auto auto + * *-------|--------|--------|--------| + */ +int32_t hal_timer_init(timer_dev_t *tmr) +{ + HAL_Status status = HAL_ERROR; + TIMER_InitParam param = {0}; + TIMER_ID timer_id; + + if (tmr->port == TIMER0_ID) { + timer_id = TIMER0_ID; + } else if (tmr->port == TIMER0_ID) { + timer_id = TIMER1_ID; + } else { + printf("[timer]: init error, wrong port %d\n", tmr->port); + return; + } + param.arg = tmr->config.arg; + param.callback = tmr->config.cb; + param.cfg = HAL_TIMER_MakeInitCfg(TIMER_MODE_REPEAT, + TIMER_CLK_SRC_HFCLK, TIMER_CLK_PRESCALER_4); + param.isEnableIRQ = 1; + param.period = 6000 * tmr->config.period; //each cycle is 6us + + status = HAL_TIMER_Init(timer_id, ¶m); + if (status != HAL_OK) { + printf("timer int error %d\n", status); + } +} + +/** + * @brief init a hardware timer + * @param None + * @retval 0 == success + * EIO == failure + */ +int32_t hal_timer_start(timer_dev_t *tmr) +{ + HAL_TIMER_Start(tmr->port); + return 0; +} + +/** + * @brief stop a hardware timer + * @param tmr timer struct + * @param cb callback to be triggered after useconds + * @param arg passed to cb + */ +void hal_timer_stop(timer_dev_t *tmr) +{ + HAL_TIMER_Stop(tmr->port); +} diff --git a/platform/mcu/xr871/aos/hal/soc/uart.c b/platform/mcu/xr871/aos/hal/soc/uart.c new file mode 100644 index 0000000000..1ee38b7ef0 --- /dev/null +++ b/platform/mcu/xr871/aos/hal/soc/uart.c @@ -0,0 +1,196 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "hal/soc/soc.h" +#include "aos/kernel.h" +#include "driver/chip/hal_chip.h" +#include "board_config.h" + +#define MAX_UART_NUM 2 +#define UART_FIFO_SIZE 64 + +enum _uart_status_e +{ + _UART_STATUS_CLOSED = 0, + _UART_STATUS_OPENED, +}; + +typedef struct +{ + uint8_t uart; + uint8_t status; + aos_mutex_t tx_mutex; + kbuf_queue_t *bufque; +} _uart_drv_t; + +static _uart_drv_t _uart_drv[MAX_UART_NUM]; + +static void uart_rx_callback(void* arg) +{ + uint8_t rx_byte; + uart_dev_t* uart = (uart_dev_t*)arg; + _uart_drv_t *pdrv = &_uart_drv[uart->port]; + UART_T *uart_i = HAL_UART_GetInstance(uart->port); + uint32_t rx_cnt = HAL_UART_GetRxFIFOLevel(uart_i); + + while (rx_cnt --) { + rx_byte = HAL_UART_GetRxData(uart_i); + krhino_buf_queue_send(pdrv->bufque, &rx_byte, 1); + } +} + +int32_t hal_uart_init(uart_dev_t *uart) +{ + int32_t ret = -1; + _uart_drv_t *pdrv = &_uart_drv[uart->port]; + + printf("%s: Humble test enter, port = %d\n", __func__, uart->port); + if (uart != NULL) { + _uart_drv_t *pdrv = &_uart_drv[uart->port]; + UART_InitParam board_uart_param = { + .baudRate = 115200, + .parity = UART_PARITY_NONE, + .stopBits = UART_STOP_BITS_1, + .dataBits = UART_DATA_BITS_8, + .isAutoHwFlowCtrl = 0 + }; + + if (pdrv->status == _UART_STATUS_CLOSED) { + // convert uart parameters + board_uart_param.baudRate = uart->config.baud_rate; + if (uart->config.parity == ODD_PARITY) { + board_uart_param.parity = UART_PARITY_ODD; + } else if (uart->config.parity == EVEN_PARITY) { + board_uart_param.parity = UART_PARITY_EVEN; + } + if (uart->config.stop_bits == STOP_BITS_2) { + board_uart_param.stopBits = UART_STOP_BITS_2; + } + switch (uart->config.data_width) { + case DATA_WIDTH_5BIT: + board_uart_param.dataBits = UART_DATA_BITS_5; + break; + case DATA_WIDTH_6BIT: + board_uart_param.dataBits = UART_DATA_BITS_6; + break; + case DATA_WIDTH_7BIT: + board_uart_param.dataBits = UART_DATA_BITS_7; + break; + case DATA_WIDTH_8BIT: + board_uart_param.dataBits = UART_DATA_BITS_8; + break; + default: + board_uart_param.dataBits = UART_DATA_BITS_8; + break; + } + if (uart->config.flow_control != FLOW_CONTROL_DISABLED) + board_uart_param.isAutoHwFlowCtrl = 1; + + ret = HAL_UART_Init(uart->port, &board_uart_param); + if (ret == 0) { + kstat_t stat; + // create tx mutex + stat = krhino_buf_queue_dyn_create(&pdrv->bufque, "cli", UART_FIFO_SIZE , 1); + if(stat != RHINO_SUCCESS) { + HAL_UART_DeInit(uart->port); + return stat; + } + aos_mutex_new(&pdrv->tx_mutex); + HAL_UART_EnableRxCallback(uart->port, uart_rx_callback, uart); + pdrv->status = _UART_STATUS_OPENED; + } + } + } + + printf("%s: exit\n", __func__); + return ret; +} + +int32_t hal_uart_finalize(uart_dev_t *uart) +{ + int32_t ret = -1; + _uart_drv_t *pdrv = &_uart_drv[uart->port]; + + printf("%s: enter, port = %d\n", __func__, uart->port); + if (pdrv->status == _UART_STATUS_OPENED) { + aos_mutex_free(&pdrv->tx_mutex); + krhino_buf_queue_dyn_del(pdrv->bufque); + ret = HAL_UART_DeInit(uart->port); + pdrv->status = _UART_STATUS_CLOSED; + } + + printf("%s: exit\n", __func__); + return ret; +} + +int32_t hal_uart_send(uart_dev_t *uart, const void *data, uint32_t size, uint32_t timeout) +{ + int32_t ret = -1; + _uart_drv_t *pdrv = &_uart_drv[uart->port]; + + if (pdrv->status == _UART_STATUS_OPENED) { + aos_mutex_lock(&pdrv->tx_mutex, timeout); + ret = HAL_UART_Transmit_Poll(uart->port, data, size); + aos_mutex_unlock(&pdrv->tx_mutex); + } + return ret; +} + +int32_t hal_uart_recv(uart_dev_t *uart, void *data, uint32_t expect_size, uint32_t *recv_size, uint32_t timeout) +{ + kstat_t ret = -1; + _uart_drv_t *pdrv = &_uart_drv[uart->port]; + + if (pdrv->status == _UART_STATUS_OPENED) { + uint32_t readlen = 0; + uint32_t totallen = 0; + while (1) { + ret = krhino_buf_queue_recv(pdrv->bufque, timeout, data, &readlen); + if (ret != RHINO_SUCCESS) { + if (recv_size) { + *recv_size = totallen; + } + return -1; + } + if (readlen == 0) { + break; + } + totallen += readlen; + data += readlen; + if (totallen >= expect_size) { + break; + } + } + if (recv_size) { + *recv_size = totallen; + } + } + + return 0; +} diff --git a/platform/mcu/xr871/aos/hal/soc/wdg.c b/platform/mcu/xr871/aos/hal/soc/wdg.c new file mode 100644 index 0000000000..b78b43bd8e --- /dev/null +++ b/platform/mcu/xr871/aos/hal/soc/wdg.c @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "hal/soc/soc.h" +#include "driver/chip/hal_wdg.h" + +int32_t hal_wdg_init(wdg_dev_t *wdg) +{ + + WDG_InitParam param; + WDG_Timeout timeout_tbl[]={WDG_TIMEOUT_500MS,WDG_TIMEOUT_1SEC,WDG_TIMEOUT_2SEC,WDG_TIMEOUT_3SEC, + WDG_TIMEOUT_4SEC,WDG_TIMEOUT_5SEC,WDG_TIMEOUT_6SEC,WDG_TIMEOUT_6SEC, + WDG_TIMEOUT_8SEC,WDG_TIMEOUT_8SEC,WDG_TIMEOUT_10SEC,WDG_TIMEOUT_10SEC, + WDG_TIMEOUT_12SEC,WDG_TIMEOUT_12SEC,WDG_TIMEOUT_14SEC,WDG_TIMEOUT_14SEC, + WDG_TIMEOUT_16SEC}; + int32_t tbl_size; + + + tbl_size = sizeof(timeout_tbl)/sizeof(timeout_tbl[0]); + int pos = wdg->config.timeout > 500 ? (wdg->config.timeout/1000) : 0; + if (pos < tbl_size) { + param.timeout = timeout_tbl[pos]; + } + else { + param.timeout = timeout_tbl[tbl_size-1]; + } + printf("*********hal_wdg_init timeout:%d",param.timeout); + param.event = WDG_EVT_RESET; + + HAL_WDG_Init(¶m); + HAL_WDG_Start(); + + return 0; +} + +void hal_wdg_reload(wdg_dev_t *wdg) +{ + HAL_WDG_Feed(); +} + +int32_t hal_wdg_finalize(wdg_dev_t *wdg) +{ + HAL_WDG_Stop(); + + HAL_WDG_DeInit(); + + return 0; +} diff --git a/platform/mcu/xr871/aos/hal/wifi.c b/platform/mcu/xr871/aos/hal/wifi.c new file mode 100644 index 0000000000..40e7aeba7e --- /dev/null +++ b/platform/mcu/xr871/aos/hal/wifi.c @@ -0,0 +1,184 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include "common.h" +#include "wifi_port.h" + + +static int wifi_init(hal_wifi_module_t *m) +{ + return xr871_wlan_init(); +} + +static void wifi_get_mac_addr(hal_wifi_module_t *m, uint8_t *mac) +{ + xr871_wlan_get_mac_addr(mac); +} + +static int wifi_start(hal_wifi_module_t *m, hal_wifi_init_type_t *init_para) +{ + int ret; + + ret = xr871_wlan_start(init_para); + + return ret; +} + +static int wifi_start_adv(hal_wifi_module_t *m, hal_wifi_init_type_adv_t *init_para_adv) +{ + int ret; + + ret = xr871_wlan_start_adv(init_para_adv); + + return ret; +} + +static int get_ip_stat(hal_wifi_module_t *m, hal_wifi_ip_stat_t *out_net_para, hal_wifi_type_t wifi_type) +{ + int ret; + + ret = xr871_wlan_get_ip_stat(out_net_para, wifi_type); + + return ret; +} + +static int get_link_stat(hal_wifi_module_t *m, hal_wifi_link_stat_t *out_stat) +{ + int ret; + + ret = xr871_wlan_get_link_stat(out_stat); + + return ret; +} + +static void start_scan(hal_wifi_module_t *m) +{ + xr871_wlan_start_scan(); +} + +static void start_scan_adv(hal_wifi_module_t *m) +{ + xr871_wlan_start_adv_scan(); +} + +static int power_off(hal_wifi_module_t *m) +{ + return xr871_wlan_power_off(); +} + +static int power_on(hal_wifi_module_t *m) +{ + return xr871_wlan_power_on(); +} + +static int suspend(hal_wifi_module_t *m) +{ + return xr871_wlan_suspend(); +} + +static int suspend_station(hal_wifi_module_t *m) +{ + return xr871_wlan_suspend_station(); +} + +static int suspend_soft_ap(hal_wifi_module_t *m) +{ + return xr871_wlan_suspend_softap(); +} + +static int set_channel(hal_wifi_module_t *m, int ch) +{ + return xr871_wlan_set_channel(ch); +} + +static void start_monitor(hal_wifi_module_t *m) +{ + xr871_wlan_start_monitor(); +} + +static void stop_monitor(hal_wifi_module_t *m) +{ + xr871_wlan_stop_monitor(); +} + +static void register_monitor_cb(hal_wifi_module_t *m, monitor_data_cb_t fn) +{ + xr871_wlan_register_monitor_cb(fn); +} + +static void register_wlan_mgnt_monitor_cb(hal_wifi_module_t *m, monitor_data_cb_t fn) +{ + xr871_wlan_register_mgnt_monitor_cb(fn); +} + +static int wlan_send_80211_raw_frame(hal_wifi_module_t *m, uint8_t *buf, int len) +{ + return xr871_wlan_send_80211_raw_frame(buf, len - 4); // len-4=exclude FCS +} + +static void start_debug_mode(hal_wifi_module_t *m) +{ + xr871_wlan_start_debug_mode(); +} + +static void stop_debug_mode(hal_wifi_module_t *m) +{ + xr871_wlan_stop_debug_mode(); +} + +hal_wifi_module_t sim_aos_wifi_xr871 = { + .base.name = "sim_aos_wifi_xr871", + .init = wifi_init, + .get_mac_addr = wifi_get_mac_addr, + .start = wifi_start, + .start_adv = wifi_start_adv, + .get_ip_stat = get_ip_stat, + .get_link_stat = get_link_stat, + .start_scan = start_scan, + .start_scan_adv = start_scan_adv, + .power_off = power_off, + .power_on = power_on, + .suspend = suspend, + .suspend_station = suspend_station, + .suspend_soft_ap = suspend_soft_ap, + .set_channel = set_channel, + .start_monitor = start_monitor, + .stop_monitor = stop_monitor, + .register_monitor_cb = register_monitor_cb, + .register_wlan_mgnt_monitor_cb = register_wlan_mgnt_monitor_cb, + .wlan_send_80211_raw_frame = wlan_send_80211_raw_frame, + .start_debug_mode = start_debug_mode, + .stop_debug_mode = stop_debug_mode +}; + diff --git a/platform/mcu/xr871/aos/hal/wifi_port.c b/platform/mcu/xr871/aos/hal/wifi_port.c new file mode 100644 index 0000000000..125a68356b --- /dev/null +++ b/platform/mcu/xr871/aos/hal/wifi_port.c @@ -0,0 +1,584 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include "common.h" +#include "net/wlan/wlan.h" +#include "net_ctrl.h" +#include "wifi_port.h" +#include "sysinfo.h" +#include "kernel/os/os.h" +#include "common/framework/sys_ctrl/sys_ctrl.h" + +#define WIFI_DEBUG(...) do { \ + printf("[wifi_port]: "); \ + printf(__VA_ARGS__); \ + } while (0) + +#define MAX_SCAN_RESULTS 10 + +extern struct netif *g_wlan_netif; + +static wlan_monitor_rx_cb g_monitor_cb; +static int g_network_up = 0; + +static enum scan_type { + SCAN_TYPE_NORMAL, + SCAN_TYPE_ADVANCE +} g_scan_type = SCAN_TYPE_NORMAL; + +void xr871_wlan_msg_process(uint32_t event, uint32_t data) +{ + struct netif *nif = g_wlan_netif; + uint16_t type = EVENT_SUBTYPE(event); + + WIFI_DEBUG("%s msg (%u, %u)\n", __func__, type, data); + switch (type) { + case NET_CTRL_MSG_WLAN_CONNECTED: + break; + case NET_CTRL_MSG_WLAN_SCAN_SUCCESS: + if (g_scan_type == SCAN_TYPE_NORMAL) + xr871_scan_compeleted_event(); + if (g_scan_type == SCAN_TYPE_ADVANCE) + xr871_scan_adv_compeleted_event(); + break; + case NET_CTRL_MSG_WLAN_CONNECT_FAILED: + xr871_connect_fail_event(); + break; + case NET_CTRL_MSG_NETWORK_UP: + if (wlan_if_get_mode(nif) == WLAN_MODE_STA) { + xr871_ip_got_event(); + } + g_network_up = 1; + xr871_stat_chg_event(); + break; + case NET_CTRL_MSG_NETWORK_DOWN: + g_network_up = 0; + xr871_stat_chg_event(); + break; + case NET_CTRL_MSG_WLAN_DISCONNECTED: + case NET_CTRL_MSG_WLAN_SCAN_FAILED: + case NET_CTRL_MSG_WLAN_4WAY_HANDSHAKE_FAILED: + WIFI_DEBUG("do nothing msg (%u, %u)\n", type, data); + break; + default: + WIFI_DEBUG("unknown msg (%u, %u)\n", type, data); + break; + } +} + +int xr871_wlan_init(void) +{ + // create network event listener + observer_base *ob; + + WIFI_DEBUG("wlan init, register network observer\n"); + ob = sys_callback_observer_create(CTRL_MSG_TYPE_NETWORK, + NET_CTRL_MSG_ALL, + xr871_wlan_msg_process); + if (ob == NULL) + return -1; + if (sys_ctrl_attach(ob) != 0) + return -1; + + WIFI_DEBUG("xr871 wifi init success!!\n"); + return 0; +} + +void xr871_wlan_get_mac_addr(uint8_t *mac) +{ + struct sysinfo *info = sysinfo_get();; + + WIFI_DEBUG("wlan get mac address\n"); + memcpy(mac, info->mac_addr, 6); + WIFI_DEBUG("MAC: %02X:%02X:%02X:%02X:%02X:%02X\n", + mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); +} + +int xr871_wlan_start(hal_wifi_init_type_t *init_para) +{ + int ret = 0; + int ssid_len = strlen(init_para->wifi_ssid); + int psk_len = strlen(init_para->wifi_key); + + WIFI_DEBUG("wlan start, mode %d\n", init_para->wifi_mode); + + if (ssid_len > 32) + ssid_len = 32; + switch (init_para->wifi_mode) { + case STATION: /* STA */ + WIFI_DEBUG("station start, ssid %s, key %s\n", + init_para->wifi_ssid, init_para->wifi_key); + net_switch_mode(WLAN_MODE_STA); + wlan_sta_disable(); + if (psk_len > 0) { + wlan_sta_set(init_para->wifi_ssid, ssid_len, init_para->wifi_key); + } + else { + wlan_sta_set(init_para->wifi_ssid, ssid_len, NULL); + } + wlan_sta_enable(); + break; + + case SOFT_AP: /* AP */ + WIFI_DEBUG("hostap start, ssid %s, key %s\n", + init_para->wifi_ssid, init_para->wifi_key); + net_switch_mode(WLAN_MODE_HOSTAP); + wlan_ap_disable(); + wlan_ap_set(init_para->wifi_ssid, ssid_len, init_para->wifi_key); + wlan_ap_enable(); + break; + + default: + WIFI_DEBUG("error wifi mode\n"); + break; + } + + return ret; +} + +int xr871_wlan_start_adv(hal_wifi_init_type_adv_t *init_para_adv) +{ + int ssid_len = strlen(init_para_adv->ap_info.ssid); + int psk_len = strlen(init_para_adv->key); + WIFI_DEBUG("wlan start advance\n"); + if (ssid_len > 32) + ssid_len = 32; + net_switch_mode(WLAN_MODE_STA); + wlan_sta_disable(); + if (psk_len > 0) { + wlan_sta_set(init_para_adv->ap_info.ssid, ssid_len, init_para_adv->key); + } + else { + wlan_sta_set(init_para_adv->ap_info.ssid, ssid_len, NULL); + } + wlan_sta_enable(); + + return 0; +} + +int xr871_wlan_get_ip_stat(hal_wifi_ip_stat_t *out_net_para, + hal_wifi_type_t wifi_type) +{ + struct netif *nif = g_wlan_netif; + + if (nif == NULL || out_net_para == NULL) { + return -1; + } + + xr871_wlan_get_mac_addr(out_net_para->mac); /* mac */ + if (netif_is_up(nif) && netif_is_link_up(nif)) { + inet_ntoa_r(nif->ip_addr, out_net_para->ip, sizeof(out_net_para->ip)); /* ip */ + inet_ntoa_r(nif->gw, out_net_para->gate, sizeof(out_net_para->gate)); /* gate */ + inet_ntoa_r(nif->netmask, out_net_para->mask, sizeof(out_net_para->mask)); /* mask */ + WIFI_DEBUG("%c%c%d up, address:%s gateway:%s netmask:%s", + nif->name[0], nif->name[1], nif->num, + out_net_para->ip, out_net_para->gate, out_net_para->mask); + } else { + WIFI_DEBUG("%c%c%d down", nif->name[0], nif->name[1], nif->num); + return -1; + } + + /* dhcp */ + /* dns */ + /* broadcastip */ + if (wifi_type == STATION) { + + } else if (wifi_type == SOFT_AP) { + + } + + return 0; +} + +int xr871_wlan_get_link_stat(hal_wifi_link_stat_t *out_stat) +{ + wlan_sta_states_t state; + wlan_sta_ap_t ap; + + WIFI_DEBUG("wlan get link stat\n"); + if (out_stat == NULL) { + return -1; + } + memset(out_stat, 0, sizeof(hal_wifi_link_stat_t)); + + /* is_connected */ + wlan_sta_state(&state); + if (state == WLAN_STA_STATE_CONNECTED) { + wlan_sta_ap_info(&ap); + out_stat->is_connected = 1; + out_stat->wifi_strength = ap.rssi; + out_stat->channel = ap.channel; + memcpy(out_stat->ssid, ap.ssid.ssid, sizeof(ap.ssid.ssid_len)); + memcpy(out_stat->bssid, ap.bssid, sizeof(out_stat->bssid)); + + WIFI_DEBUG("======= link state =======\n"); + WIFI_DEBUG("connect :\n", out_stat->is_connected); + WIFI_DEBUG("strength :\n", out_stat->wifi_strength); + WIFI_DEBUG("ssid :%-32.32s\n", out_stat->ssid); + WIFI_DEBUG("bssid %02x:%02x:%02x:%02x:%02x:%02x\n", + out_stat->bssid[0], out_stat->bssid[1], out_stat->bssid[2], + out_stat->bssid[3], out_stat->bssid[4], out_stat->bssid[5]); + WIFI_DEBUG("channel :%d\n", out_stat->channel); + + } else { + WIFI_DEBUG("Request link state in HostAP mode ???\n"); + out_stat->is_connected = 0; + } + + return 0; +} + +void xr871_wlan_start_scan(void) +{ + WIFI_DEBUG("wlan start normal scan\n"); + g_scan_type = SCAN_TYPE_NORMAL; + + wlan_sta_scan_once(); +} + +void xr871_wlan_start_adv_scan(void) +{ + WIFI_DEBUG("wlan start advance scan\n"); + g_scan_type = SCAN_TYPE_ADVANCE; + + wlan_sta_scan_once(); +} + +int xr871_wlan_power_off(void) +{ + WIFI_DEBUG("wlan power off\n"); + net_sys_stop(); + return 0; +} + +int xr871_wlan_power_on(void) +{ + WIFI_DEBUG("wlan power on\n"); + net_sys_start(WLAN_MODE_STA); + return 0; +} + +int xr871_wlan_suspend(void) +{ + struct netif *nif = g_wlan_netif; + + WIFI_DEBUG("wlan suspend\n"); + if (nif != NULL) { + enum wlan_mode mode = wlan_if_get_mode(nif); + if (mode == WLAN_MODE_HOSTAP) { + wlan_ap_disable(); + } + if (mode == WLAN_MODE_STA) { + wlan_sta_disable(); + } + if (mode == WLAN_MODE_MONITOR) { + } + } + return 0; +} + +int xr871_wlan_suspend_station(void) +{ + WIFI_DEBUG("wlan station suspend\n"); + wlan_sta_disable(); + return 0; +} + +int xr871_wlan_suspend_softap(void) +{ + WIFI_DEBUG("wlan softap suspend\n"); + wlan_ap_disable(); + return 0; +} + +int xr871_wlan_set_channel(int ch) +{ + struct netif *nif = g_wlan_netif; + + WIFI_DEBUG("wlan set monitor channel %d\n", ch); + return wlan_monitor_set_channel(nif, ch); +} + +void xr871_wlan_start_monitor(void) +{ + struct netif *nif = g_wlan_netif; + + WIFI_DEBUG("wlan start monitor mode, nif %p, cb %p\n", nif, g_monitor_cb); + + if (nif) { + wlan_monitor_set_rx_cb(nif, g_monitor_cb); + net_switch_mode(WLAN_MODE_MONITOR); + } +} + +void xr871_wlan_stop_monitor(void) +{ + struct netif *nif = g_wlan_netif; + + WIFI_DEBUG("wlan stop monitor mode\n"); + if (nif) { + wlan_monitor_set_rx_cb(nif, NULL); + } +} + +void xr871_wlan_register_monitor_cb(monitor_data_cb_t fn) +{ + WIFI_DEBUG("wlan register monitor callback: %p\n", fn); + g_monitor_cb = (wlan_monitor_rx_cb)fn; +} + +void xr871_wlan_register_mgnt_monitor_cb(monitor_data_cb_t fn) +{ + WIFI_DEBUG("wlan register manage monitor callback: %p\n", fn); + g_monitor_cb = (wlan_monitor_rx_cb)fn; +} + +int xr871_wlan_send_80211_raw_frame(uint8_t *buf, int len) +{ + WIFI_DEBUG("send raw frame(not support now)\n"); + + // dump raw frame data + if (0) + { + uint32_t i = 0; + printf("dump frame data:\n"); + for (i = 0; i < len; i++) { + printf("%02x ", buf[i]); + if ((i % 32) == 31) + printf("\n"); + } + printf("\n"); + } + return 0; +} + +void xr871_wlan_start_debug_mode(void) +{ + WIFI_DEBUG("start wifi debug mode\n"); +} + +void xr871_wlan_stop_debug_mode(void) +{ + WIFI_DEBUG("stop wifi debug mode\n"); +} + +void xr871_connect_fail_event(int err, void *arg) +{ + hal_wifi_module_t *m = hal_wifi_get_default_module(); + + WIFI_DEBUG("handle connect fail event\n"); + + if (m != NULL && m->ev_cb != NULL && m->ev_cb->connect_fail != NULL) { + m->ev_cb->connect_fail(m, err, arg); + } +} + +void xr871_ip_got_event(void) +{ + hal_wifi_module_t *m = hal_wifi_get_default_module(); + + WIFI_DEBUG("handle ip got event\n"); + if (m != NULL && m->ev_cb != NULL && m->ev_cb->ip_got != NULL) { + hal_wifi_ip_stat_t net = {0}; + xr871_wlan_get_ip_stat(&net, STATION); + m->ev_cb->ip_got(m, &net, NULL); + } +} + +void xr871_stat_chg_event(void) +{ + hal_wifi_module_t *m = hal_wifi_get_default_module(); + struct netif *nif = g_wlan_netif; + + WIFI_DEBUG("handle state change event\n"); + + if (nif != NULL && m != NULL && m->ev_cb != NULL && m->ev_cb->stat_chg != NULL) { + hal_wifi_event_t stat; + enum wlan_mode mode = wlan_if_get_mode(nif); + if (mode == WLAN_MODE_STA) { + stat = netif_is_link_up(nif) ? NOTIFY_STATION_UP : NOTIFY_STATION_DOWN; + } else if (mode == WLAN_MODE_HOSTAP) { + stat = netif_is_link_up(nif) ? NOTIFY_AP_UP : NOTIFY_AP_DOWN; + } + m->ev_cb->stat_chg(m, stat, NULL); + } +} + +static const uint32_t channel_freq_tbl[] = { + 2412, 2417, 2422, 2427, 2432, 2437, 2442, 2447, 2452, 2457, 2462, 2467, 2472 +}; + +static int32_t freq_to_chan(int32_t freq) +{ + int32_t i; + uint32_t chs = sizeof(channel_freq_tbl)/sizeof(channel_freq_tbl[0]); + + for (i = 0; i < chs && freq != channel_freq_tbl[i]; i++); + if(i == chs) { + return -1; + } + + return i + 1; +} + +static char rssi_to_ap_power(int32_t rssi) +{ + char ap_power = rssi * 100L / 128; + + if (ap_power > 100) { + ap_power = 100; + } + + return ap_power; +} + +void xr871_scan_compeleted_event(enum scan_type type) +{ + hal_wifi_module_t *m = hal_wifi_get_default_module(); + hal_wifi_scan_result_t result = {0}; + wlan_sta_scan_results_t scan_results = {0}; + int ret; + + scan_results.ap = malloc(MAX_SCAN_RESULTS * sizeof(wlan_sta_ap_t)); + if (scan_results.ap != NULL) { + scan_results.size = MAX_SCAN_RESULTS; + ret = wlan_sta_scan_result(&scan_results); + if (ret == 0) { + result.ap_num = scan_results.num; + result.ap_list = malloc(result.ap_num * sizeof(*(result.ap_list))); + + WIFI_DEBUG("scan result: ap_num: %d, ap %p, ap_list %p\n", result.ap_num, + scan_results.ap, result.ap_list); + + if (result.ap_list != NULL) { + int i; + + for (i = 0; i < scan_results.num; i++) { + memset(result.ap_list[i].ssid, 0, sizeof(result.ap_list[i].ssid)); + memcpy(result.ap_list[i].ssid, scan_results.ap[i].ssid.ssid, + scan_results.ap[i].ssid.ssid_len); + result.ap_list[i].ap_power = rssi_to_ap_power(scan_results.ap[i].rssi); + + WIFI_DEBUG("#%-2d: ssid: %-32.32s ap_power: %d\n", i+1, + result.ap_list[i].ssid, result.ap_list[i].ap_power); + } + + if (m->ev_cb != NULL && m->ev_cb->scan_compeleted != NULL) { + m->ev_cb->scan_compeleted(m, &result, NULL); + } + + free(result.ap_list); + } + } + + free(scan_results.ap); + } +} + +void xr871_scan_adv_compeleted_event(enum scan_type type) +{ + hal_wifi_module_t *m = hal_wifi_get_default_module(); + hal_wifi_scan_result_adv_t result = {0}; + wlan_sta_scan_results_t scan_results = {0}; + wlan_sta_config_t config = {0}; + int kmgt, cipher; + int ret; + + scan_results.ap = malloc(MAX_SCAN_RESULTS * sizeof(wlan_sta_ap_t)); + if (scan_results.ap != NULL) { + scan_results.size = MAX_SCAN_RESULTS; + ret = wlan_sta_scan_result(&scan_results); + if (ret == 0) { + result.ap_num = scan_results.num; + result.ap_list = malloc(result.ap_num * sizeof(*(result.ap_list))); + + WIFI_DEBUG("scan result: ap_num: %d\n", result.ap_num); + + if (result.ap_list != NULL) { + int i; + + for (i = 0; i < scan_results.num; i++) { + result.ap_list[i].ap_power = rssi_to_ap_power(scan_results.ap[i].rssi); + result.ap_list[i].channel = freq_to_chan(scan_results.ap[i].freq); + memset(result.ap_list[i].ssid, 0, sizeof(result.ap_list[i].ssid)); + memcpy(result.ap_list[i].ssid, scan_results.ap[i].ssid.ssid, + scan_results.ap[i].ssid.ssid_len); + memcpy(result.ap_list[i].bssid, scan_results.ap[i].bssid, + sizeof(result.ap_list[i].bssid)); + + config.field = WLAN_STA_FIELD_KEY_MGMT; + if (wlan_sta_get_config(&config) != 0) { + WIFI_DEBUG("***error, get config failed\n"); + } + if (config.u.key_mgmt = WPA_KEY_MGMT_NONE) { + result.ap_list[i].security = SECURITY_TYPE_NONE; + } else { + // Fixup: + WIFI_DEBUG("***warning, temp security impl\n"); + result.ap_list[i].security = SECURITY_TYPE_WPA2_MIXED; + } + + WIFI_DEBUG("#%-2d: ssid: %-32.32s ap_power: %d, ch %d, security %d " + "bssid: %02x:%02x:%02x:%02x:%02x:%02x", + i+1, + result.ap_list[i].ssid, result.ap_list[i].ap_power, + result.ap_list[i].channel, result.ap_list[i].security, + result.ap_list[i].bssid[0], result.ap_list[i].bssid[1], + result.ap_list[i].bssid[2], result.ap_list[i].bssid[3], + result.ap_list[i].bssid[4], result.ap_list[i].bssid[5]); + } + + if (m->ev_cb != NULL && m->ev_cb->scan_compeleted != NULL) { + m->ev_cb->scan_compeleted(m, &result, NULL); + } + + free(result.ap_list); + } + } + + free(scan_results.ap); + } +} + +void xr871_para_chg_event(void) +{ + WIFI_DEBUG("%s To Do\n", __func__); +} + +void xr871_fatal_err_event(void *arg) +{ + WIFI_DEBUG("%s To Do\n", __func__); +} + diff --git a/platform/mcu/xr871/aos/hal/wifi_port.h b/platform/mcu/xr871/aos/hal/wifi_port.h new file mode 100644 index 0000000000..cc29ded98a --- /dev/null +++ b/platform/mcu/xr871/aos/hal/wifi_port.h @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _WIFI_PORT_H_ +#define _WIFI_PORT_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +int xr871_wlan_init(void); +void xr871_wlan_get_mac_addr(uint8_t *mac); +int xr871_wlan_start(hal_wifi_init_type_t *init_para); +int xr871_wlan_start_adv(hal_wifi_init_type_adv_t *init_para_adv); +int xr871_wlan_get_ip_stat(hal_wifi_ip_stat_t *out_net_para, hal_wifi_type_t wifi_type); +int xr871_wlan_get_link_stat(hal_wifi_link_stat_t *out_stat); +void xr871_wlan_start_scan(void); +void xr871_wlan_start_adv_scan(void); +int xr871_wlan_power_off(void); +int xr871_wlan_power_on(void); +int xr871_wlan_suspend(void); +int xr871_wlan_suspend_station(void); +int xr871_wlan_suspend_softap(void); +int xr871_wlan_set_channel(int ch); +void xr871_wlan_start_monitor(void); +void xr871_wlan_stop_monitor(void); +void xr871_wlan_register_monitor_cb(monitor_data_cb_t fn); +void xr871_wlan_register_mgnt_monitor_cb(monitor_data_cb_t fn); +int xr871_wlan_send_80211_raw_frame(uint8_t *buf, int len); +void xr871_wlan_start_debug_mode(void); +void xr871_wlan_stop_debug_mode(void); + +#ifdef __cplusplus +} +#endif + +#endif /* _WIFI_PORT_H_ */ diff --git a/platform/mcu/xr871/aos/soc_impl.c b/platform/mcu/xr871/aos/soc_impl.c new file mode 100644 index 0000000000..3ad7468dcb --- /dev/null +++ b/platform/mcu/xr871/aos/soc_impl.c @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2015-2017 Alibaba Group Holding Limited + */ + +#include +#include +#include +#include + +void soc_driver_init() +{ + hal_init(); + + printf("soc_driver_init done\n"); +} + +void soc_system_init() +{ + printf("%s %d\n", __func__, __LINE__); + platform_init(); +} + +void soc_systick_init() +{ + /* enable system tick */ + extern uint32_t SystemCoreClock; + SysTick_Config(SystemCoreClock/RHINO_CONFIG_TICKS_PER_SECOND); +} + +void SysTick_Handler(void) +{ + krhino_intrpt_enter(); + krhino_tick_proc(); + krhino_intrpt_exit(); +} diff --git a/platform/mcu/xr871/bin/xr871/boot.bin b/platform/mcu/xr871/bin/xr871/boot.bin new file mode 100644 index 0000000000000000000000000000000000000000..70e851cbe35518d6c616a6a55e0735c8d3f60337 GIT binary patch literal 31208 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ͻa?;zDrt6y-Ax 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 By6*r0 literal 0 HcmV?d00001 diff --git a/platform/mcu/xr871/bin/xr871/net.bin b/platform/mcu/xr871/bin/xr871/net.bin new file mode 100644 index 0000000000000000000000000000000000000000..0f2c96c1664b6872fdb6b1dc114da178fcb55226 GIT binary patch literal 287000 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 literal 0 HcmV?d00001 diff --git a/platform/mcu/xr871/bin/xr871/net_ap.bin b/platform/mcu/xr871/bin/xr871/net_ap.bin new file mode 100644 index 0000000000000000000000000000000000000000..17a496282ea1d399a486d6a8b4315fdf8b0d3af4 GIT binary patch literal 254664 zcmZ_034D~*)jxipZDz7hG6Avy&&&jvWCBhS&;Tw&COnzRV8f=B#X3pQJ_#TZx}ahm z78Oz3ETC*E72BfN%7C#%iJ^imUFkS0+WS#%_J`|K2n1wT|S5l(q61`6;ghsWY&O}b! zWd;!7|H!*zsF;%2{`ECe@at=FLHQC*!Sh-?uOsxAy+fT7FGU}iQ!zqyDt0OemOy1i zDq0FwRbH;*QUkMmCROfsyWHJDmnqPGt@39K7gsy`pLz2e`PR2PD@Fv`E=FHv^|HKo z3}eeMVDF{?!-G9}@njAj!_^WrRegOeHR#1PJ-D%|&|Bz><#m1Q$qjnkGMqj>cKl`{)Aetpo%I$i5Oah?#IH!iLBw6@pnp0>)QoqI>?~zta+1S48=DqsmVI?`tLV3c zMDN?zNRYGJ>&V#IK*BCMSL>r-V z`jKv+^2GeV0x`DmdyIa!Cf>o*o+zD!YR< zg6)Nii6qV&?~AV>_I�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%0DV??`Xkz4I)9nK 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
I?&vE$%AjW4u_YASpll$!GdZodkX9aI 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 literal 0 HcmV?d00001 diff --git a/platform/mcu/xr871/bin/xr871/wlan_bl.bin b/platform/mcu/xr871/bin/xr871/wlan_bl.bin new file mode 100644 index 0000000000000000000000000000000000000000..3a9adff8658c23c28ee933185702ebbbed5481a6 GIT binary patch literal 2308 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

xWLG^kL2s3C6}(uI_k>g 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 f@H}KRVEWs + +//#define inline inline +#define __inline inline +#define __inline__ inline + +#ifdef __always_inline +#undef __always_inline /* already defined in */ +#define __always_inline inline __attribute__((always_inline)) +#endif + +#ifndef __noinline +#define __noinline __attribute__((__noinline__)) +#endif + +#define __packed __attribute__((__packed__)) +#define __asm asm +#define __weak __attribute__((weak)) + +#else +#error "Compiler not supported." +#endif + +#endif /* _COMPILER_H_ */ diff --git a/platform/mcu/xr871/include/console/console.h b/platform/mcu/xr871/include/console/console.h new file mode 100644 index 0000000000..0f1b642db0 --- /dev/null +++ b/platform/mcu/xr871/include/console/console.h @@ -0,0 +1,65 @@ +/** + * @file console.h + * @author XRADIO IOT WLAN Team + */ + +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _CONSOLE_CONSOLE_H_ +#define _CONSOLE_CONSOLE_H_ + +#include "driver/chip/hal_uart.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** @brief type define of console command executing function */ +typedef void (*console_cmd_exec_func)(char *cmd); + +typedef struct console_param { + UART_ID uart_id; /* console's uart ID */ + console_cmd_exec_func cmd_exec; /* console command executing function */ +} console_param_t; + +int console_start(console_param_t *param); +void console_stop(void); + +int console_write(uint8_t *buf, int32_t len); + +void console_disable(void); +void console_enable(void); +UART_ID console_get_uart_id(void); + +#ifdef __cplusplus +} +#endif + +#endif /* _CONSOLE_CONSOLE_H_ */ diff --git a/platform/mcu/xr871/include/driver/chip/chip.h b/platform/mcu/xr871/include/driver/chip/chip.h new file mode 100644 index 0000000000..0e6c83dc1a --- /dev/null +++ b/platform/mcu/xr871/include/driver/chip/chip.h @@ -0,0 +1,166 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _DRIVER_CHIP_CHIP_H_ +#define _DRIVER_CHIP_CHIP_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Configuration of the Cortex-M4/3 Processor and Core Peripherals + */ + +#ifdef __CONFIG_CPU_CM4F +#define __CM4_REV 0x0001 /*!< Core revision r0p1 */ +#define __FPU_PRESENT 1 /*!< FPU present */ +#else +#define __CM3_REV 0x0201 /*!< Core revision r2p1 */ +#define __FPU_PRESENT 0 /*!< FPU present */ +#endif + +#define __MPU_PRESENT 0 /*!< MPU present */ +#define __NVIC_PRIO_BITS 3 /*!< uses 3 Bits for the Priority Levels */ +#define __Vendor_SysTickConfig 0 /*!< Set to 1 if different SysTick Config is used */ + +/*!< Interrupt Number Definition */ +typedef enum { + /* Cortex-M4/3 Processor Exceptions Numbers*/ + NonMaskableInt_IRQn = -14, /*!< 2 Non Maskable Interrupt */ + MemoryManagement_IRQn = -12, /*!< 4 Cortex-M3 Memory Management Interrupt */ + BusFault_IRQn = -11, /*!< 5 Cortex-M3 Bus Fault Interrupt */ + UsageFault_IRQn = -10, /*!< 6 Cortex-M3 Usage Fault Interrupt */ + SVCall_IRQn = -5, /*!< 11 Cortex-M3 SV Call Interrupt */ + DebugMonitor_IRQn = -4, /*!< 12 Cortex-M3 Debug Monitor Interrupt */ + PendSV_IRQn = -2, /*!< 14 Cortex-M3 Pend SV Interrupt */ + SysTick_IRQn = -1, /*!< 15 Cortex-M3 System Tick Interrupt */ + + /* Specific Interrupt Numbers */ + DMA_IRQn = 0, + GPIOA_IRQn = 1, + SDC_IRQn = 2, + MBOX_A_IRQn = 3, + UART0_IRQn = 4, + UART1_IRQn = 5, + SPI0_IRQn = 6, + SPI1_IRQn = 7, + I2C0_IRQn = 8, + I2C1_IRQn = 9, + WDG_IRQn = 10, + TIMER0_IRQn = 11, + TIMER1_IRQn = 12, + RTC_SEC_ALARM_IRQn = 13, + RTC_WDAY_ALARM_IRQn = 14, + CSI_IRQn = 15, + I2S_IRQn = 16, + PWM_ECT_IRQn = 17, + CE_IRQn = 18, + GPADC_IRQn = 19, + GPIOB_IRQn = 20, + DMIC_IRQn = 21, + IRRX_IRQn = 22, + IRTX_IRQn = 23, + + MBOX_N_IRQn = 24, + A_WAKEUP_IRQn = 25, + FLASHC_IRQn = 26, + N_UART_IRQn = 27, + MAX_IRQn, /* keep last */ +} IRQn_Type; + +#define NVIC_PERIPH_IRQ_NUM MAX_IRQn +#define NVIC_PERIPH_IRQ_OFFSET 16 +#if (MAX_IRQn <= 32) +#define NVIC_PERIPH_IRQ_MASK0 ((1 << MAX_IRQn) - 1) +#else +#define NVIC_PERIPH_IRQ_MASK0 (0xffffffff) +#define NVIC_PERIPH_IRQ_MASK1 ((1 << (MAX_IRQn - 32)) - 1) +#endif +#define NVIC_VECTOR_TABLE_SIZE (NVIC_PERIPH_IRQ_OFFSET + NVIC_PERIPH_IRQ_NUM) + +#ifdef __CONFIG_CPU_CM4F +#include "driver/cmsis/core_cm4.h" +#else +#include "driver/cmsis/core_cm3.h" +#endif + +/*!< Peripheral memory map */ +#define PERIPH_BASE (0x40000000U) + +#define DMA_BASE (PERIPH_BASE + 0x00001000) +#define SDIO_BASE (PERIPH_BASE + 0x00002000) +#define CE_BASE (PERIPH_BASE + 0x00004000) +#define SPI0_BASE (PERIPH_BASE + 0x00005000) +#define SPI1_BASE (PERIPH_BASE + 0x00006000) +#define DCMI_BASE (PERIPH_BASE + 0x00007000) +#define SPINLOCK_BASE (PERIPH_BASE + 0x00008000) +#define MBOX_A_BASE (PERIPH_BASE + 0x00009000) +#define SYSCTL_BASE (PERIPH_BASE + 0x0000A000) +#define FLASH_CTRL_BASE (PERIPH_BASE + 0x0000B000) +#define FLASH_CACHE_BASE (PERIPH_BASE + 0x0000C000) +#define PRCM_BASE (PERIPH_BASE + 0x00040000) +#define CCM_BASE (PERIPH_BASE + 0x00040400) +#define TIMER_BASE (PERIPH_BASE + 0x00040800) +#define UART0_BASE (PERIPH_BASE + 0x00040C00) +#define UART1_BASE (PERIPH_BASE + 0x00041000) +#define RTC_BASE (PERIPH_BASE + 0x00041800) +#define I2C0_BASE (PERIPH_BASE + 0x00041c00) +#define I2C1_BASE (PERIPH_BASE + 0x00042000) +#define DMIC_BASE (PERIPH_BASE + 0x00042400) +#define PWM_BASE (PERIPH_BASE + 0x00042800) +#define I2S_BASE (PERIPH_BASE + 0x00042C00) +#define GPADC_BASE (PERIPH_BASE + 0x00043000) +#define IRTX_BASE (PERIPH_BASE + 0x00043400) +#define IRRX_BASE (PERIPH_BASE + 0x00043800) +#define SID_BASE (PERIPH_BASE + 0x00043C00) +#define GPIOA_CTRL_BASE (PERIPH_BASE + 0x00050000) +#define GPIOB_CTRL_BASE (PERIPH_BASE + 0x00050024) +#define GPIOA_IRQ_BASE (PERIPH_BASE + 0x00050200) +#define GPIOB_IRQ_BASE (PERIPH_BASE + 0x00050220) + +#define MBOX_N_BASE (0xA0003000U) +#define UARTN_BASE (0xA0042000U) + +/* Value of the External oscillator in Hz */ +#define HOSC_CLOCK_24M (24U * 1000U * 1000U) +#define HOSC_CLOCK_26M (26U * 1000U * 1000U) +#define HOSC_CLOCK_40M (40U * 1000U * 1000U) +#define HOSC_CLOCK_52M (52U * 1000U * 1000U) + +#define LOSC_CLOCK (32768U) + +#define SYS_PLL_CLOCK (960U * 1000U * 1000U) +#define SYS_LFCLOCK LOSC_CLOCK + +#ifdef __cplusplus +} +#endif + +#endif /* _DRIVER_CHIP_CHIP_H_ */ diff --git a/platform/mcu/xr871/include/driver/chip/flashchip/flash_chip.h b/platform/mcu/xr871/include/driver/chip/flashchip/flash_chip.h new file mode 100644 index 0000000000..f4b104a6f0 --- /dev/null +++ b/platform/mcu/xr871/include/driver/chip/flashchip/flash_chip.h @@ -0,0 +1,234 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef FLASH_CHIP_H_ +#define FLASH_CHIP_H_ + +#include +#include +#include "types.h" + +typedef struct FlashChipBase FlashChipBase; +typedef struct FlashDrvierBase FlashDrvierBase; +typedef struct XipDriverBase XipDriverBase; + +typedef struct InstructionField +{ + uint32_t data; + uint8_t *pdata; + uint32_t len; + uint32_t line; +} InstructionField; + +typedef int (*FlashDrvIface)(FlashChipBase *base, InstructionField *cmd, InstructionField *addr, InstructionField *dummy, InstructionField *data); + +typedef enum FlashReadMode +{ + FLASH_READ_NORMAL_MODE = 1 << 0, /*!< cmd: 1 line, addr + dummy: 1 line, data: 1 line, freq can't too high */ + FLASH_READ_FAST_MODE = 1 << 1, /*!< cmd: 1 line, addr + dummy: 1 line, data: 1 line */ + FLASH_READ_DUAL_O_MODE = 1 << 2, /*!< cmd: 1 line, addr + dummy: 1 line, data: 2 line */ + FLASH_READ_DUAL_IO_MODE = 1 << 3, /*!< cmd: 1 line, addr + dummy: 2 line, data: 2 line */ + FLASH_READ_QUAD_O_MODE = 1 << 4, /*!< cmd: 1 line, addr + dummy: 1 line, data: 4 line */ + FLASH_READ_QUAD_IO_MODE = 1 << 5, /*!< cmd: 1 line, addr + dummy: 4 line, data: 4 line */ + FLASH_READ_QPI_MODE = 1 << 6, /*!< cmd: 4 line, addr + dummy: 4 line, data: 4 line */ +} FlashReadMode; + +typedef enum FlashEraseMode +{ + FLASH_ERASE_NOSUPPORT = 0, + FLASH_ERASE_4KB = 1 << 12, /*!< 4KB erase mode, command: 0x20 , esize: 4096 */ + FLASH_ERASE_32KB = 1 << 15, /*!< 32KB erase mode, command: 0x52 , esize: 32768 */ + FLASH_ERASE_64KB = 1 << 16, /*!< 64KB erase mode, command: 0xD8 , esize: 65536 */ + FLASH_ERASE_CHIP = 1 << 1, /*!< chip erase mode, command: 0xC7 *///0xC7, +} FlashEraseMode; + +typedef enum FlashStatus +{ + FLASH_STATUS1 = 1 << 0, /*!< flash status 1 */ + FLASH_STATUS2 = 1 << 1, /*!< flash status 2 */ + FLASH_STATUS3 = 1 << 2, /*!< flash status 3 */ +} FlashStatus; + +typedef enum FlashPageProgramMode +{ + FLASH_PAGEPROGRAM = 1 << 0, /*!< page program */ + FLASH_QUAD_PAGEPROGRAM = 1 << 1, /*!< quad page program */ +} FlashPageProgramMode; + +typedef enum FlashLockMode +{ + FlashLockMode_NOTHING /*!< nothing to support for now */ + /*TODO: tbc...*/ +} FlashLockMode; + +struct FlashChipBase +{ + /* + attribute + */ + uint32_t mJedec; + uint32_t mMaxFreq; + uint32_t mMaxReadFreq; //no need? + uint32_t mSize; + uint32_t mCsEn; //no need? + uint32_t mPageSize; //256bytes + + uint32_t mEraseSizeSupport; + uint16_t mReadStausSupport; + uint8_t mWriteStatusSupport; + uint8_t mPageProgramSupport; + uint16_t mReadSupport; + uint16_t mLockSupport; + + uint16_t mFlashStatus; + uint8_t mDummyCount; + + struct FlashDrvierBase *mDriver; + struct XipDriverBase *mXip; + + /* + private interface + */ + void (*writeEnable)(FlashChipBase *base); + void (*writeDisable)(FlashChipBase *base); + int (*readStatus)(FlashChipBase *base, FlashStatus status, uint8_t *data); + int (*writeStatus)(FlashChipBase *base, FlashStatus status, uint8_t *data); + int (*erase)(FlashChipBase *base, FlashEraseMode mode, uint32_t addr); //block count is realized in Flash erase. + int (*suspendErasePageprogram)(FlashChipBase *base); + int (*resumeErasePageprogram)(FlashChipBase *base); + int (*powerDown)(FlashChipBase *base); + int (*releasePowerDown)(FlashChipBase *base); + int (*jedecID)(FlashChipBase *base, uint32_t *data); + int (*enableQPIMode)(FlashChipBase *base); + int (*disableQPIMode)(FlashChipBase *base); +// int (*enableReset)(FlashChipBase *base); + int (*reset)(FlashChipBase *base); + int (*uniqueID)(FlashChipBase *base, uint8_t uid[8]); + int (*pageProgram)(FlashChipBase *base, FlashPageProgramMode mode, uint32_t addr, uint8_t *data, uint32_t size); + int (*read)(FlashChipBase *base, FlashReadMode mode, uint32_t addr, uint8_t *data, uint32_t size); + // tbc... + + int (*driverWrite)(FlashChipBase *base, InstructionField *cmd, InstructionField *addr, InstructionField *dummy, InstructionField *data); + int (*driverRead)(FlashChipBase *base, InstructionField *cmd, InstructionField *addr, InstructionField *dummy, InstructionField *data); + int (*xipDriverCfg)(FlashChipBase *base, FlashReadMode mode); + + /* + public interface + */ + int (*setFreq)(FlashChipBase *base, uint32_t freq); + int (*switchReadMode)(FlashChipBase *base, FlashReadMode mode); + int (*enableXIP)(FlashChipBase *base); + int (*disableXIP)(FlashChipBase *base); + int (*isBusy)(FlashChipBase *base); + int (*control)(FlashChipBase *base, int op, void *param); + FlashEraseMode (*minEraseSize)(FlashChipBase *base); +}; + +typedef enum DefaultFlashControlCmds +{ + DEFAULT_FLASH_SET_QPI_READ_P5P4, +} DefaultFlashControlCmds; + +/* + Default Flash Chip Interface +*/ +void defaultWriteEnable(FlashChipBase *base); + +void defaultWriteDisable(FlashChipBase *base); + +int defaultReadStatus(FlashChipBase *base, FlashStatus status, uint8_t *data); + +int defaultWriteStatus(FlashChipBase *base, FlashStatus reg, uint8_t *status); + +int defaultErase(FlashChipBase *base, FlashEraseMode mode, uint32_t addr); + +int defaultSuspendErasePageprogram(FlashChipBase *base); + +int defaultResumeErasePageprogram(FlashChipBase *base); + +int defaultPowerDown(FlashChipBase *base); + +int defaultReleasePowerDown(FlashChipBase *base); + +int defaultGetJedecID(FlashChipBase *base, uint32_t *data); + +int defaultEnableQPIMode(FlashChipBase *base); + +int defaultDisableQPIMode(FlashChipBase *base); + +int defaultEnableReset(FlashChipBase *base); + +int defaultReset(FlashChipBase *base); + +int defaultGetUniqueID(FlashChipBase *, uint8_t uid[8]); + +int defaultPageProgram(FlashChipBase *base, FlashPageProgramMode mode, uint32_t addr, uint8_t *data, uint32_t size); + +int defaultRead(FlashChipBase *base, FlashReadMode mode, uint32_t addr, uint8_t *data, uint32_t size); + +int defaultDriverWrite(FlashChipBase *base, InstructionField *cmd, InstructionField *addr, InstructionField *dummy, InstructionField *data); + +int defaultDriverRead(FlashChipBase *base, InstructionField *cmd, InstructionField *addr, InstructionField *dummy, InstructionField *data); + +int defaultSetFreq(FlashChipBase *base, uint32_t freq); + +int defaultSwitchReadMode(FlashChipBase *base, FlashReadMode mode); + +int defaultEnableXIP(FlashChipBase *base); + +int defaultDisableXIP(FlashChipBase *base); + +int defaultIsBusy(FlashChipBase *base); + +int defaultControl(FlashChipBase *base, int op, void *param); + +FlashEraseMode defaultGetMinEraseSize(FlashChipBase *base); + +int defaultXipDriverCfg(FlashChipBase *base, FlashReadMode mode); + + +typedef struct FlashChipCtor +{ + uint32_t mJedecId; + + FlashChipBase *(*create)(uint32_t arg); + int (*init)(FlashChipBase *); + int (*destory)(FlashChipBase *); +} FlashChipCtor; + +FlashChipBase *FlashChipCreate(FlashDrvierBase *driver); +int FlashChipDestroy(FlashChipBase *base); + + +/* + Flash Chip Debug +*/ +#define PCHECK(p) + +#endif /* FLASH_CHIP_H_ */ diff --git a/platform/mcu/xr871/include/driver/chip/hal_adc.h b/platform/mcu/xr871/include/driver/chip/hal_adc.h new file mode 100644 index 0000000000..813746981d --- /dev/null +++ b/platform/mcu/xr871/include/driver/chip/hal_adc.h @@ -0,0 +1,215 @@ +/** + * @file hal_adc.h + * @author XRADIO IOT WLAN Team + */ + +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _DRIVER_CHIP_HAL_ADC_H_ +#define _DRIVER_CHIP_HAL_ADC_H_ + +#include "driver/chip/hal_def.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief ADC channel definition + */ +typedef enum { + ADC_CHANNEL_0 = 0, + ADC_CHANNEL_1 = 1, + ADC_CHANNEL_2 = 2, + ADC_CHANNEL_3 = 3, + ADC_CHANNEL_4 = 4, + ADC_CHANNEL_5 = 5, + ADC_CHANNEL_6 = 6, + ADC_CHANNEL_7 = 7, + ADC_CHANNEL_8 = 8, + ADC_CHANNEL_NUM = 9 +} ADC_Channel; + +/** + * @brief ADC register block structure + */ +typedef struct { + __IO uint32_t SAMPLE_RATE; /* offset: 0x00, GPADC sample rate configure register */ + __IO uint32_t CTRL; /* offset: 0x04, GPADC control Register */ + __IO uint32_t CMP_SEL_EN; /* offset: 0x08, GPADC compare and select enable register */ + __IO uint32_t FIFO_CTRL; /* offset: 0x0C, GPADC FIFO interrupt control register */ + __IO uint32_t FIFO_STATUS; /* offset: 0x10, GPADC FIFO interrupt status register */ + __IO uint32_t FIFO_DATA; /* offset: 0x14, GPADC FIFO data register */ + __IO uint32_t CALIB_DATA; /* offset: 0x18, GPADC calibration data register */ + __IO uint32_t RESERVED0; + __IO uint32_t LOW_CONFIG; /* offset: 0x20, GPADC data low interrupt configure register */ + __IO uint32_t HIGH_CONFIG; /* offset: 0x24, GPADC data high interrupt configure register */ + __IO uint32_t DATA_CONFIG; /* offset: 0x28, GPADC data interrupt configure register */ + __IO uint32_t RESERVED1; + __IO uint32_t LOW_STATUS; /* offset: 0x30, GPADC data low interrupt status register */ + __IO uint32_t HIGH_STATUS; /* offset: 0x34, GPADC data high interrupt status register */ + __IO uint32_t DATA_STATUS; /* offset: 0x38, GPADC data interrupt status register */ + __IO uint32_t RESERVED2; + __IO uint32_t CMP_DATA[ADC_CHANNEL_NUM]; /* offset: 0x40, GPADC CH n compare data register */ + __IO uint32_t RESERVED3[7]; + __I uint32_t DATA[ADC_CHANNEL_NUM]; /* offset: 0x80, GPADC CH n data register */ +} ADC_T; + +#define ADC ((ADC_T *)GPADC_BASE) /* address: 0x40043000 */ + +/* ADC->SAMPLE_RATE */ +#define ADC_FS_DIV_SHIFT 16 +#define ADC_FS_DIV_MASK (0xFFFFU << ADC_FS_DIV_SHIFT) + +#define ADC_TACQ_SHIFT 0 +#define ADC_TACQ_MASK (0xFFFFU << ADC_TACQ_SHIFT) + +/* ADC->CTRL */ +#define ADC_FIRST_DELAY_SHIFT 24 +#define ADC_FIRST_DELAY_MASK (0xFFU << ADC_FIRST_DELAY_SHIFT) + +#define ADC_OP_BIAS_SHIFT 20 +#define ADC_OP_BIAS_MASK (0x3U << ADC_OP_BIAS_SHIFT) + +#define ADC_WORK_MODE_SHIFT 18 +#define ADC_WORK_MODE_MASK (0x3U << ADC_WORK_MODE_SHIFT) +typedef enum { + ADC_SINGLE_CONV = 0U, + ADC_SINGLE_CYCLE = 1U, + ADC_CONTI_CONV = 2U, + ADC_BURST_CONV = 3U +} ADC_WorkMode; + +#define ADC_CALIB_EN_BIT HAL_BIT(17) +#define ADC_EN_BIT HAL_BIT(16) +#define ADC_VBAT_EN_BIT HAL_BIT(4) +#define ADC_LDO_EN_BIT HAL_BIT(0) + +/* ADC->CMP_SEL_EN */ +#define ADC_CMP_EN_SHIFT 16 +#define ADC_CMP_EN_MASK (0x1FFU << ADC_CMP_EN_SHIFT) + +#define ADC_SEL_EN_SHIFT 0 +#define ADC_SEL_EN_MASK (0x1FFU << ADC_SEL_EN_SHIFT) + +/* + * ADC->LOW_CONFIG + * ADC->HIGH_CONFIG + * ADC->DATA_CONFIG + */ +#define ADC_LOW_IRQ_MASK 0x1FFU +#define ADC_HIGH_IRQ_MASK 0x1FFU +#define ADC_DATA_IRQ_MASK 0x1FFU + +/* + * ADC->LOW_STATUS + * ADC->HIGH_STATUS + * ADC->DATA_STATUS + */ +#define ADC_LOW_PENDING_MASK 0x1FFU +#define ADC_HIGH_PENDING_MASK 0x1FFU +#define ADC_DATA_PENDING_MASK 0x1FFU + +/* ADC->CMP_DATA */ +#define ADC_HIGH_DATA_SHIFT 16 +#define ADC_HIGH_DATA_MASK (0xFFFU << ADC_HIGH_DATA_SHIFT) + +#define ADC_LOW_DATA_SHIFT 0 +#define ADC_LOW_DATA_MASK (0xFFFU << ADC_LOW_DATA_SHIFT) + +/* ADC->DATA */ +#define ADC_DATA_MASK 0xFFFU + +/******************************************************************************/ + +/** + * @brief ADC channel initialization parameters + */ +typedef struct { + uint32_t freq; /* ADC sample frequency */ + uint8_t delay; /* the number of delayed samples in first conversion */ +} ADC_InitParam; + +/** + * @brief ADC channel selected state definition + */ +typedef enum { + ADC_SELECT_DISABLE = 0, + ADC_SELECT_ENABLE = 1 +} ADC_Select; + +/** + * @brief ADC interrupt mode definition + */ +typedef enum { + ADC_IRQ_NONE = 0, + ADC_IRQ_DATA = 1, + ADC_IRQ_LOW = 2, + ADC_IRQ_HIGH = 3, + ADC_IRQ_LOW_DATA = 4, + ADC_IRQ_HIGH_DATA = 5, + ADC_IRQ_LOW_HIGH = 6, + ADC_IRQ_LOW_HIGH_DATA = 7 +} ADC_IRQMode; + +/** + * @brief ADC interrupt state definition + */ +typedef enum { + ADC_NO_IRQ = 0, + ADC_DATA_IRQ = 1, + ADC_LOW_IRQ = 2, + ADC_HIGH_IRQ = 3, + ADC_LOW_DATA_IRQ = 4, + ADC_HIGH_DATA_IRQ = 5 +} ADC_IRQState; + +/** @brief Type define of ADC interrupt callback function */ +typedef void (*ADC_IRQCallback)(void *arg); + +HAL_Status HAL_ADC_Init(ADC_InitParam *initParam); +HAL_Status HAL_ADC_DeInit(void); + +HAL_Status HAL_ADC_Conv_Polling(ADC_Channel chan, uint32_t *data, uint32_t msec); + +HAL_Status HAL_ADC_EnableIRQCallback(ADC_Channel chan, ADC_IRQCallback cb, void *arg); +HAL_Status HAL_ADC_DisableIRQCallback(ADC_Channel chan); + +HAL_Status HAL_ADC_ConfigChannel(ADC_Channel chan, ADC_Select select, ADC_IRQMode mode, uint32_t lowValue, uint32_t highValue); +HAL_Status HAL_ADC_Start_Conv_IT(void); +HAL_Status HAL_ADC_Stop_Conv_IT(void); +ADC_IRQState HAL_ADC_GetIRQState(ADC_Channel chan); +uint32_t HAL_ADC_GetValue(ADC_Channel chan); + +#ifdef __cplusplus +} +#endif + +#endif /* _DRIVER_CHIP_HAL_ADC_H_ */ diff --git a/platform/mcu/xr871/include/driver/chip/hal_audio.h b/platform/mcu/xr871/include/driver/chip/hal_audio.h new file mode 100644 index 0000000000..b90a02ed31 --- /dev/null +++ b/platform/mcu/xr871/include/driver/chip/hal_audio.h @@ -0,0 +1,61 @@ +/** + * @file hal_audio.h + * @author XRADIO IOT WLAN Team + */ + +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef HAL_AUDIO_H_H +#define HAL_AUDIO_H_H + +typedef enum { + DAIFMT_CBM_CFM, /*(codec clk & FRM master) use((i2s slave))*/ + DAIFMT_CBS_CFM, /*(codec clk slave & FRM master) not use*/ + DAIFMT_CBM_CFS, /*(codec clk master & frame slave) not use*/ + DAIFMT_CBS_CFS, /*(codec clk & FRM slave) use(i2s master)*/ +} PCM_ClkMode; + +typedef enum { + DAIFMT_I2S, /*(standard i2s format). use*/ + DAIFMT_RIGHT_J, /*(right justfied format).*/ + DAIFMT_LEFT_J, /*(left justfied format) */ + DAIFMT_DSP_A, /*(pcm. MSB is available on 2nd BCLK rising edge after LRC rising edge). use*/ + DAIFMT_DSP_B, /*(pcm. MSB is available on 1nd BCLK rising edge after LRC rising*/ +} PCM_TranFmt; + +typedef enum { + DAIFMT_NB_NF, /*(normal bit clock + frame) use*/ + DAIFMT_NB_IF, /*(normal BCLK + inv FRM) */ + DAIFMT_IB_NF, /*(invert BCLK + nor FRM) use*/ + DAIFMT_IB_IF, /*(invert BCLK + FRM)*/ +} PCM_SignalInv; + + +#endif diff --git a/platform/mcu/xr871/include/driver/chip/hal_ccm.h b/platform/mcu/xr871/include/driver/chip/hal_ccm.h new file mode 100644 index 0000000000..382e86848a --- /dev/null +++ b/platform/mcu/xr871/include/driver/chip/hal_ccm.h @@ -0,0 +1,308 @@ +/** + * @file hal_board.h + * @author XRADIO IOT WLAN Team + */ + +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _DRIVER_CHIP_HAL_CCM_H_ +#define _DRIVER_CHIP_HAL_CCM_H_ + +#include "driver/chip/hal_def.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief CCM (Clock Control Module) register block structure + */ +typedef struct +{ + __IO uint32_t CPU_BUS_CLKCFG; /* offset: 0x00, CPU bus clock configure register */ + __IO uint32_t BUS_PERIPH_CLK_CTRL; /* offset: 0x04, Bus device clock gating control Register */ + __IO uint32_t BUS_PERIPH_RST_CTRL; /* offset: 0x08, Bus device reset control register */ + uint32_t RESERVED0[5]; + __IO uint32_t SPI0_MCLK_CTRL; /* offset: 0x20, SPI0 clock control register */ + __IO uint32_t SPI1_MCLK_CTRL; /* offset: 0x24, SPI1 clock control register */ + __IO uint32_t SDC_MCLK_CTRL; /* offset: 0x28, SDC clock control register */ + __IO uint32_t CE_MCLK_CTRL; /* offset: 0x2C, CE clock control register */ + __IO uint32_t CSI_OCLK_CTRL; /* offset: 0x30, CSI output clock control register */ + __IO uint32_t DAUDIO_MCLK_CTRL; /* offset: 0x34, Digital audio clock control register */ + __IO uint32_t IRRX_MCLK_CTRL; /* offset: 0x38, IRRX clock control register */ + __IO uint32_t IRTX_MCLK_CTRL; /* offset: 0x3C, IRTX clock control register */ + __IO uint32_t SYSTICK_REFCLK_CTRL; /* offset: 0x40, System tick reference clock register */ + __IO uint32_t SYSTICK_CALIB_CTRL; /* offset: 0x44, System tick clock calibration register */ + __IO uint32_t DMIC_MCLK_CTRL; /* offset: 0x48, DMIC clock control register */ + __IO uint32_t GPADC_MCLK_CTRL; /* offset: 0x4C, GPADC clock control register */ + __IO uint32_t CSI_MCLK_CTRL; /* offset: 0x50, CSI module clock control register */ + __IO uint32_t FLASHC_MCLK_CTRL; /* offset: 0x54, Flash controller clock control register */ +} CCM_T; + +#define CCM ((CCM_T *)CCM_BASE) /* address: 0x40040400 */ + +/* + * Bit field definition of CCM->CPU_BUS_CLKCFG + */ +#define CCM_AHB2_CLK_DIV_SHIFT 8 /* R/W */ +#define CCM_AHB2_CLK_DIV_MASK (0x3U << CCM_AHB2_CLK_DIV_SHIFT) +typedef enum { + CCM_AHB2_CLK_DIV_1 = (0x0U << CCM_AHB2_CLK_DIV_SHIFT), + CCM_AHB2_CLK_DIV_2 = (0x1U << CCM_AHB2_CLK_DIV_SHIFT), + CCM_AHB2_CLK_DIV_3 = (0x2U << CCM_AHB2_CLK_DIV_SHIFT), + CCM_AHB2_CLK_DIV_4 = (0x3U << CCM_AHB2_CLK_DIV_SHIFT) +} CCM_AHB2ClkDiv; + +#define CCM_APB_CLK_SRC_SHIFT 4 /* R/W */ +#define CCM_APB_CLK_SRC_MASK (0x3U << CCM_APB_CLK_SRC_SHIFT) +typedef enum { + CCM_APB_CLK_SRC_HFCLK = (0x0U << CCM_APB_CLK_SRC_SHIFT), + CCM_APB_CLK_SRC_LFCLK = (0x1U << CCM_APB_CLK_SRC_SHIFT), + CCM_APB_CLK_SRC_AHB2CLK = (0x2U << CCM_APB_CLK_SRC_SHIFT) +} CCM_APBClkSrc; + +#define CCM_APB_CLK_DIV_SHIFT 0 /* R/W */ +#define CCM_APB_CLK_DIV_MASK (0x3U << CCM_APB_CLK_DIV_SHIFT) +typedef enum { + CCM_APB_CLK_DIV_1 = (0x0U << CCM_APB_CLK_DIV_SHIFT), + CCM_APB_CLK_DIV_2 = (0x1U << CCM_APB_CLK_DIV_SHIFT), + CCM_APB_CLK_DIV_4 = (0x2U << CCM_APB_CLK_DIV_SHIFT), + CCM_APB_CLK_DIV_8 = (0x3U << CCM_APB_CLK_DIV_SHIFT) +} CCM_APBClkDiv; + +/* + * Bit field definition of + * - CCM->BUS_PERIPH_CLK_CTRL + * - CCM->BUS_PERIPH_RST_CTRL + */ +typedef enum { + CCM_BUS_PERIPH_BIT_GPIO = HAL_BIT(27), /* R/W, only valid for clock, not for reset */ + CCM_BUS_PERIPH_BIT_DMIC = HAL_BIT(26), /* R/W */ + CCM_BUS_PERIPH_BIT_GPADC = HAL_BIT(25), /* R/W */ + CCM_BUS_PERIPH_BIT_IRRX = HAL_BIT(24), /* R/W */ + CCM_BUS_PERIPH_BIT_IRTX = HAL_BIT(23), /* R/W */ + CCM_BUS_PERIPH_BIT_DAUDIO = HAL_BIT(22), /* R/W */ + CCM_BUS_PERIPH_BIT_PWM = HAL_BIT(21), /* R/W */ + CCM_BUS_PERIPH_BIT_I2C1 = HAL_BIT(19), /* R/W */ + CCM_BUS_PERIPH_BIT_I2C0 = HAL_BIT(18), /* R/W */ + CCM_BUS_PERIPH_BIT_UART1 = HAL_BIT(17), /* R/W */ + CCM_BUS_PERIPH_BIT_UART0 = HAL_BIT(16), /* R/W */ + CCM_BUS_PERIPH_BIT_MSGBOX = HAL_BIT(8), /* R/W */ + CCM_BUS_PERIPH_BIT_SPINLOCK = HAL_BIT(7), /* R/W */ + CCM_BUS_PERIPH_BIT_DMA = HAL_BIT(6), /* R/W */ + CCM_BUS_PERIPH_BIT_CSI = HAL_BIT(5), /* R/W */ + CCM_BUS_PERIPH_BIT_SDC0 = HAL_BIT(4), /* R/W */ + CCM_BUS_PERIPH_BIT_FLASHC = HAL_BIT(3), /* R/W */ + CCM_BUS_PERIPH_BIT_CE = HAL_BIT(2), /* R/W */ + CCM_BUS_PERIPH_BIT_SPI1 = HAL_BIT(1), /* R/W */ + CCM_BUS_PERIPH_BIT_SPI0 = HAL_BIT(0) /* R/W */ +} CCM_BusPeriphBit; + +/* + * Bit field definition of CLK enable + * - for SPI0, SP1, SDC, CE, CSI, DAUDIO, IRRX, IRTX, SYSTICK, DMIC, GPADC, CSI_OUT + */ +#define CCM_PERIPH_CLK_EN_BIT HAL_BIT(31) /* R/W */ + +/* + * Bit field definition of CLK source + * - AHB peripheral: SPI0, SP1, SDC, CE, CSI, CSI_OUT + * - APB peripheral: IRRX, IRTX, SYSTICK, GPADC + */ +#define CCM_PERIPH_CLK_SRC_SHIFT 24 /* R/W */ +#define CCM_PERIPH_CLK_SRC_MASK (0x3U << CCM_PERIPH_CLK_SRC_SHIFT) + +typedef enum { + CCM_AHB_PERIPH_CLK_SRC_HFCLK = (0x0U << CCM_PERIPH_CLK_SRC_SHIFT), + CCM_AHB_PERIPH_CLK_SRC_DEVCLK = (0x1U << CCM_PERIPH_CLK_SRC_SHIFT), +} CCM_AHBPeriphClkSrc; + +typedef enum { + CCM_APB_PERIPH_CLK_SRC_HFCLK = (0x0U << CCM_PERIPH_CLK_SRC_SHIFT), + CCM_APB_PERIPH_CLK_SRC_LFCLK = (0x1U << CCM_PERIPH_CLK_SRC_SHIFT) +} CCM_APBPeriphClkSrc; + +/* + * Bit field definition of CLK divider N, M + * - for SPI0, SP1, SDC, CE, CSI, IRRX, IRTX, SYSTICK, GPADC, CSI_OUT + */ +#define CCM_PERIPH_CLK_DIV_N_SHIFT 16 /* R/W */ +#define CCM_PERIPH_CLK_DIV_N_MASK (0x3U << CCM_PERIPH_CLK_DIV_N_SHIFT) +typedef enum { + CCM_PERIPH_CLK_DIV_N_1 = (0U << CCM_PERIPH_CLK_DIV_N_SHIFT), + CCM_PERIPH_CLK_DIV_N_2 = (1U << CCM_PERIPH_CLK_DIV_N_SHIFT), + CCM_PERIPH_CLK_DIV_N_4 = (2U << CCM_PERIPH_CLK_DIV_N_SHIFT), + CCM_PERIPH_CLK_DIV_N_8 = (3U << CCM_PERIPH_CLK_DIV_N_SHIFT) +} CCM_PeriphClkDivN; + +#define CCM_PERIPH_CLK_DIV_M_SHIFT 0 /* R/W */ +#define CCM_PERIPH_CLK_DIV_M_MASK (0xFU << CCM_PERIPH_CLK_DIV_M_SHIFT) +typedef enum { + CCM_PERIPH_CLK_DIV_M_1 = (0U << CCM_PERIPH_CLK_DIV_M_SHIFT), + CCM_PERIPH_CLK_DIV_M_2 = (1U << CCM_PERIPH_CLK_DIV_M_SHIFT), + CCM_PERIPH_CLK_DIV_M_3 = (2U << CCM_PERIPH_CLK_DIV_M_SHIFT), + CCM_PERIPH_CLK_DIV_M_4 = (3U << CCM_PERIPH_CLK_DIV_M_SHIFT), + CCM_PERIPH_CLK_DIV_M_5 = (4U << CCM_PERIPH_CLK_DIV_M_SHIFT), + CCM_PERIPH_CLK_DIV_M_6 = (5U << CCM_PERIPH_CLK_DIV_M_SHIFT), + CCM_PERIPH_CLK_DIV_M_7 = (6U << CCM_PERIPH_CLK_DIV_M_SHIFT), + CCM_PERIPH_CLK_DIV_M_8 = (7U << CCM_PERIPH_CLK_DIV_M_SHIFT), + CCM_PERIPH_CLK_DIV_M_9 = (8U << CCM_PERIPH_CLK_DIV_M_SHIFT), + CCM_PERIPH_CLK_DIV_M_10 = (9U << CCM_PERIPH_CLK_DIV_M_SHIFT), + CCM_PERIPH_CLK_DIV_M_11 = (10U << CCM_PERIPH_CLK_DIV_M_SHIFT), + CCM_PERIPH_CLK_DIV_M_12 = (11U << CCM_PERIPH_CLK_DIV_M_SHIFT), + CCM_PERIPH_CLK_DIV_M_13 = (12U << CCM_PERIPH_CLK_DIV_M_SHIFT), + CCM_PERIPH_CLK_DIV_M_14 = (13U << CCM_PERIPH_CLK_DIV_M_SHIFT), + CCM_PERIPH_CLK_DIV_M_15 = (14U << CCM_PERIPH_CLK_DIV_M_SHIFT), + CCM_PERIPH_CLK_DIV_M_16 = (15U << CCM_PERIPH_CLK_DIV_M_SHIFT) +} CCM_PeriphClkDivM; + +#define CCM_PERIPH_CLK_PARAM_MASK (CCM_PERIPH_CLK_SRC_MASK | \ + CCM_PERIPH_CLK_DIV_N_MASK | \ + CCM_PERIPH_CLK_DIV_M_MASK) + +/* CCM->SPI0_MCLK_CTRL */ + +/* CCM->SPI1_MCLK_CTRL */ + +/* CCM->SDC_MCLK_CTRL */ + +/* CCM->CE_MCLK_CTRL */ + +/* CCM->CSI_OCLK_CTRL */ + +/* + * Bit field definition of CCM->DAUDIO_MCLK_CTRL + */ +#define CCM_DAUDIO_MCLK_SRC_SHIFT 0 /* R/W */ +#define CCM_DAUDIO_MCLK_SRC_MASK (0x3U << CCM_DAUDIO_MCLK_SRC_SHIFT) +typedef enum { + CCM_DAUDIO_MCLK_SRC_8X = (0x0U << CCM_DAUDIO_MCLK_SRC_SHIFT), + CCM_DAUDIO_MCLK_SRC_4X = (0x1U << CCM_DAUDIO_MCLK_SRC_SHIFT), + CCM_DAUDIO_MCLK_SRC_2X = (0x2U << CCM_DAUDIO_MCLK_SRC_SHIFT), + CCM_DAUDIO_MCLK_SRC_1X = (0x3U << CCM_DAUDIO_MCLK_SRC_SHIFT) +} CCM_DAudioClkSrc; + +/* CCM->IRRX_MCLK_CTRL */ + +/* CCM->IRTX_MCLK_CTRL */ + +/* CCM->SYSTICK_REFCLK_CTRL */ + +/* + * Bit field definition of CCM->SYSTICK_CALIB_CTRL + */ +#define CCM_SYSTICK_NOREF_BIT HAL_BIT(25) /* R/W */ +#define CCM_SYSTICK_SKEW_BIT HAL_BIT(24) /* R/W */ +#define CCM_SYSTICK_TENMS_SHIFT 0 /* R/W */ +#define CCM_SYSTICK_TENMS_MASK (0xFFFFFFU << CCM_SYSTICK_TENMS_SHIFT) + +/* CCM->DMIC_MCLK_CTRL */ + +/* CCM->DMIC_MCLK_CTRL */ + +/* CCM->CSI_MCLK_CTRL */ + +/* CCM->FLASHC_MCLK_CTRL */ + +/******************************************************************************/ + +void HAL_CCM_BusSetClock(CCM_AHB2ClkDiv AHB2Div, CCM_APBClkSrc APBSrc, CCM_APBClkDiv APBDiv); +uint32_t HAL_CCM_BusGetAHB1Clock(void); +uint32_t HAL_CCM_BusGetAHB2Clock(void); +uint32_t HAL_CCM_BusGetAPBClock(void); + +void HAL_CCM_BusEnablePeriphClock(uint32_t periphMask); +void HAL_CCM_BusDisablePeriphClock(uint32_t periphMask); +void HAL_CCM_BusDisableAllPeriphClock(void); +void HAL_CCM_BusForcePeriphReset(uint32_t periphMask); +void HAL_CCM_BusReleasePeriphReset(uint32_t periphMask); +void HAL_CCM_BusForceAllPeriphReset(void); + +void HAL_CCM_SPI0_SetMClock(CCM_AHBPeriphClkSrc src, CCM_PeriphClkDivN divN, CCM_PeriphClkDivM divM); +void HAL_CCM_SPI0_EnableMClock(void); +void HAL_CCM_SPI0_DisableMClock(void); +void HAL_CCM_SPI1_SetMClock(CCM_AHBPeriphClkSrc src, CCM_PeriphClkDivN divN, CCM_PeriphClkDivM divM); +void HAL_CCM_SPI1_EnableMClock(void); +void HAL_CCM_SPI1_DisableMClock(void); + +void HAL_CCM_SDC_SetMClock(CCM_AHBPeriphClkSrc src, CCM_PeriphClkDivN divN, CCM_PeriphClkDivM divM); +void HAL_CCM_SDC_EnableMClock(void); +void HAL_CCM_SDC_DisableMClock(void); + +void HAL_CCM_CE_SetMClock(CCM_AHBPeriphClkSrc src, CCM_PeriphClkDivN divN, CCM_PeriphClkDivM divM); +void HAL_CCM_CE_EnableMClock(void); +void HAL_CCM_CE_DisableMClock(void); + +void HAL_CCM_CSI_SetOutClock(CCM_AHBPeriphClkSrc src, CCM_PeriphClkDivN divN, CCM_PeriphClkDivM divM); +void HAL_CCM_CSI_EnableOutClock(void); +void HAL_CCM_CSI_DisableOutClock(void); + +void HAL_CCM_DAUDIO_SetMClock(CCM_DAudioClkSrc src); +void HAL_CCM_DAUDIO_EnableMClock(void); +void HAL_CCM_DAUDIO_DisableMClock(void); + +void HAL_CCM_IRRX_SetMClock(CCM_APBPeriphClkSrc src, CCM_PeriphClkDivN divN, CCM_PeriphClkDivM divM); +void HAL_CCM_IRRX_EnableMClock(void); +void HAL_CCM_IRRX_DisableMClock(void); +void HAL_CCM_IRTX_SetMClock(CCM_APBPeriphClkSrc src, CCM_PeriphClkDivN divN, CCM_PeriphClkDivM divM); +void HAL_CCM_IRTX_EnableMClock(void); +void HAL_CCM_IRTX_DisableMClock(void); + +void HAL_CCM_SYSTICK_SetRefClock(CCM_APBPeriphClkSrc src, CCM_PeriphClkDivN divN, CCM_PeriphClkDivM divM); +void HAL_CCM_SYSTICK_EnableRefClock(void); +void HAL_CCM_SYSTICK_DisableRefClock(void); +void HAL_CCM_SYSTICK_EnableRef(void); +void HAL_CCM_SYSTICK_DisableRef(void); +void HAL_CCM_SYSTICK_EnableSkew(void); +void HAL_CCM_SYSTICK_DisableSkew(void); +void HAL_CCM_SYSTICK_SetTENMS(uint32_t cnt); + +void HAL_CCM_DMIC_EnableMClock(void); +void HAL_CCM_DMIC_DisableMClock(void); + +void HAL_CCM_GPADC_SetMClock(CCM_APBPeriphClkSrc src, CCM_PeriphClkDivN divN, CCM_PeriphClkDivM divM); +void HAL_CCM_GPADC_EnableMClock(void); +void HAL_CCM_GPADC_DisableMClock(void); + +void HAL_CCM_CSI_SetMClock(CCM_AHBPeriphClkSrc src, CCM_PeriphClkDivN divN, CCM_PeriphClkDivM divM); +void HAL_CCM_CSI_EnableMClock(void); +void HAL_CCM_CSI_DisableMClock(void); + +void HAL_CCM_FLASHC_SetMClock(CCM_AHBPeriphClkSrc src, CCM_PeriphClkDivN divN, CCM_PeriphClkDivM divM); +void HAL_CCM_FLASHC_EnableMClock(void); +void HAL_CCM_FLASHC_DisableMClock(void); + +void HAL_CCM_Init(void); + +#ifdef __cplusplus +} +#endif + +#endif /* _DRIVER_CHIP_HAL_CCM_H_ */ diff --git a/platform/mcu/xr871/include/driver/chip/hal_chip.h b/platform/mcu/xr871/include/driver/chip/hal_chip.h new file mode 100644 index 0000000000..b81da08368 --- /dev/null +++ b/platform/mcu/xr871/include/driver/chip/hal_chip.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _DRIVER_CHIP_HAL_CHIP_H_ +#define _DRIVER_CHIP_HAL_CHIP_H_ + +#include "driver/chip/hal_def.h" +#include "driver/chip/hal_nvic.h" +#include "driver/chip/hal_clock.h" +#include "driver/chip/hal_ccm.h" +#include "driver/chip/hal_prcm.h" +#include "driver/chip/hal_dma.h" +#include "driver/chip/hal_crypto.h" +#include "driver/chip/hal_efuse.h" +#include "driver/chip/hal_rtc.h" +#include "driver/chip/hal_timer.h" +#include "driver/chip/hal_wdg.h" +#include "driver/chip/hal_mbox.h" +#include "driver/chip/hal_gpio.h" +#include "driver/chip/hal_uart.h" +#include "driver/chip/hal_i2c.h" +#include "driver/chip/hal_adc.h" +#include "driver/chip/hal_spi.h" +#include "driver/chip/hal_pwm.h" +#include "driver/chip/hal_i2s.h" +#include "driver/chip/hal_dmic.h" +#include "driver/chip/hal_csi.h" +#include "driver/chip/hal_irtx.h" +#include "driver/chip/hal_irrx.h" +#include "driver/chip/sdmmc/hal_sdhost.h" +#include "driver/chip/hal_flash.h" + +#endif /* _DRIVER_CHIP_HAL_CHIP_H_ */ diff --git a/platform/mcu/xr871/include/driver/chip/hal_clock.h b/platform/mcu/xr871/include/driver/chip/hal_clock.h new file mode 100644 index 0000000000..3751a4a3c7 --- /dev/null +++ b/platform/mcu/xr871/include/driver/chip/hal_clock.h @@ -0,0 +1,112 @@ +/** + * @file hal_clock.h + * @author XRADIO IOT WLAN Team + */ + +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _DRIVER_CHIP_HAL_CLOCK_H_ +#define _DRIVER_CHIP_HAL_CLOCK_H_ + +#include "driver/chip/hal_prcm.h" +#include "driver/chip/hal_ccm.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Get HF clock, which is the value of external high frequency oscillator + * @return HF clock in Hz + */ +__STATIC_INLINE uint32_t HAL_GetHFClock(void) +{ + return HAL_PRCM_GetHFClock(); +} + +/** + * @brief Get LF clock, which is the value of low frequence oscillator + * @return LF clock in Hz + */ +__STATIC_INLINE uint32_t HAL_GetLFClock(void) +{ + return SYS_LFCLOCK; +} + +/** + * @brief Get CPU clock + * @return CPU clock in Hz + */ +__STATIC_INLINE uint32_t HAL_GetCPUClock(void) +{ + return HAL_PRCM_GetCPUAClk(); +} + +/** + * @brief Get Device clock + * @return Device clock in Hz + */ +__STATIC_INLINE uint32_t HAL_GetDevClock(void) +{ + return HAL_PRCM_GetDevClock(); +} + +/** + * @brief Get AHB1 clock + * @return AHB1 clock in Hz + */ +__STATIC_INLINE uint32_t HAL_GetAHB1Clock(void) +{ + return HAL_CCM_BusGetAHB1Clock(); +} + +/** + * @brief Get AHB2 clock + * @return AHB2 clock in Hz + */ +__STATIC_INLINE uint32_t HAL_GetAHB2Clock(void) +{ + return HAL_CCM_BusGetAHB2Clock(); +} + +/** + * @brief Get APB clock + * @return APB clock in Hz + */ +__STATIC_INLINE uint32_t HAL_GetAPBClock(void) +{ + return HAL_CCM_BusGetAPBClock(); +} + +#ifdef __cplusplus +} +#endif + +#endif /* _DRIVER_CHIP_HAL_CLOCK_H_ */ diff --git a/platform/mcu/xr871/include/driver/chip/hal_cmsis.h b/platform/mcu/xr871/include/driver/chip/hal_cmsis.h new file mode 100644 index 0000000000..5719254c94 --- /dev/null +++ b/platform/mcu/xr871/include/driver/chip/hal_cmsis.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _DRIVER_CHIP_HAL_CMSIS_H_ +#define _DRIVER_CHIP_HAL_CMSIS_H_ + +#include "driver/chip/chip.h" + +#endif /* _DRIVER_CHIP_HAL_CMSIS_H_ */ diff --git a/platform/mcu/xr871/include/driver/chip/hal_codec.h b/platform/mcu/xr871/include/driver/chip/hal_codec.h new file mode 100644 index 0000000000..d9ae91d3b1 --- /dev/null +++ b/platform/mcu/xr871/include/driver/chip/hal_codec.h @@ -0,0 +1,172 @@ +/** + * @file hal_codec.h + * @author XRADIO IOT WLAN Team + */ + +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _DRIVER_CHIP_HAL_CODEC_H_ +#define _DRIVER_CHIP_HAL_CODEC_H_ + +#include +#include "driver/chip/hal_gpio.h" +#include "driver/chip/hal_i2c.h" +#include "driver/chip/hal_audio.h" + +#ifdef __cplusplus + extern "C" { +#endif + +/** + * @brief The param for PA control. + */ +typedef struct { + GPIO_Port ctrl_port; + GPIO_Pin ctrl_pin; + GPIO_PinState ctrl_on_state; + GPIO_PinState ctrl_off_state; +} SPK_Param; + +/** + * @brief Audio device structures definition. + */ +typedef enum { + AUDIO_DEVICE_HEADPHONE = 1, /*!< Headphone */ + AUDIO_DEVICE_SPEAKER, /*!< Speaker */ + AUDIO_DEVICE_HEADPHONEMIC, /*!< Headphone Mic */ + AUDIO_DEVICE_MAINMIC, /*!< Main Mic */ + AUDIO_DEVICE_NONE /*!< none */ +} AUDIO_Device; + +/** + * @brief Audio volume gain structures definition. + */ +typedef enum { + VOLUME_LEVEL0, + VOLUME_LEVEL1, + VOLUME_LEVEL2, + VOLUME_LEVEL3, + VOLUME_LEVEL4, + VOLUME_LEVEL5, + VOLUME_LEVEL6, + VOLUME_LEVEL7, + VOLUME_LEVEL8, + VOLUME_LEVEL9, + VOLUME_LEVEL10, + VOLUME_LEVEL11, + VOLUME_LEVEL12, + VOLUME_LEVEL13, + VOLUME_LEVEL14, + VOLUME_LEVEL15, + VOLUME_LEVEL16, + VOLUME_LEVEL17, + VOLUME_LEVEL18, + VOLUME_LEVEL19, + VOLUME_LEVEL20, + VOLUME_LEVEL21, + VOLUME_LEVEL22, + VOLUME_LEVEL23, + VOLUME_LEVEL24, + VOLUME_LEVEL25, + VOLUME_LEVEL26, + VOLUME_LEVEL27, + VOLUME_LEVEL28, + VOLUME_LEVEL29, + VOLUME_LEVEL30, + VOLUME_LEVEL31, + VOLUME_MAX_LEVEL = VOLUME_LEVEL31, +} Vollevel; + +/** + * @brief Pcm communication parameters + */ +typedef struct { + PCM_ClkMode clkMode; /*!< Specifies the pcm clk mode */ + PCM_TranFmt transferFormat; /*!< Specifies the pcm operating mode */ + PCM_SignalInv signalInterval; /*!< Specifies the pcm Clock Polarity */ + uint32_t slotWidth; /*!< Specifies the pcm slot width */ + uint32_t wordWidth; /*!< Specifies the data format for the communication */ + uint32_t freqIn; /*!< Specifies the external input clock frequency */ + uint32_t freqOut; /*!< Specifies the clock the module work properly */ + uint32_t pllId; +} DAI_FmtParam; + +/** + * @brief Codec Gain parameters + */ +typedef struct { + uint8_t speaker_double_used; /*!< Flag of speaker case (double or single) */ + uint8_t double_speaker_val; /*!< Volume gain of double speaker */ + uint8_t single_speaker_val; /*!< Volume gain of single speaker */ + uint8_t headset_val; /*!< Volume gain of headset */ + uint8_t mainmic_val; /*!< Volume gain of main mic */ + uint8_t headsetmic_val; /*!< Volume gain of headset mic */ +} CODEC_InitParam; + +/** + * @brief Data format Init parameters + */ +typedef struct { + uint32_t sampleRate; /*!< Sample rate for the stream data */ + AUDIO_Device audioDev; /*!< Audio device to play or capture */ + DAI_FmtParam *fmtParam; /*!< Parameters for Pcm transfer initialization */ +} DATA_Param; + +typedef int32_t (*hw_write)(I2C_ID i2cId, uint16_t devAddr, uint8_t memAddr, uint8_t *buf, int32_t size); +typedef int32_t (*hw_read)(I2C_ID i2cId, uint16_t devAddr, uint8_t memAddr, uint8_t *buf, int32_t size); + +/** + * @brief CODEC Param Init structure definition + */ +typedef struct { + uint8_t *name; /*!< Name of specific codec */ + hw_write write; /*!< I2C write function */ + hw_read read; /*!< I2C read function */ + CODEC_InitParam *param; /*!< Parameters for codec gain initialization */ + uint8_t i2cId; /*!< Index of I2C for control codec */ +} CODEC_Param; + +HAL_Status HAL_CODEC_DeInit(); +HAL_Status HAL_CODEC_Init(const CODEC_Param *param); +HAL_Status HAL_CODEC_Close(uint32_t dir); +HAL_Status HAL_CODEC_Open(DATA_Param *param); +HAL_Status HAL_CODEC_VOLUME_LEVEL_Set(AUDIO_Device dev,int volume); +HAL_Status HAL_CODEC_ROUTE_Set(AUDIO_Device dev); +HAL_Status HAL_CODEC_Mute(AUDIO_Device dev, uint32_t mute); +HAL_Status HAL_CODEC_Trigger(AUDIO_Device dev, uint32_t on); +uint32_t HAL_CODEC_MUTE_STATUS_Get(); +HAL_Status HAL_CODEC_MUTE_STATUS_Init(int status); +HAL_Status HAL_CODEC_INIT_VOLUME_Set(AUDIO_Device dev,int volume); + +#ifdef __cplusplus +} +#endif + +#endif /* _DRIVER_CHIP_HAL_CODEC_H_ */ diff --git a/platform/mcu/xr871/include/driver/chip/hal_crypto.h b/platform/mcu/xr871/include/driver/chip/hal_crypto.h new file mode 100644 index 0000000000..274c1d5017 --- /dev/null +++ b/platform/mcu/xr871/include/driver/chip/hal_crypto.h @@ -0,0 +1,414 @@ +/** + * @file hal_crypto.h + * @author XRADIO IOT WLAN Team + */ + +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _DRIVER_CHIP_HAL_CRYPTO_H_ +#define _DRIVER_CHIP_HAL_CRYPTO_H_ + +#include +#include "driver/chip/hal_def.h" +#include "driver/chip/hal_dma.h" +#include "sys/xr_debug.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +typedef struct +{ + __IO uint32_t CTL; /*!< CRYP Control Register, Address offset: 0x00 */ +// __IO uint32_t KEY0; /*!< CRYP Input Key 0/PRNG Seed 0 Register, Address offset: 0x04 */ +// __IO uint32_t KEY1; /*!< CRYP Input Key 1/PRNG Seed 1 Register, Address offset: 0x08 */ +// __IO uint32_t KEY2; /*!< CRYP Input Key 2/PRNG Seed 2 Register, Address offset: 0x0C */ +// __IO uint32_t KEY3; /*!< CRYP Input Key 3/PRNG Seed 3 Register, Address offset: 0x10 */ +// __IO uint32_t KEY4; /*!< CRYP Input Key 4/PRNG Seed 4 Register, Address offset: 0x14 */ +// __IO uint32_t KEY5; /*!< CRYP Input Key 5/PRNG Seed 5 Register, Address offset: 0x18 */ +// __IO uint32_t KEY6; /*!< CRYP Input Key 6/PRNG Seed 6 Register, Address offset: 0x1C */ +// __IO uint32_t KEY7; /*!< CRYP Input Key 7/PRNG Seed 7 Register, Address offset: 0x20 */ + __IO uint32_t KEY[8]; +// __IO uint32_t IV0; /*!< CRYP Initialization Vector 0 Register, Address offset: 0x24 */ +// __IO uint32_t IV1; /*!< CRYP Initialization Vector 1 Register, Address offset: 0x28 */ +// __IO uint32_t IV2; /*!< CRYP Initialization Vector 2 Register, Address offset: 0x2C */ +// __IO uint32_t IV3; /*!< CRYP Initialization Vector 3 Register, Address offset: 0x30 */ + __IO uint32_t IV[4]; +// __IO uint32_t CNT0; /*!< CRYP Counter 0 Register, Address offset: 0x34 */ +// __IO uint32_t CNT1; /*!< CRYP Counter 1 Register, Address offset: 0x38 */ +// __IO uint32_t CNT2; /*!< CRYP Counter 2 Register, Address offset: 0x3C */ +// __IO uint32_t CNT3; /*!< CRYP Counter 3 Register, Address offset: 0x40 */ + __IO uint32_t CNT[4]; + __IO uint32_t FCSR; /*!< CRYP FIFO Control/Status Register, Address offset: 0x44 */ + __IO uint32_t ICSR; /*!< CRYP Interrupt Control/Status Register, Address offset: 0x48 */ + __I uint32_t MD0; /*!< CRYP Message Digest 0/PRNG Data 0 Register, Address offset: 0x4C */ + __I uint32_t MD1; /*!< CRYP Message Digest 1/PRNG Data 1 Register, Address offset: 0x50 */ + __I uint32_t MD2; /*!< CRYP Message Digest 2/PRNG Data 2 Register, Address offset: 0x54 */ + __I uint32_t MD3; /*!< CRYP Message Digest 3/PRNG Data 3 Register, Address offset: 0x58 */ + __I uint32_t MD4; /*!< CRYP Message Digest 4/PRNG Data 4 Register, Address offset: 0x5C */ + __IO uint32_t CTS_LEN; /*!< CRYP AES-CTS txt Length Register, Address offset: 0x60 */ + uint32_t RESERVED1[3]; /*!< Reserved, 0x64-0x6C */ + __IO uint32_t CRC_POLY; /*!< CRYP CRC Poly Register, Address offset: 0x70 */ + __IO uint32_t CRC_RESULT; /*!< CRYP CRC Result Register, Address offset: 0x74 */ + uint32_t RESERVED2[10]; /*!< Reserved, 0x78-0x9C */ + __I uint32_t MD5; /*!< CRYP SHA256 Message Digest Data 5 Register, Address offset: 0xA0 */ + __I uint32_t MD6; /*!< CRYP SHA256 Message Digest Data 6 Register, Address offset: 0xA4 */ + __I uint32_t MD7; /*!< CRYP SHA256 Message Digest Data 7 Register, Address offset: 0xA8 */ + uint32_t RESERVED3[85]; /*!< Reserved, 0xAC-0x1FC */ + __O uint32_t RXFIFO; /*!< CRYP RX FIFO input Register, Address offset: 0x200 */ + __I uint32_t TXFIFO; /*!< CRYP TX FIFO output Register, Address offset: 0x204 */ + +} CE_T; + +#define CE ((CE_T *)CE_BASE) + +/* + * @brief SPI Global Control Register + */ +#define CE_CTL_CRC_XOR_OUT_SHIFT (31) +#define CE_CTL_CRC_XOR_OUT_MASK (0x1U << CE_CTL_CRC_XOR_OUT_SHIFT) + +#define CE_CTL_CRC_REF_OUT_SHIFT (30) +#define CE_CTL_CRC_REF_OUT_MASK (0x1U << CE_CTL_CRC_REF_OUT_SHIFT) + +#define CE_CTL_CRC_REF_IN_SHIFT (29) +#define CE_CTL_CRC_REF_IN_MASK (0x1U << CE_CTL_CRC_REF_IN_SHIFT) + +#define CE_CTL_CRC_INIT_SHIFT (28) +#define CE_CTL_CRC_INIT_MASK (0x1U << CE_CTL_CRC_INIT_SHIFT) + +#define CE_CTL_KEY_SEL_SHIFT (24) +#define CE_CTL_KEY_SEL_MASK (0xFU << CE_CTL_KEY_SEL_SHIFT) +typedef enum { + CE_CTL_KEYSOURCE_INPUT = 0 << CE_CTL_KEY_SEL_SHIFT, /*!< key from input(normal mode) */ + CE_CTL_KEYSOURCE_SID = 1 << CE_CTL_KEY_SEL_SHIFT, /*!< key from SID */ + CE_CTL_KEYSOURCE_INTERNAL0 = 3 << CE_CTL_KEY_SEL_SHIFT, /*!< Not support for now */ + CE_CTL_KEYSOURCE_INTERNAL1 = 4 << CE_CTL_KEY_SEL_SHIFT, /*!< Not support for now */ + CE_CTL_KEYSOURCE_INTERNAL2 = 5 << CE_CTL_KEY_SEL_SHIFT, /*!< Not support for now */ + CE_CTL_KEYSOURCE_INTERNAL3 = 6 << CE_CTL_KEY_SEL_SHIFT, /*!< Not support for now */ + CE_CTL_KEYSOURCE_INTERNAL4 = 7 << CE_CTL_KEY_SEL_SHIFT, /*!< Not support for now */ + CE_CTL_KEYSOURCE_INTERNAL5 = 8 << CE_CTL_KEY_SEL_SHIFT, /*!< Not support for now */ + CE_CTL_KEYSOURCE_INTERNAL6 = 9 << CE_CTL_KEY_SEL_SHIFT, /*!< Not support for now */ + CE_CTL_KEYSOURCE_INTERNAL7 = 10 << CE_CTL_KEY_SEL_SHIFT /*!< Not support for now */ +} CE_CTL_KeySource; + +#define CE_CTL_PRNG_MODE_SHIFT (15) +#define CE_CTL_PRNG_MODE_MASK (0x1U << CE_CTL_PRNG_MODE_SHIFT) + +#define CE_CTL_CRC_CONT_SHIFT (15) +#define CE_CTL_CRC_CONT_MASK (0x1U << CE_CTL_CRC_CONT_SHIFT) + +#define CE_CTL_IV_MODE_SHIFT (14) +#define CE_CTL_IV_MODE_MASK (0x1U << CE_CTL_IV_MODE_SHIFT) +typedef enum { + CE_CTL_IVMODE_SHA_MD5_FIPS180 = 0 << CE_CTL_IV_MODE_SHIFT, /*!< IV in FIPS-180 defined */ + CE_CTL_IVMODE_SHA_MD5_INPUT = 1 << CE_CTL_IV_MODE_SHIFT /*!< IV from input */ +} CE_CTL_IvMode_SHA_MD5; + +#define CE_CTL_OP_MODE_SHIFT (12) +#define CE_CTL_OP_MODE_MASK (0x3U << CE_CTL_OP_MODE_SHIFT) +typedef enum { + CE_CTL_CRYPT_MODE_ECB = 0 << CE_CTL_OP_MODE_SHIFT, /*!< Crypto mode: ECB mode */ + CE_CTL_CRYPT_MODE_CBC = 1 << CE_CTL_OP_MODE_SHIFT, /*!< Crypto mode: CBC mode */ + CE_CTL_CRYPT_MODE_CTR = 2 << CE_CTL_OP_MODE_SHIFT, /*!< Crypto mode: CTR mode, only AES can support */ + CE_CTL_CRYPT_MODE_CTS = 3 << CE_CTL_OP_MODE_SHIFT /*!< Crypto mode: CTS mode, not support for now */ +} CE_CTL_Crypto_Mode; + +#define CE_CTL_CTR_WIDTH_SHIFT (10) +#define CE_CTL_CTR_WIDTH_MASK (0x3U << CE_CTL_CTR_WIDTH_SHIFT) +typedef enum { + CE_CTL_CTRWITDH_16BITS = 0 << CE_CTL_CTR_WIDTH_SHIFT, /*!< CTR mode counter size: 16 bits */ + CE_CTL_CTRWITDH_32BITS = 1 << CE_CTL_CTR_WIDTH_SHIFT, /*!< CTR mode counter size: 32 bits */ + CE_CTL_CTRWITDH_64BITS = 2 << CE_CTL_CTR_WIDTH_SHIFT, /*!< CTR mode counter size: 64 bits */ + CE_CTL_CTRWITDH_128BITS = 3 << CE_CTL_CTR_WIDTH_SHIFT /*!< CTR mode counter size: 128 bits */ +} CE_CTL_CtrWidth; + +#define CE_CTL_AES_KEY_SIZE_SHIFT (8) +#define CE_CTL_AES_KEY_SIZE_MASK (0x3U << CE_CTL_AES_KEY_SIZE_SHIFT) +typedef enum { + CE_CTL_AES_KEYSIZE_128BITS = 0 << CE_CTL_AES_KEY_SIZE_SHIFT, /*!< AES type: AES128 */ + CE_CTL_AES_KEYSIZE_192BITS = 1 << CE_CTL_AES_KEY_SIZE_SHIFT, /*!< AES type: AES192 */ + CE_CTL_AES_KEYSIZE_256BITS = 2 << CE_CTL_AES_KEY_SIZE_SHIFT /*!< AES type: AES256 */ +} CE_CTL_AES_KeySize; + +#define CE_CTL_OP_DIR_SHIFT (7) +#define CE_CTL_OP_DIR_MASK (0x1U << CE_CTL_OP_DIR_SHIFT) +typedef enum { + CE_CRYPT_OP_ENCRYPTION = 0 << CE_CTL_OP_DIR_SHIFT, /*!< Encryption operation */ + CE_CRYPT_OP_DECRYPTION = 1 << CE_CTL_OP_DIR_SHIFT /*!< Decryption operation */ +} CE_Crypto_Op; + +#define CE_CTL_METHOD_SHIFT (4) +#define CE_CTL_METHOD_MASK (0x7U << CE_CTL_METHOD_SHIFT) +typedef enum { + CE_CTL_METHOD_AES = 0 << CE_CTL_METHOD_SHIFT, /*!< AES */ + CE_CTL_METHOD_DES = 1 << CE_CTL_METHOD_SHIFT, /*!< DES */ + CE_CTL_METHOD_3DES = 2 << CE_CTL_METHOD_SHIFT, /*!< 3DES */ + CE_CTL_METHOD_SHA1 = 3 << CE_CTL_METHOD_SHIFT, /*!< SHA1 */ + CE_CTL_METHOD_MD5 = 4 << CE_CTL_METHOD_SHIFT, /*!< MD5 */ + CE_CTL_METHOD_PRNG = 5 << CE_CTL_METHOD_SHIFT, /*!< PRNG */ + CE_CTL_METHOD_SHA256 = 6 << CE_CTL_METHOD_SHIFT, /*!< SHA256 */ + CE_CTL_METHOD_CRC = 7 << CE_CTL_METHOD_SHIFT /*!< CRC */ +} CE_CTL_Method; + +#define CE_CTL_CRC_WIDTH_SHIFT (3) +#define CE_CTL_CRC_WIDTH_MASK (0x1U << CE_CTL_CRC_WIDTH_SHIFT) +typedef enum { + CE_CTL_CRC_WIDTH_16BITS = 0 << CE_CTL_CRC_WIDTH_SHIFT, /*!< CRC type: 16bits CRC */ + CE_CTL_CRC_WIDTH_32BITS = 1 << CE_CTL_CRC_WIDTH_SHIFT /*!< CRC type: 32bits CRC */ +} CE_CTL_CRC_Width; + +#define CE_CTL_END_BIT_SHIFT (2) +#define CE_CTL_END_BIT_MASK (0x1U << CE_CTL_END_BIT_SHIFT) + +#define CE_CTL_PRNG_START_SHIFT (1) +#define CE_CTL_PRNG_START_MASK (0x1U << CE_CTL_PRNG_START_SHIFT) + +#define CE_CTL_ENABLE_SHIFT (0) +#define CE_CTL_ENABLE_MASK (0x1U << CE_CTL_ENABLE_SHIFT) + + +#define CE_FCSR_RXFIFO_STATUS_SHIFT (30) +#define CE_FCSR_RXFIFO_STATUS_MASK (0x1U << CE_FCSR_RXFIFO_STATUS_SHIFT) + +#define CE_FCSR_RXFIFO_EMP_CNT_SHIFT (24) +#define CE_FCSR_RXFIFO_EMP_CNT_MASK (0x3FU << CE_FCSR_RXFIFO_EMP_CNT_SHIFT) +#define CE_FCSR_RXFIFO_EMP_CNT_VMASK (0x3FU) + +#define CE_FCSR_TXFIFO_STATUS_SHIFT (22) +#define CE_FCSR_TXFIFO_STATUS_MASK (0x1U << CE_FCSR_TXFIFO_STATUS_SHIFT) + +#define CE_FCSR_TXFIFO_AVA_CNT_SHIFT (16) +#define CE_FCSR_TXFIFO_AVA_CNT_MASK (0x3FU << CE_FCSR_TXFIFO_AVA_CNT_SHIFT) +#define CE_FCSR_TXFIFO_AVA_CNT_VMASK (0x3FU) + +#define CE_FCSR_RXFIFO_INT_TRIG_LEVEL_SHIFT (8) +#define CE_FCSR_RXFIFO_INT_TRIG_LEVEL_MASK (0x1FU << CE_FCSR_RXFIFO_INT_TRIG_LEVEL_SHIFT) + +#define CE_FCSR_TXFIFO_INT_TRIG_LEVEL_SHIFT (0) +#define CE_FCSR_TXFIFO_INT_TRIG_LEVEL_MASK (0x1FU << CE_FCSR_TXFIFO_INT_TRIG_LEVEL_SHIFT) + + +#define CE_ICSR_HASH_CRC_END_SHIFT (11) +#define CE_ICSR_HASH_CRC_END_MASK (0x1U << CE_ICSR_HASH_CRC_END_SHIFT) + +#define CE_ICSR_RXFIFO_EMP_SHIFT (10) +#define CE_ICSR_RXFIFO_EMP_MASK (0x1U << CE_ICSR_RXFIFO_EMP_SHIFT) + +#define CE_ICSR_TXFIFO_AVA_SHIFT (8) +#define CE_ICSR_TXFIFO_AVA_MASK (0x1U << CE_ICSR_TXFIFO_AVA_SHIFT) + +#define CE_ICSR_DRQ_ENABLE_SHIFT (4) +#define CE_ICSR_DRQ_ENABLE_MASK (0x1U << CE_ICSR_DRQ_ENABLE_SHIFT) + +#define CE_ICSR_HASH_CRC_END_INT_EN_SHIFT (3) +#define CE_ICSR_HASH_CRC_END_INT_EN_MASK (0x1U << CE_ICSR_HASH_CRC_END_INT_EN_SHIFT) + +#define CE_ICSR_RXFIFO_EMP_INT_EN_SHIFT (2) +#define CE_ICSR_RXFIFO_EMP_INT_EN_MASK (0x1U << CE_ICSR_RXFIFO_EMP_INT_EN_SHIFT) + +#define CE_ICSR_TXFIFO_AVA_INT_EN_SHIFT (0) +#define CE_ICSR_TXFIFO_AVA_INT_EN_MASK (0x1U << CE_ICSR_TXFIFO_AVA_INT_EN_SHIFT) + + +#define CE_3DES_KEY_COUNT (6) +#define CE_DES_KEY_COUNT (2) + + +/************************ public **************************************/ +#define CE_WAIT_TIME (4000) + +/* + * @brief aes/des/3des + * @notice Only support ECB & CBC mode. CTR & CTS mode have hardware bug. + */ +typedef enum { + CE_CRYPT_MODE_ECB = CE_CTL_CRYPT_MODE_ECB, /*!< Crypto Mode: ECB mode */ + CE_CRYPT_MODE_CBC = CE_CTL_CRYPT_MODE_CBC /*!< Crypto Mode: CBC mode */ +} CE_Crypto_Mode; + +typedef CE_CTL_KeySource CE_Crypto_KeySrc; +typedef CE_CTL_AES_KeySize CE_AES_KeySize; + +#define AES_BLOCK_SIZE (16) +#define DES_BLOCK_SIZE (8) +#define DES3_BLOCK_SIZE (8) + +typedef struct { + struct { + CE_Crypto_Mode mode; /*!< ECB & CBC */ + struct { + uint8_t iv[16]; /*!< initialization value, same as a block size which is 16 bytes */ +#ifdef CE_CTR_MODE + uint8_t cnt[16]; +#endif + }; +#ifdef CE_CTR_MODE + CE_CTL_CtrWidth width; +#endif + }; + + struct { + CE_Crypto_KeySrc src; /*!< key source */ + uint8_t key[32]; /*!< key */ + CE_CTL_AES_KeySize keysize; /*!< AES key size according to AES type like AES128 */ + }; +} CE_AES_Config; + +typedef struct { + struct { + CE_Crypto_Mode mode; /*!< ECB & CBC */ + union { + uint8_t iv[8]; /*!< initialization value, same as a block size which is 8 bytes */ +#ifdef CE_CTR_MODE + uint8_t cnt[8]; +#endif + }; +#ifdef CE_CTR_MODE + CE_CTL_CtrWidth width; +#endif + }; + + struct { + CE_Crypto_KeySrc src; /*!< key source */ + uint8_t key[8]; /*!< key */ + }; +} CE_DES_Config; + +typedef struct { + struct { + CE_Crypto_Mode mode; /*!< ECB & CBC */ + union { + uint8_t iv[8]; /*!< initialization value, same as a block size which is 8 bytes */ +#ifdef CE_CTR_MODE + uint8_t cnt[8]; +#endif + }; +#ifdef CE_CTR_MODE + CE_CTL_CtrWidth width; +#endif + }; + + struct { + CE_Crypto_KeySrc src; /*!< key source */ + uint8_t key[24]; /*!< key */ + }; +} CE_3DES_Config; + + +typedef struct { + uint8_t word[4]; + uint8_t word_size; +} CE_Fifo_Align; + +/* + * @brief crc + */ +typedef enum { + CE_CRC16_IBM, /*!< x16+x15+x2+1, poly 0x8005, init 0x0000, refin-true, refout-true, xorout-0x0000 */ + CE_CRC16_MAXIM, /*!< x16+x15+x2+1, poly 0x8005, init 0x0000, refin-true, refout-true, xorout-0xffff */ + CE_CRC16_USB, /*!< x16+x15+x2+1, poly 0x8005, init 0xffff, refin-true, refout-true, xorout-0xffff */ + CE_CRC16_MODBUS, /*!< x16+x15+x2+1, poly 0x8005, init 0xffff, refin-true, refout-true, xorout-0x0000 */ + CE_CRC16_CCITT, /*!< x16+x15+x5+1, poly 0x1021, init 0x0000, refin-true, refout-true, xorout-0x0000 */ + CE_CRC16_CCITT_1, /*!< x16+x15+x5+1, poly 0x1021, init 0xffff, refin-false, refout-false, xorout-0x0000 */ + CE_CRC16_X25, /*!< x16+x15+x5+1, poly 0x1021, init 0xffff, refin-true, refout-true, xorout-0xffff */ + CE_CRC16_XMODEM, /*!< x16+x15+x5+1, poly 0x1021, init 0x0000, refin-false, refout-false, xorout-0x0000 */ + CE_CRC16_DNP, /*!< x16+x13+x12+x11+x10+x8+x6+x5+x2+1, poly 0x3d65, init 0x0000, refin-true, refout-true, xorout-0xffff */ + CE_CRC32, /*!< x32+x26+x23+x22+x16+x12+x11+x10+x8+x7+x5+x4+x2+x+1, poly 0x04c11db7, init 0xffffffff, refin-true, refout-true, xorout-0xffffffff */ + CE_CRC32_MPEG2, /*!< x32+x26+x23+x22+x16+x12+x11+x10+x8+x7+x5+x4+x2+x+1, poly 0x04c11db7, init 0xffffffff, refin-false, refout-false, xorout-0x00000000 */ +} CE_CRC_Types; + +typedef struct { + uint64_t total_size; + struct { + uint8_t word[4]; + uint8_t word_size; + }; + + CE_CRC_Types type; + uint32_t crc; +} CE_CRC_Handler; + +/* + * @brief md5/sha1/sha256 + */ +typedef CE_CTL_IvMode_SHA_MD5 CE_Hash_IVsrc; + +#define CE_MD5_IV_SIZE (4) +#define CE_SHA1_IV_SIZE (5) +#define CE_SHA256_IV_SIZE (8) + +typedef struct { + uint64_t total_size; + struct { + uint8_t word[4]; + uint8_t word_size; + }; +} CE_MD5_Handler, CE_SHA1_Handler, CE_SHA256_Handler; + + +/********************************** Public Interface *************************************/ +HAL_Status HAL_CE_Init(); +HAL_Status HAL_CE_Deinit(); + +HAL_Status HAL_AES_Encrypt(CE_AES_Config *aes, uint8_t *plain, uint8_t *cipher, uint32_t size); +HAL_Status HAL_AES_Decrypt(CE_AES_Config *aes, uint8_t *cipher, uint8_t *plain, uint32_t size); + +HAL_Status HAL_DES_Encrypt(CE_DES_Config *des, uint8_t *plain, uint8_t *cipher, uint32_t size); +HAL_Status HAL_DES_Decrypt(CE_DES_Config *des, uint8_t *cipher, uint8_t *plain, uint32_t size); + +HAL_Status HAL_3DES_Encrypt(CE_3DES_Config *des, uint8_t *plain, uint8_t *cipher, uint32_t size); +HAL_Status HAL_3DES_Decrypt(CE_3DES_Config *des, uint8_t *cipher, uint8_t *plain, uint32_t size); + +HAL_Status HAL_CRC_Init(CE_CRC_Handler *hdl, CE_CRC_Types type, uint32_t total_size); +HAL_Status HAL_CRC_Append(CE_CRC_Handler *hdl, uint8_t *data, uint32_t size); +HAL_Status HAL_CRC_Finish(CE_CRC_Handler *hdl, uint32_t *crc); + +HAL_Status HAL_MD5_Init(CE_MD5_Handler *hdl, CE_Hash_IVsrc src, uint32_t iv[4]); +HAL_Status HAL_MD5_Append(CE_MD5_Handler *hdl, uint8_t *data, uint32_t size); +HAL_Status HAL_MD5_Finish(CE_MD5_Handler *hdl, uint32_t digest[4]); + +HAL_Status HAL_SHA1_Init(CE_SHA1_Handler *hdl, CE_Hash_IVsrc src, uint32_t iv[5]); +HAL_Status HAL_SHA1_Append(CE_SHA1_Handler *hdl, uint8_t *data, uint32_t size); +HAL_Status HAL_SHA1_Finish(CE_SHA1_Handler *hdl, uint32_t digest[5]); + +HAL_Status HAL_SHA256_Init(CE_SHA256_Handler *hdl, CE_Hash_IVsrc src, uint32_t iv[8]); +HAL_Status HAL_SHA256_Append(CE_SHA256_Handler *hdl, uint8_t *data, uint32_t size); +HAL_Status HAL_SHA256_Finish(CE_SHA256_Handler *hdl, uint32_t digest[8]); + +HAL_Status HAL_PRNG_Generate(uint8_t *random, uint32_t size); + + +#ifdef __cplusplus +} +#endif + +#endif /* _DRIVER_CHIP_HAL_CRYPTO_H_ */ diff --git a/platform/mcu/xr871/include/driver/chip/hal_csi.h b/platform/mcu/xr871/include/driver/chip/hal_csi.h new file mode 100644 index 0000000000..f38c751b65 --- /dev/null +++ b/platform/mcu/xr871/include/driver/chip/hal_csi.h @@ -0,0 +1,230 @@ +/** + * @file hal_csi.h + * @author XRADIO IOT WLAN Team + */ + +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _DRIVER_CHIP_HAL_CSI_H_ +#define _DRIVER_CHIP_HAL_CSI_H_ + +#include "driver/chip/hal_def.h" +#include "driver/chip/hal_ccm.h" +#include "driver/chip/hal_gpio.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief The register for CSI. + */ +typedef struct { + __IO uint32_t CSI_EN_REG; /*!< 0x00 CSI enable register*/ + __IO uint32_t CSI_CFG_REG; /*!< 0x04 CSI configuration register*/ + __IO uint32_t CSI_CAP_REG; /*!< 0x08 CSI capture control register*/ + __IO uint32_t CSI_SCALE_REG; /*!< 0x0c CSI scale */ + uint32_t RESERVED1[6]; + __IO uint32_t CSI_BUF_CTL_REG; /*!< 0x28 CSI output buffer control register*/ + __IO uint32_t CSI_BUF_STA_REG; /*!< 0x2c CSI status register*/ + __IO uint32_t CSI_INT_EN_REG; /*!< 0x30 CSI interrupt enable register*/ + __IO uint32_t CSI_INT_STA_REG; /*!< 0x34 CSI interrupt size register*/ + uint32_t RESERVED2[2]; + __IO uint32_t CSI_HSIZE_REG; /*!< 0x40 CSI horizontal size register*/ + uint32_t RESERVED3[1]; + __IO uint32_t CSI_BF_LEN_REG; /*!< 0x48 CSI line buffer length register*/ + __IO uint32_t CSI_TRUE_DATA_NUM;/*!< 0x4c CSI true data number of fifo sram register*/ + __IO uint32_t CSI_JPEG_MOD_SEL; /*!< 0x50 CSI JPEG Mode select register*/ +}CSI_T ; + + +#define CSI ((CSI_T *)DCMI_BASE) + +#define CSI_FIFO_A (DCMI_BASE + 0X800) +#define CSI_FIFO_B (DCMI_BASE + 0XA00) + +#define CSI_EN HAL_BIT(0) +#define CSI_CFG_INPUT_FORMAT ((uint32_t)0x00700000) +#define CSI_CFG_SYNC_SIGNAL_POL ((uint32_t)0x00000007) +#define CSI_CAP_MODE ((uint32_t)0x00000003) + +#define CSI_VER_MASK ((uint32_t)0x0F000000) +#define CSI_HER_MASK ((uint32_t)0x000000FF) + +#define LUM_STATIS ((uint32_t)0xFFFFFF00) + +#define CSI_INTERRUPT_CLEAR ((uint32_t)0x000003FF) + +#define CSI_SIZE_REG ((uint32_t)0xFFFFFFFF) + +#define CSI_LUM_LEN ((uint32_t)0x00001FFF) +#define CSI_VALID_DATA_LEN ((uint32_t)0x000003FF) + +/** + * @brief Set CSI clock. + */ +typedef struct { + CCM_AHBPeriphClkSrc clk; /*!< The source clock for CSI */ + CCM_PeriphClkDivN divN; /*!< CSI sourec clock division */ + CCM_PeriphClkDivM divM; /*!< CSI sourec clock division + The division result = divN * divM*/ +} CSI_Clk; + +/** + * @brief CSI clock configure. + */ +typedef struct { + CSI_Clk src_Clk; /*!< Set CSI clock */ +} CSI_Config; + +/** + * @brief Enable or disable func. + */ +typedef enum { + CSI_DISABLE, /*!< Enable*/ + CSI_ENABLE, /*!< Disable*/ +}CSI_CTRL; + +/** + * @brief Sync signal polarity. + */ +typedef enum { + CSI_NEGATIVE, /*!< Negative*/ + CSI_POSITIVE, /*!< Positive*/ +}CSI_SYNC_SIGNAL_POLARITY; + +/** + * @brief Configure CSI sync signal polarity. + */ +typedef struct { + CSI_SYNC_SIGNAL_POLARITY vsync; /*!< Set the polarity for vsync signal */ + CSI_SYNC_SIGNAL_POLARITY herf; /*!< Set the polarity for herf signal */ + CSI_SYNC_SIGNAL_POLARITY p_Clk; /*!< Set the polarity for pclk signal */ +} CSI_Sync_Signal; + +/** + * @brief CSI capture mode. + */ +typedef enum { + CSI_STILL_MODE, /*!< Capture one picture;When a picturee capture done, you need restart capture */ + CSI_VIDEO_MODE, /*!< Continuous capture of images, shoot video*/ +} CSI_CAPTURE_MODE; + +/** + * @brief The FIFO used for cache data. + */ +typedef enum { + CSI_FIFO_0_A, /*!< FIFO A */ + CSI_FIFO_0_B, /*!< FIFO B */ +}CSI_FIFO; + +/** + * @brief CSI module status. + */ +typedef enum { + CSI_FREE, /*!< The CSI module is free*/ + CSI_BUSY, /*!< The CSI module is running*/ +} CSI_RUN_STA; + +/** + * @brief CSI moudle Current operating status + */ +typedef struct { + uint32_t luminance; /*!< luminance statistics*/ + CSI_RUN_STA video_Mode; /*!< video mode status*/ + CSI_RUN_STA still_Mode; /*!< still mode status*/ +} CSI_Status; + +/** + * @brief Sets the size of the captured image. + */ +typedef struct { + uint16_t hor_start; /*!< Start capture position, unit byte.*/ + uint16_t hor_len; /*!< The picture one row contains the number of data(not number of pixel), unit byte.*/ +} CSI_Picture_Size; + + +/** + * @brief The CSI interrupt flag. + */ +typedef enum { + CSI_CAPTURE_DONE_IRQ = HAL_BIT(0), /*!< When the picture capture done, trigger this flag.*/ + CSI_FRAME_DONE_IRQ = HAL_BIT(1), /*!< When capture the frame done signal, trigger this flag.*/ + CSI_FIFO_0_OVERFLOW_IRQ = HAL_BIT(2), /*!< When FIFO is overflow, trigger this flag.*/ + CSI_ALL_FIFO_OVERFLOW_IRQ = HAL_BIT(6), /*!< When all FIFO is overflow, trigger this flag.*/ + CSI_VSYNC_IRQ = HAL_BIT(7), /*!< When capture the vsync signal, trigger this flag.*/ + CSI_FIFO_0_A_READY_IRQ = HAL_BIT(8), /*!< The CSI_FIFO_A is ready for read, trigger this flag.*/ + CSI_FIFO_0_B_READY_IRQ = HAL_BIT(9), /*!< The CSI_FIFO_B is ready for read, trigger this flag.*/ +} CSI_INTERRUPT_SIGNAL; + +/** + * @brief The number of bytes in the FIFO. + */ +typedef struct { + uint16_t FIFO_0_A_Data_Len; /*!< The number of bytes in the FIFO_A.*/ + uint16_t FIFO_0_B_Data_Len; /*!< The number of bytes in the FIFO_B.*/ +}CSI_FIFO_Data_Len; + +/** + * @brief Callback. + */ +typedef struct { + void *arg; + void(*callBack)(void *arg); +}CSI_Call_Back; + +HAL_Status HAL_CSI_Config(CSI_Config *param); +void HAL_CSI_DeInit(void); + +void HAL_CSI_Sync_Signal_Polarity_Cfg(CSI_Sync_Signal *signal); +void HAL_CSI_Capture_Enable(CSI_CAPTURE_MODE mode , CSI_CTRL ctrl); +void HAL_CSI_Interval_Capture_Cfg(uint8_t ver_mask, uint16_t hor_mask); + +void HAL_CSI_Selection_Next_FIFO (CSI_FIFO fifo_num); +CSI_FIFO HAL_CSI_Current_FIFO(); +void HAL_CSI_Double_FIFO_Mode_Enable(CSI_CTRL ctrl); + +CSI_Status HAL_CSI_Status(); +void HAL_CSI_Interrupt_Cfg(CSI_INTERRUPT_SIGNAL irq_signel, CSI_CTRL ctrl); +__IO uint32_t HAL_CSI_Interrupt_Sta(); +void HAL_CSI_Interrupt_Clear(); + +HAL_Status HAL_CSI_Set_Picture_Size(CSI_Picture_Size *size); +CSI_FIFO_Data_Len HAL_CSI_FIFO_Data_Len(); +void HAL_CIS_JPEG_Mode_Enable(CSI_CTRL ctrl); +void HAL_CSI_Interrupt_Enable(CSI_Call_Back *cb, CSI_CTRL ctrl); +void HAL_CSI_Moudle_Enalbe(CSI_CTRL ctrl); + +#ifdef __cplusplus +} +#endif + +#endif /* _DRIVER_CHIP_HAL_CSI_H_ */ + diff --git a/platform/mcu/xr871/include/driver/chip/hal_def.h b/platform/mcu/xr871/include/driver/chip/hal_def.h new file mode 100644 index 0000000000..c851111f2e --- /dev/null +++ b/platform/mcu/xr871/include/driver/chip/hal_def.h @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _DRIVER_CHIP_HAL_DEF_H_ +#define _DRIVER_CHIP_HAL_DEF_H_ + +#include "driver/chip/chip.h" +#include "kernel/os/os_common.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Bitwise operation + */ +#define HAL_BIT(pos) (1U << (pos)) + +#define HAL_SET_BIT(reg, mask) ((reg) |= (mask)) +#define HAL_CLR_BIT(reg, mask) ((reg) &= ~(mask)) +#define HAL_GET_BIT(reg, mask) ((reg) & (mask)) +#define HAL_GET_BIT_VAL(reg, shift, vmask) (((reg) >> (shift)) & (vmask)) + +#define HAL_MODIFY_REG(reg, clr_mask, set_mask) \ + ((reg) = (((reg) & (~(clr_mask))) | (set_mask))) + +/* + * Macros for accessing LSBs of a 32-bit register (little endian only) + */ +#define HAL_REG_32BIT(reg_addr) (*((__IO uint32_t *)(reg_addr))) +#define HAL_REG_16BIT(reg_addr) (*((__IO uint16_t *)(reg_addr))) +#define HAL_REG_8BIT(reg_addr) (*((__IO uint8_t *)(reg_addr))) + +/* Macro for counting the element number of an array */ +#define HAL_ARRAY_SIZE(a) (sizeof((a)) / sizeof((a)[0])) + +/* Wait forever timeout value */ +#define HAL_WAIT_FOREVER OS_WAIT_FOREVER + +/** + * @brief HAL Status value + */ +typedef enum +{ + HAL_OK = 0, /* success */ + HAL_ERROR = -1, /* general error */ + HAL_BUSY = -2, /* device or resource busy */ + HAL_TIMEOUT = -3, /* wait timeout */ + HAL_INVALID = -4 /* invalid argument */ +} HAL_Status; + +#ifdef __cplusplus +} +#endif + +#endif /* _DRIVER_CHIP_HAL_DEF_H_ */ diff --git a/platform/mcu/xr871/include/driver/chip/hal_dma.h b/platform/mcu/xr871/include/driver/chip/hal_dma.h new file mode 100644 index 0000000000..43044f08c5 --- /dev/null +++ b/platform/mcu/xr871/include/driver/chip/hal_dma.h @@ -0,0 +1,276 @@ +/** + * @file hal_dma.h + * @author XRADIO IOT WLAN Team + */ + +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _DRIVER_CHIP_HAL_DMA_H_ +#define _DRIVER_CHIP_HAL_DMA_H_ + +#include "driver/chip/hal_def.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Options of supporting DMA half transfer complete IRQ */ +#define HAL_DMA_TRANSFER_HALF_IRQ_SUPPORT 1 + +/** + * @brief DMA channel definition + */ +typedef enum { + DMA_CHANNEL_0 = 0U, + DMA_CHANNEL_1 = 1U, + DMA_CHANNEL_2 = 2U, + DMA_CHANNEL_3 = 3U, + DMA_CHANNEL_4 = 4U, + DMA_CHANNEL_5 = 5U, + DMA_CHANNEL_6 = 6U, + DMA_CHANNEL_7 = 7U, + DMA_CHANNEL_NUM = 8U, + DMA_CHANNEL_INVALID = DMA_CHANNEL_NUM +} DMA_Channel; + +/** + * @brief DMA channel register block structure + */ +typedef struct +{ + __IO uint32_t CTRL; /* offset: 0x00, DMA channel configuration register */ + __IO uint32_t SRC_ADDR; /* offset: 0x04, DMA channel source address register */ + __IO uint32_t DST_ADDR; /* offset: 0x08, DMA channel destination address register */ + __IO uint32_t BYTE_CNT; /* offset: 0x0C, DMA channel byte counter register */ + uint32_t RESERVED0[4]; +} DMA_CHANNEL_T; + +/** + * @brief DMA register block structure + */ +typedef struct +{ + __IO uint32_t IRQ_EN; /* offset: 0x00, DMA IRQ enable register */ + __IO uint32_t IRQ_STATUS; /* offset: 0x04, DMA IRQ status register */ + uint32_t RESERVED0[62]; + DMA_CHANNEL_T CHANNEL[DMA_CHANNEL_NUM]; /* offset: 0x100, DMA channels register blocks */ +} DMA_T; + +#define DMA ((DMA_T *)DMA_BASE) /* address: 0x40001000 */ + +/* + * Bit field definition of + * - DMA->IRQ_EN + * - DMA->IRQ_STATUS + */ +#define DMA_IRQ_TYPE_VMASK 0x3U +typedef enum { +#if HAL_DMA_TRANSFER_HALF_IRQ_SUPPORT + DMA_IRQ_TYPE_HALF = HAL_BIT(0), +#endif + DMA_IRQ_TYPE_END = HAL_BIT(1), +#if HAL_DMA_TRANSFER_HALF_IRQ_SUPPORT + DMA_IRQ_TYPE_BOTH = HAL_BIT(0) | HAL_BIT(1), +#endif +} DMA_IRQType; + +/* + * Bit field definition of DMA->CHANNEL[x].CTRL + */ +#define DMA_START_BIT HAL_BIT(31) /* R/W */ +#define DMA_BUSY_BIT HAL_BIT(30) /* R */ + +#define DMA_WORK_MODE_SHIFT 29 /* R/W */ +#define DMA_WORK_MODE_VMASK 0x1U +typedef enum { + DMA_WORK_MODE_SINGLE = 0U, /* one-shot DMA transfer */ + DMA_WORK_MODE_CIRCULAR = 1U /* repeat DMA transfer */ +} DMA_WorkMode; + +#define DMA_WAIT_CYCLE_SHIFT 26 /* R/W */ +#define DMA_WAIT_CYCLE_VMASK 0x7U +typedef enum { + DMA_WAIT_CYCLE_1 = 0U, + DMA_WAIT_CYCLE_2 = 1U, + DMA_WAIT_CYCLE_4 = 2U, + DMA_WAIT_CYCLE_8 = 3U, + DMA_WAIT_CYCLE_16 = 4U, + DMA_WAIT_CYCLE_32 = 5U, + DMA_WAIT_CYCLE_64 = 6U, + DMA_WAIT_CYCLE_128 = 7U +} DMA_WaitCycle; /* waiting cycles between two burst transaction */ + +#define DMA_DATA_WIDTH_VMASK 0x3U +typedef enum { + DMA_DATA_WIDTH_8BIT = 0U, + DMA_DATA_WIDTH_16BIT = 1U, + DMA_DATA_WIDTH_32BIT = 2U +} DMA_DataWidth; + +#define DMA_BURST_LEN_VMASK 0x1U +typedef enum { + DMA_BURST_LEN_1 = 0U, + DMA_BURST_LEN_4 = 1U +} DMA_BurstLen; + +#define DMA_ADDR_MODE_VMASK 0x1U +typedef enum { + DMA_ADDR_MODE_INC = 0U, /* address is increased when DMA transferring */ + DMA_ADDR_MODE_FIXED = 1U /* address is not changed when DMA transferring */ +} DMA_AddrMode; + +#define DMA_PERIPH_VMASK 0x1F +typedef enum { + DMA_PERIPH_SRAM = 0U, + DMA_PERIPH_SPI0 = 1U, + DMA_PERIPH_SPI1 = 2U, + DMA_PERIPH_UART0 = 3U, + DMA_PERIPH_UART1 = 4U, + DMA_PERIPH_CE = 5U, + DMA_PERIPH_DAUDIO = 6U, + DMA_PERIPH_FLASHC = 7U, + DMA_PERIPH_DMIC = 8U +} DMA_Periph; + +#define DMA_BYTE_CNT_MODE_SHIFT 15 /* R/W */ +#define DMA_BYTE_CNT_MODE_VMASK 0x1U +typedef enum { + DMA_BYTE_CNT_MODE_NORMAL = 0U, /* DMA->CHANNEL[x].BYTE_CNT is the data length of the DAM transfer */ + DMA_BYTE_CNT_MODE_REMAIN = 1U /* DMA->CHANNEL[x].BYTE_CNT is the length of the remaining data not transferred */ +} DMA_ByteCntMode; + +#define DMA_DST_DATA_WIDTH_SHIFT 24 /* R/W */ +#define DMA_DST_BURST_LEN_SHIFT 23 /* R/W */ +#define DMA_DST_ADDR_MODE_SHIFT 21 /* R/W */ +#define DMA_DST_PERIPH_SHIFT 16 /* R/W */ +#define DMA_SRC_DATA_WIDTH_SHIFT 8 /* R/W */ +#define DMA_SRC_BURST_LEN_SHIFT 7 /* R/W */ +#define DMA_SRC_ADDR_MODE_SHIFT 5 /* R/W */ +#define DMA_SRC_PERIPH_SHIFT 0 + +/* DMA->CHANNEL[x].SRC_ADDR */ + +/* DMA->CHANNEL[x].DST_ADDR */ + +/* DMA->CHANNEL[x].BYTE_CNT */ +#define DMA_BYTE_CNT_VMASK 0x3FFFF + +/* Maximum data length of one DMA transfer */ +#define DMA_DATA_MAX_LEN (128 * 1024U) + +/******************************************************************************/ + +/** @brief Type define of DMA IRQ callback function */ +typedef void (*DMA_IRQCallback)(void *arg); + +/** + * @brief DMA channel initialization parameters + */ +typedef struct { + uint32_t cfg; /* created by HAL_DMA_MakeInitCfg() */ + + DMA_IRQType irqType; /* DMA IRQ type to be enabled */ + DMA_IRQCallback endCallback; /* DMA transfer complete callback fucntion */ + void *endArg; /* argument of DMA transfer complete callback fucntion */ +#if HAL_DMA_TRANSFER_HALF_IRQ_SUPPORT + DMA_IRQCallback halfCallback; /* DMA half transfer complete callback fucntion */ + void *halfArg; /* argument of DMA half transfer complete callback fucntion */ +#endif +} DMA_ChannelInitParam; + +/** + * @brief Make configuration value for DMA_ChannelInitParam::cfg + * @param[in] workMode DMA transfer working mode + * @param[in] waitCycle Waiting cycles between two DMA burst transaction + * @param[in] byteCntMode DMA byte count mode + * @arg DMA_BYTE_CNT_MODE_NORMAL In normal mode, DMA->CHANNEL[x].BYTE_CNT is + * not changed in the whole DMA transfer, and + * its value is the data length of the DAM + * transfer. + * @arg DMA_BYTE_CNT_MODE_REMAIN In remaining mode, DMA->CHANNEL[x].BYTE_CNT + * is decreased when DMA transferring, and its + * value is the length of the remaining data + * not transferred. + * @param[in] dstDataWidth Destination DMA transaction data width + * @param[in] dstBurstLen Destination DMA transaction burst length + * @param[in] dstAddrMode Destination DMA address mode + * @param[in] dstPeriph Destination DMA peripheral type + * @param[in] srcDataWidth Source DMA transaction data width + * @param[in] srcBurstLen Source DMA transaction burst length + * @param[in] srcAddrMode Source DMA address mode + * @param[in] srcPeriph Source DMA peripheral type + * @return configuration value for DMA_ChannelInitParam::cfg + * + * @note + * - A DMA transaction is not interruptible + * - A DMA transaction data length = data width * burst length + */ +__STATIC_INLINE uint32_t HAL_DMA_MakeChannelInitCfg(DMA_WorkMode workMode, + DMA_WaitCycle waitCycle, + DMA_ByteCntMode byteCntMode, + DMA_DataWidth dstDataWidth, + DMA_BurstLen dstBurstLen, + DMA_AddrMode dstAddrMode, + DMA_Periph dstPeriph, + DMA_DataWidth srcDataWidth, + DMA_BurstLen srcBurstLen, + DMA_AddrMode srcAddrMode, + DMA_Periph srcPeriph) +{ + return ((((uint32_t)workMode & DMA_WORK_MODE_VMASK) << DMA_WORK_MODE_SHIFT) | + (((uint32_t)waitCycle & DMA_WAIT_CYCLE_VMASK) << DMA_WAIT_CYCLE_SHIFT) | + (((uint32_t)byteCntMode & DMA_BYTE_CNT_MODE_VMASK) << DMA_BYTE_CNT_MODE_SHIFT) | + (((uint32_t)dstDataWidth & DMA_DATA_WIDTH_VMASK) << DMA_DST_DATA_WIDTH_SHIFT) | + (((uint32_t)dstBurstLen & DMA_BURST_LEN_VMASK) << DMA_DST_BURST_LEN_SHIFT) | + (((uint32_t)dstAddrMode & DMA_ADDR_MODE_VMASK) << DMA_DST_ADDR_MODE_SHIFT) | + (((uint32_t)dstPeriph & DMA_PERIPH_VMASK) << DMA_DST_PERIPH_SHIFT) | + (((uint32_t)srcDataWidth & DMA_DATA_WIDTH_VMASK) << DMA_SRC_DATA_WIDTH_SHIFT) | + (((uint32_t)srcBurstLen & DMA_BURST_LEN_VMASK) << DMA_SRC_BURST_LEN_SHIFT) | + (((uint32_t)srcAddrMode & DMA_ADDR_MODE_VMASK) << DMA_SRC_ADDR_MODE_SHIFT) | + (((uint32_t)srcPeriph & DMA_PERIPH_VMASK) << DMA_SRC_PERIPH_SHIFT)); +} + +DMA_Channel HAL_DMA_Request(void); +void HAL_DMA_Release(DMA_Channel chan); + +void HAL_DMA_Init(DMA_Channel chan, const DMA_ChannelInitParam *param); +void HAL_DMA_DeInit(DMA_Channel chan); + +void HAL_DMA_Start(DMA_Channel chan, uint32_t srcAddr, uint32_t dstAddr, uint32_t datalen); +void HAL_DMA_Stop(DMA_Channel chan); + +int HAL_DMA_IsBusy(DMA_Channel chan); +uint32_t HAL_DMA_GetByteCount(DMA_Channel chan); + +#ifdef __cplusplus +} +#endif + +#endif /* _DRIVER_CHIP_HAL_DMA_H_ */ diff --git a/platform/mcu/xr871/include/driver/chip/hal_dmic.h b/platform/mcu/xr871/include/driver/chip/hal_dmic.h new file mode 100644 index 0000000000..cea09ee6ea --- /dev/null +++ b/platform/mcu/xr871/include/driver/chip/hal_dmic.h @@ -0,0 +1,281 @@ +/** + * @file hal_dmic.h + * @author XRADIO IOT WLAN Team + */ + +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _DRIVER_CHIP_HAL_DMIC_H_ +#define _DRIVER_CHIP_HAL_DMIC_H_ + +#include +#include "driver/chip/hal_def.h" +#include "driver/chip/hal_gpio.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct +{ + __IO uint32_t DMIC_EN; /* DMIC enable control register Address offset: 0x0000 */ + __IO uint32_t DMIC_SR; /* DMIC sample rate register Adrress offset: 0x0004 */ + __IO uint32_t DMIC_CTR; /* DMIC control register Address offset: 0x0008 */ + __IO uint32_t RESERVED0; + __IO uint32_t DMIC_DATA; /* DMIC data register Address offset: 0x0010 */ + __IO uint32_t DMIC_INTC; /* DMIC interrupt control register Address offset: 0x0014 */ + __IO uint32_t DMIC_INTS; /* DMIC interrupt status register Address offset: 0x0018 */ + __IO uint32_t FIFO_CTR; /* DMIC FIFO control register Address offset: 0x001C */ + __I uint32_t FIFO_STA; /* DMIC FIFO status register Address offset: 0x0020 */ + __IO uint32_t CH_NUM; /* DMIC channel numbers register Address offset: 0x0024 */ + __IO uint32_t CH_MAP; /* DMIC channel mapping register Address offset: 0x0028 */ + __IO uint32_t DMIC_CNT; /* DMIC counter register Address offset: 0x002C */ + __IO uint32_t DATA0_DATA1_VOL_CTR; /* DATA0 and DATA1 volume control register Address offset: 0x0030 */ + __IO uint32_t DATA2_DATA3_VOL_CTR; /* High pass filter enable control register Address offset: 0x0034 */ + __IO uint32_t HPF_EN_CTR; /**/ + __IO uint32_t HPF_COEF_REG; /* High pass filter coef register Address offset: 0x003C */ + __IO uint32_t HPF_GAIN_REG; /* High pass filter gain register Address offset: 0x0040 */ +} DMIC_T; + +#define DMIC ((DMIC_T *)DMIC_BASE) + +/* + * Bits definition for DMIC_ECR register(0x0000) + */ +#define DMIC_ECR_DATA0_CHL_EN_BIT HAL_BIT(0) +#define DMIC_ECR_DATA0_CHR_EN_BIT HAL_BIT(1) +#define DMIC_ECR_DATA1_CHL_EN_BIT HAL_BIT(2) +#define DMIC_ECR_DATA1_CHR_EN_BIT HAL_BIT(3) +#define DMIC_ECR_DATA2_CHL_EN_BIT HAL_BIT(4) +#define DMIC_ECR_DATA2_CHR_EN_BIT HAL_BIT(5) +#define DMIC_ECR_DATA3_CHL_EN_BIT HAL_BIT(6) +#define DMIC_ECR_DATA3_CHR_EN_BIT HAL_BIT(7) +#define DMIC_ECR_GLOBE_EN HAL_BIT(8) + +/* + * Bits definition for DMIC_SRR register (0x0004) + */ +#define DMIC_SR_SHIFT (0) +#define DMIC_SR_MASK (0x7U << DMIC_SR_SHIFT) + +/** + * @brief Dmic sample rate definition + */ +typedef enum { + DMIC_SR48KHZ = (0X0U << DMIC_SR_SHIFT), + DMIC_SR44KHZ = (0X8U << DMIC_SR_SHIFT),/*44100*/ + DMIC_SR24KHZ = (0X2U << DMIC_SR_SHIFT), + DMIC_SR22KHZ = (0X9U << DMIC_SR_SHIFT),/*22050*/ + DMIC_SR12KHZ = (0X4U << DMIC_SR_SHIFT), + DMIC_SR11KHZ = (0XaU << DMIC_SR_SHIFT),/*11025*/ + DMIC_SR32KHZ = (0X1U << DMIC_SR_SHIFT), + DMIC_SR16KHZ = (0X3U << DMIC_SR_SHIFT), + DMIC_SR8KHZ = (0X5U << DMIC_SR_SHIFT), +} DMIC_SampleRate; + +/* + * Bits definition for DMIC_CTLR register (0X0008) + */ +#define DMIC_CTLR_OVERSAMPLE_RATE_SHIFT (0) +#define DMIC_CTLR_OVERSAMPLE_RATE_MASK (0x1U << DMIC_CTLR_OVERSAMPLE_RATE_SHIFT) +typedef enum { + DMIC_CTLR_OVERSAMPLE_RATE64 = (0X1U << DMIC_CTLR_OVERSAMPLE_RATE_SHIFT), + DMIC_CTLR_OVERSAMPLE_RATE128 = (0X0U << DMIC_CTLR_OVERSAMPLE_RATE_SHIFT), +} DMIC_OverSampleRate; + +#define DMIC_CTLR_DATA0_SWAP_BIT HAL_BIT(4) /* bit4, data0 left data and right data sweep enable */ +#define DMIC_CTLR_DATA1_SWAP_BIT HAL_BIT(5) /* bit5, data1 left data and right data sweep enable */ +#define DMIC_CTLR_DATA2_SWAP_BIT HAL_BIT(6) /* bit6, data2 left data and right data sweep enable */ +#define DMIC_CTLR_DATA3_SWAP_BIT HAL_BIT(7) /* bit7, data3 left data and right data sweep enable */ +#define DMIC_CTLR_DMICDFEN_BIT HAL_BIT(8) /* bit8, dmic fifo delay function for writing data after globe_en */ + +#define DMIC_CTLR_DMICFDT_SHIFT (9) +#define DMIC_CTLR_DMICFDT_MASK (0x3U << DMIC_CTLR_OVERSAMPLE_RATE_SHIFT) +typedef enum { + DMIC_CTLR_DMICFDT_5MS = (0X0U << DMIC_CTLR_DMICFDT_SHIFT), + DMIC_CTLR_DMICFDT_10MS = (0X1U << DMIC_CTLR_DMICFDT_SHIFT), + DMIC_CTLR_DMICFDT_200MS = (0X2U << DMIC_CTLR_DMICFDT_SHIFT), + DMIC_CTLR_DMICFDT_30MS = (0X3U << DMIC_CTLR_DMICFDT_SHIFT), +} DMIC_CtrlDmicFDT; + +/* + * Bits definition for DMIC_ICR register (0x14) + */ +#define DMIC_ICR_DATA_IRQ_EN_BIT HAL_BIT(0) /* bit0, dmic fifo data available irq enable */ +#define DMIC_ICR_OVERRUN_IRQ_EN_BIT HAL_BIT(1) /* bit1, dmic fifo fifo overrun irq enable */ +#define DMIC_ICR_FIFO_DRQ_EN_BIT HAL_BIT(2) /* bit2, dmic fifo data available drq enable */ + +/* + * Bits definition for DMIC_ISR register (0X18) + */ +#define DMIC_ISR_DATA_IRQ_PEND_BIT HAL_BIT(0) /* bit0, dmic fifo data available pending interrupt */ +#define DMIC_ISR_OVERRUN_IRQ_PEND_BIT HAL_BIT(1) /* bit1, dmic fifo fifo overrun pending interrupt */ + +/* + * Bits definition for DMIC_FIFOCR register(0X1C) + */ +#define DMIC_FIFOCR_TRG_LEVEL_SHIFT (0) +#define DMIC_FIFOCR_TRG_LEVEL_MASK (0xFFU << DMIC_FIFOCR_TRG_LEVEL_SHIFT) +#define DMIC_FIFOCR_TRG_LEVEL(n) ((n) << DMIC_FIFOCR_TRG_LEVEL_SHIFT) + +#define DMIC_FIFOCR_SAMPLE_RES_SHIFT (8) +#define DMIC_FIFOCR_SAMPLE_RES_MASK (0x1U << DMIC_FIFOCR_SAMPLE_RES_SHIFT) + +/** + * @brief sampling accuracy + */ +typedef enum { + DMIC_RES16BIT = (0X0U << DMIC_FIFOCR_SAMPLE_RES_SHIFT), + DMIC_RES24BIT = (0X1U << DMIC_FIFOCR_SAMPLE_RES_SHIFT), +} DMIC_FifoSampleRes; + +#define DMIC_FIFOCR_MODE_SHIFT (9) +#define DMIC_FIFOCR_MODE_MASK (0x1U << DMIC_FIFOCR_MODE_SHIFT) +typedef enum { + DMIC_FIFOCR_MODE0 = (0X0U << DMIC_FIFOCR_MODE_SHIFT), + DMIC_FIFOCR_MODE1 = (0X1U << DMIC_FIFOCR_MODE_SHIFT), +} DMIC_FifoMode; + +#define DMIC_FIFOCR_FIFO_FLUSH_BIT HAL_BIT(31) /* bit31, dmic fifo flush */ + +/* + * Bits definition for DMIC_FIFOSR register (0x20) + */ +#define DMIC_FIFOSR_DATA_CNT_SHIFT (0) +#define DMIC_FIFOSR_DATA_CNT_MASK (0xFFU << DMIC_FIFOSR_DATA_CNT_SHIFT) /* bit0:bit7, FIFO available sample world counter */ + +/* + * Bits definition for DMIC_CNR register + */ +#define DMIC_CH_NUM_SHIFT (0) +#define DMIC_CH_NUM_MASK (0X7U << DMIC_CH_NUM_SHIFT) +typedef enum { + DMIC_CH_NUM1 = (0X0U << DMIC_CH_NUM_SHIFT), + DMIC_CH_NUM2 = (0X1U << DMIC_CH_NUM_SHIFT), + DMIC_CH_NUM3 = (0X2U << DMIC_CH_NUM_SHIFT), + DMIC_CH_NUM4 = (0X3U << DMIC_CH_NUM_SHIFT), + DMIC_CH_NUM5 = (0X4U << DMIC_CH_NUM_SHIFT), + DMIC_CH_NUM6 = (0X5U << DMIC_CH_NUM_SHIFT), + DMIC_CH_NUM7 = (0X6U << DMIC_CH_NUM_SHIFT), + DMIC_CH_NUM8 = (0X7U << DMIC_CH_NUM_SHIFT), +} DMIC_ChannelNum; + +/* + * Bits definition for DMIC_CMR register(0x28) + */ +#define DMIC_CMR_CH0_MAP_SHIFT (0) +#define DMIC_CMR_CH0_MAP_MASK(m) (0X7U << 4*m) +#define DMIC_CMR_CH0_MAP(m) (m << 4*m) + +/* + * Bits definition for DMIC_VCR0 register (0X0030) + */ +#define DMIC_DATA0R_VOL_SHIFT (0) +#define DMIC_DATA0R_VOL_MASK (0xFFU << DMIC_DATA0R_VOL_SHIFT) +#define DMIC_DATA0R_VOL(n) (n << DMIC_DATA0R_VOL_SHIFT) + +#define DMIC_DATA0L_VOL_SHIFT (8) +#define DMIC_DATA0L_VOL_MASK (0xFFU << DMIC_DATA0L_VOL_SHIFT) +#define DMIC_DATA0L_VOL(n) (n << DMIC_DATA0L_VOL_SHIFT) + +#define DMIC_DATA1R_VOL_SHIFT (16) +#define DMIC_DATA1R_VOL_MASK (0xFFU << DMIC_DATA1R_VOL_SHIFT) + +#define DMIC_DATA1L_VOL_SHIFT (24) +#define DMIC_DATA1L_VOL_MASK (0xFFU << DMIC_DATA1L_VOL_SHIFT) + +/* + * Bits definition for DMIC_VCR1 register (0X0034) + */ +#define DMIC_DATA2R_VOL_SHIFT (0) +#define DMIC_DATA2R_VOL_MASK (0xFFU << DMIC_DATA2R_VOL_SHIFT) + +#define DMIC_DATA2L_VOL_SHIFT (8) +#define DMIC_DATA2L_VOL_MASK (0xFFU << DMIC_DATA2L_VOL_SHIFT) + +#define DMIC_DATA3R_VOL_SHIFT (16) +#define DMIC_DATA3R_VOL_MASK (0xFFU << DMIC_DATA3R_VOL_SHIFT) + +#define DMIC_DATA3L_VOL_SHIFT (24) +#define DMIC_DATA3L_VOL_MASK (0xFFU << DMIC_DATA3L_VOL_SHIFT) + +/* + * Bits definition for DMIC_HPFECR register (0X0038) + */ +#define DMIC_HPF_DATA0_CHL_EN_BIT HAL_BIT(0) /* bit0, data0 left channel enable */ +#define DMIC_HPF_DATA0_CHR_EN_BIT HAL_BIT(1) /* bit1, data0 right channel 1 enable */ +#define DMIC_HPF_DATA1_CHL_EN_BIT HAL_BIT(2) /* bit2, data1 left channel enable */ +#define DMIC_HPF_DATA1_CHR_EN_BIT HAL_BIT(3) /* bit3, data1 right channel 1 enable */ +#define DMIC_HPF_DATA2_CHL_EN_BIT HAL_BIT(4) /* bit4, data2 left channel enable */ +#define DMIC_HPF_DATA2_CHR_EN_BIT HAL_BIT(5) /* bit5, data2 right channel 1 enable */ +#define DMIC_HPF_DATA3_CHL_EN_BIT HAL_BIT(6) /* bit6, data3 left channel enable */ +#define DMIC_HPF_DATA3_CHR_EN_BIT HAL_BIT(7) /* bit7, data3 right channel 1 enable */ + +/** + * @brief Dmic low level hardware init structure definition + */ +typedef struct { + DMIC_CtrlDmicFDT delayTime; /*!< Specifies dmic fdt delay time. */ + DMIC_FifoMode fifoMode; /*!< Specifies dmic fifo mode. */ + uint32_t triggerLevel; /*!< Specifies the threshold for dmic fifo triggers. */ + uint32_t volumeGain; /*!< Specifies the dmic hardware gain. */ + bool hpfEnable; /*!< Specifies whether to enable hpf function. */ +} DMIC_HWParam; + +/** + * @brief Dmic data format init structure definition + */ +typedef struct { + DMIC_SampleRate sampleRate; /*!< Specifies the sampling rate of the stream. */ + DMIC_FifoSampleRes resolution; /*!< Specifies the sampling accuracy of the stream. */ + uint32_t channels; /*!< Specifies the number of the stream. */ + uint32_t bufSize; /*!< Specifies the buffer size of received data. */ +} DMIC_DataParam; + +/** + * @brief Dmic module init structure definition + */ +typedef struct { + DMIC_HWParam *hwParam; /*!< Dmic hardware init structure. */ +} DMIC_Param; + +HAL_Status HAL_DMIC_Init(DMIC_Param *param); +void HAL_DMIC_DeInit(); +HAL_Status HAL_DMIC_Open(DMIC_DataParam *param); +void HAL_DMIC_Close(); +HAL_Status HAL_DMIC_Suspend(); +HAL_Status HAL_DMIC_Resume(); +int32_t HAL_DMIC_Read_DMA(uint8_t *buf, uint32_t size); + +#ifdef __cplusplus +} +#endif + +#endif /* _DRIVER_CHIP_HAL_DMIC_H_ */ diff --git a/platform/mcu/xr871/include/driver/chip/hal_efuse.h b/platform/mcu/xr871/include/driver/chip/hal_efuse.h new file mode 100644 index 0000000000..dcb13f7f83 --- /dev/null +++ b/platform/mcu/xr871/include/driver/chip/hal_efuse.h @@ -0,0 +1,93 @@ +/** + * @file hal_efuse.h + * @author XRADIO IOT WLAN Team + */ + +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _DRIVER_CHIP_HAL_EFUSE_H_ +#define _DRIVER_CHIP_HAL_EFUSE_H_ + +#include "driver/chip/hal_def.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief EFUSE register block structure + */ +typedef struct { + uint32_t RESERVED0[16]; + __IO uint32_t CTRL; /* offset: 0x0040 EFUSE program/read control register */ + uint32_t RESERVED1[3]; + __IO uint32_t PROGRAM_VALUE; /* offset: 0x0050 EFUSE program key value register */ + uint32_t RESERVED2[3]; + __I uint32_t READ_VALUE; /* offset: 0x0060 EFUSE read key value register */ + uint32_t RESERVED3[11]; + __IO uint32_t TIMING_CTRL; /* offset: 0x0090 EFUSE burned timing control register */ + __IO uint32_t DEBUG_REG; /* offset: 0x0094 EFUSE debug register */ +} EFUSE_T; + +#define EFUSE ((EFUSE_T *)SID_BASE) /* address: 0x40043C00 */ + +/* EFUSE->CTRL */ +#define EFUSE_CLK_GATE_EN_BIT HAL_BIT(28) + +#define EFUSE_INDEX_SHIFT 16 +#define EFUSE_INDEX_MASK ((0xFFU) << EFUSE_INDEX_SHIFT) + +#define EFUSE_OPERA_LOCK_SHIFT 8 +#define EFUSE_OPERA_LOCK_MASK ((0xFFU) << EFUSE_OPERA_LOCK_SHIFT) +#define EFUSE_OPERA_UNLOCK_VAL 0xACU + +#define EFUSE_HW_READ_STATUS_BIT HAL_BIT(2) +#define EFUSE_SW_READ_START_BIT HAL_BIT(1) +#define EFUSE_SW_PROG_START_BIT HAL_BIT(0) + +/* EFUSE->TIMING_CTRL */ +typedef enum { + EFUSE_TIMING_PARAM_24M = 0x63321190U, + EFUSE_TIMING_PARAM_26M = 0x63321190U, +} EFUSE_TimingParam; + +/******************************************************************************/ + +/** @brief The number of bits on chip EFUSE */ +#define HAL_EFUSE_BIT_NUM (2048) + +HAL_Status HAL_EFUSE_Read(uint32_t start_bit, uint32_t bit_num, uint8_t *data); +HAL_Status HAL_EFUSE_Write(uint32_t start_bit, uint32_t bit_num, uint8_t *data); + +#ifdef __cplusplus +} +#endif + +#endif /* _DRIVER_CHIP_HAL_EFUSE_H_ */ diff --git a/platform/mcu/xr871/include/driver/chip/hal_flash.h b/platform/mcu/xr871/include/driver/chip/hal_flash.h new file mode 100644 index 0000000000..627edd4daf --- /dev/null +++ b/platform/mcu/xr871/include/driver/chip/hal_flash.h @@ -0,0 +1,142 @@ +/** + * @file hal_flash.h + * @author XRADIO IOT WLAN Team + */ + +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef HAL_FLASH_H_ +#define HAL_FLASH_H_ + +#include +#include +#include "flashchip/flash_chip.h" +#include "driver/chip/hal_spi.h" + +#define FLASH_FLASHC_ENABLE (1) +#define FLASH_SPI_ENABLE (!defined(__CONFIG_BOOTLOADER)) + +typedef struct FlashDrvierBase FlashDrvierBase; + +typedef HAL_Status (*FlashDriverFunc)(FlashDrvierBase *base, InstructionField *cmd, InstructionField *addr, InstructionField *dummy, InstructionField *data); + +struct FlashDrvierBase +{ + /* + attribute + */ + int dev; + uint32_t sizeToDma; + + /* + public interface + */ + FlashDriverFunc write; + FlashDriverFunc read; + HAL_Status (*open)(FlashDrvierBase *base); + HAL_Status (*close)(FlashDrvierBase *base); + HAL_Status (*setFreq)(FlashDrvierBase *base, uint32_t freq); + void (*msleep)(FlashDrvierBase *base, uint32_t ms); + void (*destroy)(FlashDrvierBase *); +}; + +FlashDrvierBase *FlashDriverCreator(int driver); +int FlashDriverDestory(FlashDrvierBase *base); + +/* + Flash Board Config +*/ +enum FlashBoardType +{ +#if FLASH_FLASHC_ENABLE + FLASH_DRV_FLASHC, /*!< flash controller driver */ +#endif +#if FLASH_SPI_ENABLE + FLASH_DRV_SPI /*!< spi driver */ +#endif +}; + +typedef struct FlashcBoardCfg +{ + uint32_t clk; /*!< flash clock */ +} FlashcBoardCfg; + +typedef struct SpiBoardCfg +{ + uint32_t clk; /*!< flash clock */ + SPI_Port port; /*!< spi port */ + SPI_CS cs; /*!< cs pin */ +} SpiBoardCfg; + +typedef struct FlashBoardCfg +{ + enum FlashBoardType type; /*!< flash driver */ + FlashReadMode mode; /*!< read mode to flash */ + union { + FlashcBoardCfg flashc; /*!< flash driver controller configuration. Notice!! flashc support all read mode */ + SpiBoardCfg spi; /*!< spi driver configuration. Notice!! spi only support normal read, fast read, dual output mode */ + }; +} FlashBoardCfg; + + +/* + Flash Global Interface +*/ +typedef struct FlashDev FlashDev; + +typedef enum FlashControlCmd +{ + FLASH_GET_MIN_ERASE_SIZE, + /*TODO: tbc...*/ +} FlashControlCmd; + +HAL_Status HAL_Flash_Init(uint32_t flash); + +HAL_Status HAL_Flash_Deinit(uint32_t flash); + +HAL_Status HAL_Flash_Open(uint32_t flash, uint32_t timeout_ms); + +HAL_Status HAL_Flash_Close(uint32_t flash); + +HAL_Status HAL_Flash_Control(uint32_t flash, FlashControlCmd attr, uint32_t arg); + +HAL_Status HAL_Flash_Overwrite(uint32_t flash, uint32_t addr, uint8_t *data, uint32_t size); + +HAL_Status HAL_Flash_Write(uint32_t flash, uint32_t addr, uint8_t *data, uint32_t size); + +HAL_Status HAL_Flash_Read(uint32_t flash, uint32_t addr, uint8_t *data, uint32_t size); + +HAL_Status HAL_Flash_Erase(uint32_t flash, FlashEraseMode blk_size, uint32_t addr, uint32_t blk_cnt); + +HAL_Status HAL_Flash_MemoryOf(uint32_t flash, FlashEraseMode size, uint32_t addr, uint32_t *start); + +int HAL_Flash_Check(uint32_t flash, uint32_t addr, uint8_t *data, uint32_t size); + +#endif /* HAL_FLASH_H_ */ diff --git a/platform/mcu/xr871/include/driver/chip/hal_flashcache.h b/platform/mcu/xr871/include/driver/chip/hal_flashcache.h new file mode 100644 index 0000000000..8733c533b4 --- /dev/null +++ b/platform/mcu/xr871/include/driver/chip/hal_flashcache.h @@ -0,0 +1,141 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _DRIVER_CHIP_HAL_FLASHCACHE_H_ +#define _DRIVER_CHIP_HAL_FLASHCACHE_H_ + +#include +#include "driver/chip/hal_def.h" +#include "sys/xr_debug.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct +{ + __IO uint32_t COM_CTRL; /* , Address offset: */ + __IO uint32_t PREFETCH_CTRL; + __IO uint32_t PREFETCH_START_ADDR; /* , Address offset: */ + __IO uint32_t IV_PREFETCH_CTRL; /* , Address offset: */ + __IO uint32_t IV_ADDR[16]; /* , Address offset: */ + __IO uint32_t INDIRECT_JUMP_ADDR[16]; /* , Address offset: */ + __IO uint32_t MIN_ADDR; /* , Address offset: */ + __IO uint32_t MAX_ADDR; /* , Address offset: */ + __IO uint32_t READ_BIAS_ADDR; /* , Address offset: */ + +} FLASH_CACHE_T; + + +#define FLASH_CACHE ((FLASH_CACHE_T *)FLASH_CACHE_BASE) + +/* + * @brief + */ +#define FLASH_CACHE_COM_CTRL_SIZE_SHIFT (0) +#define FLASH_CACHE_COM_CTRL_SIZE_MASK (0x1FFU << FLASH_CACHE_COM_CTRL_SIZE_SHIFT) + +#define FLASH_CACHE_PREFETCH_CTRL_SIZE_SHIFT (8) +#define FLASH_CACHE_PREFETCH_CTRL_SIZE_MASK (0xFFU << FLASH_CACHE_PREFETCH_CTRL_SIZE_SHIFT) + +#define FLASH_CACHE_PREFETCH_CTRL_BRANCH_SHIFT (4) +#define FLASH_CACHE_PREFETCH_CTRL_BRANCH_MASK (0x1U << FLASH_CACHE_PREFETCH_CTRL_BRANCH_SHIFT) + +#define FLASH_CACHE_PREFETCH_CTRL_STOP_SHIFT (0) +#define FLASH_CACHE_PREFETCH_CTRL_STOP_MASK (0x1U << FLASH_CACHE_PREFETCH_CTRL_STOP_SHIFT) + +typedef enum { + FLASH_CACHE_PREFETCH_ENABLE = 0, + FLASH_CACHE_PREFETCH_DISABLE = 1 +} Flash_Cache_Prefetch; + +#define FLASH_CACHE_PREFETCH_START_ADDR_SHIFT (0) +#define FLASH_CACHE_PREFETCH_START_ADDR_MASK (0xFFFFFFU << FLASH_CACHE_PREFETCH_START_ADDR_SHIFT) + +#define FLASH_CACHE_IV_PREFETCH_CTRL_RELEASE_SHIFT (12) +#define FLASH_CACHE_IV_PREFETCH_CTRL_RELEASE_MASK (0x1U << FLASH_CACHE_IV_PREFETCH_CTRL_RELEASE_SHIFT) + +#define FLASH_CACHE_IV_PREFETCH_CTRL_IV_NUM_SHIFT (4) +#define FLASH_CACHE_IV_PREFETCH_CTRL_IV_NUM_MASK (0xFU << FLASH_CACHE_IV_PREFETCH_CTRL_IV_NUM_SHIFT) + +#define FLASH_CACHE_IV_PREFETCH_CTRL_ENABLE_SHIFT (0) +#define FLASH_CACHE_IV_PREFETCH_CTRL_ENABLE_MASK (0x1U << FLASH_CACHE_IV_PREFETCH_CTRL_ENABLE_SHIFT) + +#define FLASH_CACHE_IV_ADDR_SHIFT (0) +#define FLASH_CACHE_IV_ADDR_MASK (0xFFFFFFU << FLASH_CACHE_IV_ADDR_SHIFT) + +#define FLASH_CACHE_INDIRECT_JUMP_ADDR_SHIFT (0) +#define FLASH_CACHE_INDIRECT_JUMP_ADDR_MASK (0xFFFFFFU << FLASH_CACHE_INDIRECT_JUMP_ADDR_SHIFT) + +#define FLASH_CACHE_MIN_ADDR_SHIFT (0) +#define FLASH_CACHE_MIN_ADDR_MASK (0xFFFFFFFFU << FLASH_CACHE_MIN_ADDR_SHIFT) + +#define FLASH_CACHE_MAX_ADDR_SHIFT (0) +#define FLASH_CACHE_MAX_ADDR_MASK (0xFFFFFFFFU << FLASH_CACHE_MAX_ADDR_SHIFT) + +#define FLASH_CACHE_READ_BIAS_ADDR_SHIFT (0) +#define FLASH_CACHE_READ_BIAS_ADDR_MASK (0xFFFFFFU << FLASH_CACHE_READ_BIAS_ADDR_SHIFT) + + +#define FLASH_ROM_START_ADDR (0x10000000U) +#define FLASH_ROM_END_ADDR (0x101FFFFFU) + +#define CACHE_LINE_MAX (0x1FF) +#define CACHE_START_ADDR (FLASH_ROM_START_ADDR) +#define CACHE_END_ADDR (FLASH_ROM_END_ADDR) + + + +typedef struct { +// uint32_t cache_size; /* in bytes */ + uint32_t addr_bias; /* bias from flash */ +} FlashCache_Config; + +typedef struct { +// uint32_t prefetch_cache_size; /* in bytes, recommended size is no more than FlashCache_Config.cache_size */ + bool prefetch_2nd_branch; + uint32_t addr_prefetch_start; +} FlashCache_PrefetchConfig; + +typedef struct { + struct { + uint32_t addr_interrupt_vector; + uint32_t addr_interrupt_cb; + } prefetch_interrupt[16]; +} FlashCache_PrefetchIntConfig; + + +HAL_Status Hal_FlashCache_Init(FlashCache_Config *cfg); +HAL_Status Hal_FlashCache_Deinit(); + +#ifdef __cplusplus +} +#endif + +#endif /* _DRIVER_CHIP_HAL_FLASHCACHE_H_ */ diff --git a/platform/mcu/xr871/include/driver/chip/hal_flashctrl.h b/platform/mcu/xr871/include/driver/chip/hal_flashctrl.h new file mode 100644 index 0000000000..b829c12d7f --- /dev/null +++ b/platform/mcu/xr871/include/driver/chip/hal_flashctrl.h @@ -0,0 +1,458 @@ +/** + * @file hal_flashctrl.h + * @author XRADIO IOT WLAN Team + */ + +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __HAL_FLASHCTRL_H_ +#define __HAL_FLASHCTRL_H_ + +#include +#include +#include "driver/chip/hal_def.h" +#include "sys/xr_debug.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct +{ + __IO uint32_t COMMON_CFG; /* , Address offset: 0x000 */ + __IO uint32_t I_CMD_CFG; /* , Address offset: 0x004 */ + __IO uint32_t I_DUMMY_H; /* , Address offset: 0x008 */ + __IO uint32_t I_DUMMY_L; /* , Address offset: 0x00C */ + __IO uint32_t I_CS_WAIT; /* , Address offset: 0x010 */ + __IO uint32_t I_IO_WAIT;//CMD_WAIT; /* , Address offset: 0x014 */ + __IO uint32_t RESERVE0[2]; + __IO uint32_t FLASH_COMMON_CFG; /* , Address offset: 0x020 */ + __I uint32_t XIP_EXEC; /* , Address offset: 0x024 */ + + __IO uint32_t COMMON_ADD_CFG; /* , Address offset: 0x028 */ + __IO uint32_t S_CMD_CFG; /* , Address offset: 0x02C */ + __IO uint32_t S_ADDR_CFG; /* , Address offset: 0x030 */ + __IO uint32_t S_WR_NUM; /* , Address offset: 0x034 */ + __IO uint32_t S_RD_NUM; /* , Address offset: 0x038 */ + __IO uint32_t S_DUMMY_H; /* , Address offset: 0x03C */ + __IO uint32_t S_DUMMY_L; /* , Address offset: 0x040 */ + __IO uint32_t FIFO_TRIG_LEVEL; /* , Address offset: 0x044 */ + __I uint32_t FIFO_STATUS; /* , Address offset: 0x048 */ + __IO uint32_t S_IO_WAIT; /* , Address offset: 0x04C */ + __I uint32_t WRAP_MODE; /* , Address offset: 0x050 */ + __IO uint32_t START_SEND; /* , Address offset: 0x054 */ + __IO uint32_t INT_EN; /* , Address offset: 0x058 */ + __IO uint32_t INT_STA; /* , Address offset: 0x05C */ + __IO uint32_t RESERVE1[40]; + __IO uint32_t S_WDATA; /* , Address offset: 0x100 */ + __IO uint32_t RESERVE2[63]; + /* (0X200 - 0X100) * 4 / 16 -1 = 63*/ + __I uint32_t S_RDATA; /* , Address offset: 0x200 */ +} FLASH_CTRL_T; + + + +#define FLASH_CTRL ((FLASH_CTRL_T *)FLASH_CTRL_BASE) + +/* + * @brief + */ +#define FC_CC_CONT_EN_SHIFT (20) +#define FC_CC_CONT_EN_MASK (0x1U << FC_CC_CONT_EN_SHIFT) + +#define FC_CC_IO1_SHIFT (16) +#define FC_CC_IO1_MASK (0x3U << FC_CC_IO1_SHIFT) + +#define FC_CC_IO2_SHIFT (12) +#define FC_CC_IO2_MASK (0x3U << FC_CC_IO2_SHIFT) + +#define FC_CC_IO3_SHIFT (8) +#define FC_CC_IO3_MASK (0x3U << FC_CC_IO2_SHIFT) + +typedef enum { + FC_IO_OUTPUT_0, + FC_IO_OUTPUT_1, + FC_IO_OUTPUT_Z +} FC_Io_Output; + +#define FC_CC_PREFETCH_EN_SHIFT (4) +#define FC_CC_PREFETCH_EN_MASK (0x1U << FC_CC_PREFETCH_EN_SHIFT) + +#define FC_CC_IBUS_EN_SHIFT (0) +#define FC_CC_IBUS_EN_MASK (0x1U << FC_CC_IBUS_EN_SHIFT) + +#define FC_ICC_CMD_SHIFT (24) +#define FC_ICC_CMD_MASK (0xFFU << FC_ICC_CMD_SHIFT) + +#define FC_ICC_CMD_BIT_SHIFT (20) +#define FC_ICC_CMD_BIT_MASK (0x3U << FC_ICC_CMD_BIT_SHIFT) + +#define FC_ICC_ADDR_BIT_SHIFT (16) +#define FC_ICC_ADDR_BIT_MASK (0x3U << FC_ICC_ADDR_BIT_SHIFT) + +#define FC_ICC_DUMMY_BIT_SHIFT (12) +#define FC_ICC_DUMMY_BIT_MASK (0x3U << FC_ICC_DUMMY_BIT_SHIFT) + +#define FC_ICC_DUMMY_WIDTH_SHIFT (4) +#define FC_ICC_DUMMY_WIDTH_MASK (0x7FU << FC_ICC_DUMMY_WIDTH_SHIFT) + +#define FC_ICC_DATA_BIT_SHIFT (0) +#define FC_ICC_DATA_BIT_MASK (0x3U << FC_ICC_DATA_BIT_SHIFT) + +typedef enum { + FC_CYCLEBITS_0, + FC_CYCLEBITS_1, + FC_CYCLEBITS_2, + FC_CYCLEBITS_4 +} FC_CycleBits; + +#define FC_IDH_SHIFT (0) +#define FC_IDH_MASK (0xFFFFFFFFU << FC_IDH_SHIFT) + +#define FC_IDL_SHIFT (0) +#define FC_IDL_MASK (0xFFFFFFFFU << FC_IDL_SHIFT) + +#define FC_ICW_BEGIN_SHIFT (16) +#define FC_ICW_BEGIN_MASK (0xFFU << FC_ICW_BEGIN_SHIFT) + +#define FC_ICW_OVER_SHIFT (8) +#define FC_ICW_OVER_MASK (0xFFU << FC_ICW_OVER_SHIFT) + +#define FC_ICW_DESEL_SHIFT (0) +#define FC_ICW_DESEL_MASK (0xFFU << FC_ICW_DESEL_SHIFT) + +#define FC_IIW_CMD_SHIFT (16) +#define FC_IIW_CMD_MASK (0xFFU << FC_IIW_CMD_SHIFT) + +#define FC_IIW_ADDR_SHIFT (8) +#define FC_IIW_ADDR_MASK (0xFFU << FC_IIW_ADDR_SHIFT) + +#define FC_IIW_DUM_SHIFT (0) +#define FC_IIW_DUM_MASK (0xFFU << FC_IIW_DUM_SHIFT) + +#define FC_FCC_WAIT_DATA_SHIFT (12) +#define FC_FCC_WAIT_DATA_MASK (0x3U << FC_FCC_WAIT_DATA_SHIFT) + +#define FC_FCC_CS_SHIFT (8) +#define FC_FCC_CS_MASK (0x1U << FC_FCC_CS_SHIFT) + +typedef enum { + FC_TCTRL_CS_LOW_ENABLE = 0 << FC_FCC_CS_SHIFT, + FC_TCTRL_CS_HIGH_ENABLE = 1 << FC_FCC_CS_SHIFT +} FC_Cs; + +#define FC_FCC_FBS_SHIFT (4) +#define FC_FCC_FBS_MASK (0x1U << FC_FCC_FBS_SHIFT) + +typedef enum { + FC_TCTRL_FBS_MSB = 0 << FC_FCC_FBS_SHIFT, + FC_TCTRL_FBS_LSB = 1 << FC_FCC_FBS_SHIFT +} FC_TCTRL_Fbs; + +#define FC_FCC_CPOL_SHIFT (1) +#define FC_FCC_CPOL_MASK (0x1U << FC_FCC_CPOL_SHIFT) + +#define FC_FCC_CPHA_SHIFT (0) +#define FC_FCC_CPHA_MASK (0x1U << FC_FCC_CPHA_SHIFT) + +typedef enum { + FC_SCLK_Mode0 = 0 << FC_FCC_CPHA_SHIFT, + FC_SCLK_Mode1 = 1 << FC_FCC_CPHA_SHIFT, + FC_SCLK_Mode2 = 2 << FC_FCC_CPHA_SHIFT, + FC_SCLK_Mode3 = 3 << FC_FCC_CPHA_SHIFT +} FC_Sclk_Mode; + +#define FC_XE_SHIFT (0) +#define FC_XE_MASK (0x1U << FC_XE_SHIFT) + + +#define FC_CAC_CS_DESEL_WAIT_SHIFT (4) +#define FC_CAC_CS_DESEL_WAIT_MASK (0xFFU << FC_CAC_CS_DESEL_WAIT_SHIFT) + +#define FC_CAC_TX_FIFO_RESET_SHIFT (3) +#define FC_CAC_TX_FIFO_RESET_MASK (0x1U << FC_CAC_TX_FIFO_RESET_SHIFT) + +#define FC_CAC_RX_FIFO_RESET_SHIFT (2) +#define FC_CAC_RX_FIFO_RESET_MASK (0x1U << FC_CAC_RX_FIFO_RESET_SHIFT) + +#define FC_CAC_WRAP_EN_SHIFT (1) +#define FC_CAC_WRAP_EN_MASK (0x1U << FC_CAC_WRAP_EN_SHIFT) + +#define FC_CAC_ADDR_SIZE_MODE_SHIFT (0) +#define FC_CAC_ADDR_SIZE_MODE_MASK (0x1U << FC_CAC_ADDR_SIZE_MODE_SHIFT) + +typedef enum { + FC_ADDR_MODE_24BIT = 0 << FC_CAC_ADDR_SIZE_MODE_SHIFT, + FC_ADDR_MODE_32BIT = 1 << FC_CAC_ADDR_SIZE_MODE_SHIFT, +} FC_Addr_Mode; + +#define FC_SCC_CMD_SHIFT (24) +#define FC_SCC_CMD_MASK (0xFFU << FC_SCC_CMD_SHIFT) + +#define FC_SCC_CMD_BIT_SHIFT (20) +#define FC_SCC_CMD_BIT_MASK (0x3U << FC_SCC_CMD_BIT_SHIFT) + +#define FC_SCC_ADDR_BIT_SHIFT (16) +#define FC_SCC_ADDR_BIT_MASK (0x3U << FC_SCC_ADDR_BIT_SHIFT) + +#define FC_SCC_DUMMY_BIT_SHIFT (12) +#define FC_SCC_DUMMY_BIT_MASK (0x3U << FC_SCC_DUMMY_BIT_SHIFT) + +#define FC_SCC_DUMMY_DATA_BIT_SHIFT (4) +#define FC_SCC_DUMMY_DATA_BIT_MASK (0x3FU << FC_SCC_DUMMY_DATA_BIT_SHIFT) + +#define FC_SCC_DATA_BIT_SHIFT (0) +#define FC_SCC_DATA_BIT_MASK (0x3U << FC_SCC_DATA_BIT_SHIFT) + +#define FC_SAC_SHIFT (0) +#define FC_SAC_MASK (0xFFFFFFFFU << FC_SAC_SHIFT) + +#define FC_SWN_SHIFT (0) +#define FC_SWN_MASK (0xFFU << FC_SWN_SHIFT) + +#define FC_SRN_SHIFT (0) +#define FC_SRN_MASK (0xFFFFFFFFU << FC_SRN_SHIFT) + +#define FC_SDH_SHIFT (0) +#define FC_SDH_MASK (0xFFFFFFFFU << FC_SDH_SHIFT) + +#define FC_SDL_SHIFT (0) +#define FC_SDL_MASK (0xFFFFFFFFU << FC_SDL_SHIFT) + +#define FC_FTL_WR_FIFO_FULL_SHIFT (24) +#define FC_FTL_WR_FIFO_FULL_MASK (0xFFU << FC_FTL_WR_FIFO_FULL_SHIFT) + +#define FC_FTL_WR_FIFO_EMPTY_SHIFT (16) +#define FC_FTL_WR_FIFO_EMPTY_MASK (0xFFU << FC_FTL_WR_FIFO_EMPTY_SHIFT) + +#define FC_FTL_RD_FIFO_FULL_SHIFT (8) +#define FC_FTL_RD_FIFO_FULL_MASK (0xFFU << FC_FTL_RD_FIFO_FULL_SHIFT) + +#define FC_FTL_RD_FIFO_EMPTY_SHIFT (0) +#define FC_FTL_RD_FIFO_EMPTY_MASK (0xFFU << FC_FTL_RD_FIFO_EMPTY_SHIFT) + +#define FC_FS_STATUS_DGB_SHIFT (24) +#define FC_FS_STATUS_DGB_MASK (0xFU << FC_FS_STATUS_DGB_SHIFT) +#define FC_FS_STATUS_DGB_VMASK (0xFU) + +#define FC_FS_WR_BUF_VALID_SHIFT (23) +#define FC_FS_WR_BUF_VALID_MASK (0x1U << FC_FS_WR_BUF_VALID_SHIFT) + +#define FC_FS_WR_BUF_CNT_SHIFT (20) +#define FC_FS_WR_BUF_CNT_MASK (0x7U << FC_FS_WR_BUF_CNT_SHIFT) +#define FC_FS_WR_BUF_CNT_VMASK (0x7U) + +#define FC_FS_RD_BUF_VALID_SHIFT (19) +#define FC_FS_RD_BUF_VALID_MASK (0x1U << FC_FS_RD_BUF_VALID_SHIFT) + +#define FC_FS_RD_BUF_CNT_SHIFT (16) +#define FC_FS_RD_BUF_CNT_MASK (0x7U << FC_FS_RD_BUF_CNT_SHIFT) +#define FC_FS_RD_BUF_CNT_VMASK (0x7U) + +#define FC_FS_WR_FIFO_CNT_SHIFT (8) +#define FC_FS_WR_FIFO_CNT_MASK (0xFFU << FC_FS_WR_FIFO_CNT_SHIFT) +#define FC_FS_WR_FIFO_CNT_VMASK (0xFFU) + +#define FC_FS_RD_FIFO_CNT_SHIFT (0) +#define FC_FS_RD_FIFO_CNT_MASK (0xFFU << FC_FS_RD_FIFO_CNT_SHIFT) +#define FC_FS_RD_FIFO_CNT_VMASK (0xFFU) + +#define FC_SIW_CMD_SHIFT (16) +#define FC_SIW_CMD_MASK (0xFFU << FC_SIW_CMD_SHIFT) + +#define FC_SIW_ADDR_SHIFT (8) +#define FC_SIW_ADDR_MASK (0xFFU << FC_SIW_ADDR_SHIFT) + +#define FC_SIW_DUMMY_SHIFT (0) +#define FC_SIW_DUMMY_MASK (0xFFU << FC_SIW_DUMMY_SHIFT) + +#define FC_WM_SHIFT (0) +#define FC_WM_MASK (0x1U << FC_WM_SHIFT) + +#define FC_SS_SHIFT (0) +#define FC_SS_MASK (0x1U << FC_SS_SHIFT) + +#define FC_IE_CMPL_SHIFT (12) +#define FC_IE_CMPL_MASK (0x1U << FC_IE_CMPL_SHIFT) + +#define FC_IE_WR_FIFO_UNDERFLOW_SHIFT (11) +#define FC_IE_WR_FIFO_UNDERFLOW_MASK (0x1U << FC_IE_WR_FIFO_UNDERFLOW_SHIFT) + +#define FC_IE_WR_FIFO_OVERFLOW_SHIFT (10) +#define FC_IE_WR_FIFO_OVERFLOW_MASK (0x1U << FC_IE_WR_FIFO_OVERFLOW_SHIFT) + +#define FC_IE_RD_FIFO_UNDERFLOW_SHIFT (9) +#define FC_IE_RD_FIFO_UNDERFLOW_MASK (0x1U << FC_IE_RD_FIFO_UNDERFLOW_SHIFT) + +#define FC_IE_RD_FIFO_OVERFLOW_SHIFT (8) +#define FC_IE_RD_FIFO_OVERFLOW_MASK (0x1U << FC_IE_RD_FIFO_OVERFLOW_SHIFT) + +#define FC_IE_WR_FIFO_FULL_SHIFT (6) +#define FC_IE_WR_FIFO_FULL_MASK (0x1U << FC_IE_WR_FIFO_FULL_SHIFT) + +#define FC_IE_WR_FIFO_EMPTY_SHIFT (5) +#define FC_IE_WR_FIFO_EMPTY_MASK (0x1U << FC_IE_WR_FIFO_EMPTY_SHIFT) + +#define FC_IE_WR_FIFO_READY_SHIFT (4) +#define FC_IE_WR_FIFO_READY_MASK (0x1U << FC_IE_WR_FIFO_READY_SHIFT) + +#define FC_IE_RD_FIFO_FULL_SHIFT (2) +#define FC_IE_RD_FIFO_FULL_MASK (0x1U << FC_IE_RD_FIFO_FULL_SHIFT) + +#define FC_IE_RD_FIFO_EMPTY_SHIFT (1) +#define FC_IE_RD_FIFO_EMPTY_MASK (0x1U << FC_IE_RD_FIFO_EMPTY_SHIFT) + +#define FC_IE_RD_FIFO_READY_SHIFT (0) +#define FC_IE_RD_FIFO_READY_MASK (0x1U << FC_IE_RD_FIFO_READY_SHIFT) + +#define FC_IS_CMPL_SHIFT (12) +#define FC_IS_CMPL_MASK (0x1U << FC_IS_CMPL_SHIFT) + +#define FC_IS_WR_FIFO_UNDERFLOW_SHIFT (11) +#define FC_IS_WR_FIFO_UNDERFLOW_MASK (0x1U << FC_IS_WR_FIFO_UNDERFLOW_SHIFT) + +#define FC_IS_WR_FIFO_OVERFLOW_SHIFT (10) +#define FC_IS_WR_FIFO_OVERFLOW_MASK (0x1U << FC_IS_WR_FIFO_OVERFLOW_SHIFT) + +#define FC_IS_RD_FIFO_UNDERFLOW_SHIFT (9) +#define FC_IS_RD_FIFO_UNDERFLOW_MASK (0x1U << FC_IS_RD_FIFO_UNDERFLOW_SHIFT) + +#define FC_IS_RD_FIFO_OVERFLOW_SHIFT (8) +#define FC_IS_RD_FIFO_OVERFLOW_MASK (0x1U << FC_IS_RD_FIFO_OVERFLOW_SHIFT) + +#define FC_IS_WR_FIFO_FULL_SHIFT (6) +#define FC_IS_WR_FIFO_FULL_MASK (0x1U << FC_IS_WR_FIFO_FULL_SHIFT) + +#define FC_IS_WR_FIFO_EMPTY_SHIFT (5) +#define FC_IS_WR_FIFO_EMPTY_MASK (0x1U << FC_IS_WR_FIFO_EMPTY_SHIFT) + +#define FC_IS_WR_FIFO_READY_SHIFT (4) +#define FC_IS_WR_FIFO_READY_MASK (0x1U << FC_IS_WR_FIFO_READY_SHIFT) + +#define FC_IS_RD_FIFO_FULL_SHIFT (2) +#define FC_IS_RD_FIFO_FULL_MASK (0x1U << FC_IS_RD_FIFO_FULL_SHIFT) + +#define FC_IS_RD_FIFO_EMPTY_SHIFT (1) +#define FC_IS_RD_FIFO_EMPTY_MASK (0x1U << FC_IS_RD_FIFO_EMPTY_SHIFT) + +#define FC_IS_RD_FIFO_READY_SHIFT (0) +#define FC_IS_RD_FIFO_READY_MASK (0x1U << FC_IS_RD_FIFO_READY_SHIFT) + +#define FC_SW_SHIFT (0) +#define FC_SW_MASK (0xFFFFFFFFU << FC_SW_SHIFT) + +#define FC_SR_SHIFT (0) +#define FC_SR_MASK (0xFFFFFFFFU << FC_SR_SHIFT) + + +typedef struct { + uint8_t cs_begin; /*!< cs active to valid clk edge setup minimum time */ + uint8_t cs_over; /*!< valid clk edge to cs active hold minimum time */ + uint8_t cs_deselect; /*!< cs minimum deselect time after read */ + uint8_t cmd_over; + uint8_t addr_over; + uint8_t dummy_over; + uint8_t data; /*!< delay n half cycle */ +} Flash_Ctrl_DelayCycle; + +typedef enum { + XIP_MODE_NORMAL, + XIP_MODE_FAST, + XIP_MODE_DUAL_O, + XIP_MODE_DUAL_IO, + XIP_MODE_QUAD_O, + XIP_MODE_QUAD_IO +} XIP_Mode; + +typedef struct XIP_Instruction{ + uint8_t cmd; /*!< command */ + FC_CycleBits cmd_line; /*!< line mode of command */ + FC_CycleBits addr_line; /*!< line mode of address */ + FC_CycleBits dummy_line; /*!< line mode of dummy */ + FC_CycleBits data_line; /*!< line mode of data */ + uint32_t dum_btyes; /*!< dummy length */ +// uint32_t dummyh; +// uint32_t dummyl; +} XIP_Instruction; + +typedef struct { + uint32_t addr; /*!< XIP code started address in flash */ + uint32_t freq; /*!< flash working frequency */ + Flash_Ctrl_DelayCycle delay; /*!< board delay config */ + XIP_Instruction ins; /*!< XIP read instruction */ + bool cont_mode; /*!< continue mode or not */ +} XIP_Config; + +typedef struct Flashc_Config +{ + uint32_t freq; /*!< flash working frequency */ +// uint32_t t_shsl_ns; /*!< flash t_shsl parameter. for calculate the cs delay. */ +} Flashc_Config; + +typedef struct Flashc_Delay +{ + uint32_t t_shsl_ns; /*!< flash t_shsl parameter. for calculate the cs delay. */ +} Flashc_Delay; + +typedef struct FC_InstructionField +{ + uint8_t *pdata; /*!< instruction field: data */ + uint32_t len; /*!< instruction field: data len */ + FC_CycleBits line; /*!< instruction field: line mode */ +} FC_InstructionField; + +typedef enum Flashc_Commands +{ + /*TODO: tbc...*/ + Flashc_Commands_NOTHING +} Flashc_Commands; + +HAL_Status HAL_Flashc_Xip_Init(XIP_Config *cfg); +HAL_Status HAL_Flashc_Xip_Deinit(); +void HAL_Flashc_Xip_RawEnable(); +void HAL_Flashc_Xip_RawDisable(); +void HAL_Flashc_Xip_Enable(); +void HAL_Flashc_Xip_Disable(); + +HAL_Status HAL_Flashc_Init(const Flashc_Config *cfg); +HAL_Status HAL_Flashc_Deinit(); +HAL_Status HAL_Flashc_Open(); +HAL_Status HAL_Flashc_Close(); +HAL_Status HAL_Flashc_Control(Flashc_Commands op, void *arg); +HAL_Status HAL_Flashc_Write(FC_InstructionField *cmd, FC_InstructionField *addr, FC_InstructionField *dummy, FC_InstructionField *data, bool dma); +HAL_Status HAL_Flashc_Read(FC_InstructionField *cmd, FC_InstructionField *addr, FC_InstructionField *dummy, FC_InstructionField *data, bool dma); + + +#ifdef __cplusplus +} +#endif + + +#endif diff --git a/platform/mcu/xr871/include/driver/chip/hal_global.h b/platform/mcu/xr871/include/driver/chip/hal_global.h new file mode 100644 index 0000000000..b73b7e4458 --- /dev/null +++ b/platform/mcu/xr871/include/driver/chip/hal_global.h @@ -0,0 +1,48 @@ +/** + * @file hal_global.h + * @author XRADIO IOT WLAN Team + */ + +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _DRIVER_CHIP_HAL_GLOBAL_H_ +#define _DRIVER_CHIP_HAL_GLOBAL_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +void HAL_GlobalInit(void); + +#ifdef __cplusplus +} +#endif + +#endif /* _DRIVER_CHIP_HAL_GLOBAL_H_ */ diff --git a/platform/mcu/xr871/include/driver/chip/hal_gpio.h b/platform/mcu/xr871/include/driver/chip/hal_gpio.h new file mode 100644 index 0000000000..1635684300 --- /dev/null +++ b/platform/mcu/xr871/include/driver/chip/hal_gpio.h @@ -0,0 +1,414 @@ +/** + * @file hal_gpio.h + * @author XRADIO IOT WLAN Team + */ + +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _DRIVER_CHIP_HAL_GPIO_H_ +#define _DRIVER_CHIP_HAL_GPIO_H_ + +#include "driver/chip/hal_def.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief GPIO control register block structure + */ +typedef struct { + __IO uint32_t MODE[4]; /* offset: 0x00, GPIO working mode configuration register */ + __IO uint32_t DATA; /* offset: 0x10, GPIO data register */ + __IO uint32_t DRIVING[2]; /* offset: 0x14, GPIO driving level register */ + __IO uint32_t PULL[2]; /* offset: 0x1C, GPIO pull type register */ +} GPIO_CTRL_T; + +/** + * @brief GPIO port definition + */ +typedef enum { + GPIO_PORT_A = 0U, + GPIO_PORT_B = 1U, + GPIO_PORT_NUM +} GPIO_Port; + +/** + * @brief GPIO working mode (function) definition + */ +typedef enum { + GPIOx_Pn_F0_INPUT = 0U, /* for all GPIO pins */ + GPIOx_Pn_F1_OUTPUT = 1U, /* for all GPIO pins */ + GPIOx_Pn_F6_EINT = 6U, /* for all GPIO pins */ + GPIOx_Pn_F7_DISABLE = 7U, /* for all GPIO pins */ + + GPIOA_P0_F2_SPI1_MOSI = 2U, + GPIOA_P0_F3_SD_CMD = 3U, + GPIOA_P0_F4_UART0_TX = 4U, + GPIOA_P0_F5_CSI_D0 = 5U, + GPIOA_P0_F6_EINTA0 = 6U, + + GPIOA_P1_F2_SPI1_MISO = 2U, + GPIOA_P1_F3_SD_DATA0 = 3U, + GPIOA_P1_F4_UART0_RX = 4U, + GPIOA_P1_F5_CSI_D1 = 5U, + GPIOA_P1_F6_EINTA1 = 6U, + + GPIOA_P2_F2_SPI1_CLK = 2U, + GPIOA_P2_F3_SD_CLK = 3U, + GPIOA_P2_F4_I2C1_SCL = 4U, + GPIOA_P2_F5_CSI_D2 = 5U, + GPIOA_P2_F6_EINTA2 = 6U, + + GPIOA_P3_F2_SPI1_CS0 = 2U, + GPIOA_P3_F3_SD_DATA1 = 3U, + GPIOA_P3_F4_I2C1_SDA = 4U, + GPIOA_P3_F5_CSI_D3 = 5U, + GPIOA_P3_F6_EINTA3 = 6U, + + GPIOA_P4_F2_UART1_CTS = 2U, + GPIOA_P4_F3_SD_DATA2 = 3U, + GPIOA_P4_F4_I2C0_SCL = 4U, + GPIOA_P4_F4_CSI_D4 = 5U, + GPIOA_P4_F6_EINTA4 = 6U, + + GPIOA_P5_F2_UART1_RTS = 2U, + GPIOA_P5_F3_SD_DATA3 = 3U, + GPIOA_P5_F4_I2C0_SDA = 4U, + GPIOA_P5_F5_CSI_D5 = 5U, + GPIOA_P5_F6_EINTA5 = 6U, + + GPIOA_P6_F2_UART1_TX = 2U, + GPIOA_P6_F3_SPI1_CS1 = 3U, + GPIOA_P6_F4_I2C0_SCL = 4U, + GPIOA_P6_F5_CSI_D6 = 5U, + GPIOA_P6_F6_EINTA6 = 6U, + + GPIOA_P7_F2_UART1_RX = 2U, + GPIOA_P7_F3_SPI1_CS2 = 3U, + GPIOA_P7_F4_I2C0_SDA = 4U, + GPIOA_P7_F5_CSI_D7 = 5U, + GPIOA_P7_F6_EINTA7 = 6U, + + GPIOA_P8_F2_ADC_CH0 = 2U, + GPIOA_P8_F3_PWM0_ECT0 = 3U, + GPIOA_P8_F4_I2C1_SCL = 4U, + GPIOA_P8_F5_CSI_PCLK = 5U, + GPIOA_P8_F6_EINTA8 = 6U, + + GPIOA_P9_F2_ADC_CH1 = 2U, + GPIOA_P9_F3_PWM1_ECT1 = 3U, + GPIOA_P9_F4_I2C1_SDA = 4U, + GPIOA_P9_F5_CSI_MCLK = 5U, + GPIOA_P9_F6_EINTA9 = 6U, + + GPIOA_P10_F2_ADC_CH2 = 2U, + GPIOA_P10_F3_PWM2_ECT2 = 3U, + GPIOA_P10_F4_DMIC_CLK = 4U, + GPIOA_P10_F5_CSI_HSYNC = 5U, + GPIOA_P10_F6_EINTA10 = 6U, + + GPIOA_P11_F2_ADC_CH3 = 2U, + GPIOA_P11_F3_PWM3_ECT3 = 3U, + GPIOA_P11_F4_DMIC_DATA = 4U, + GPIOA_P11_F5_CSI_VSYNC = 5U, + GPIOA_P11_F6_EINTA11 = 6U, + + GPIOA_P12_F2_ADC_CH4 = 2U, + GPIOA_P12_F3_PWM4_ECT4 = 3U, + GPIOA_P12_F4_I2S_MCLK = 4U, + GPIOA_P12_F5_IR_TX = 5U, + GPIOA_P12_F6_EINTA12 = 6U, + + GPIOA_P13_F2_ADC_CH5 = 2U, + GPIOA_P13_F3_PWM5_ECT5 = 3U, + GPIOA_P13_F4_I2S_BCLK = 4U, + GPIOA_P13_F6_EINTA13 = 6U, + + GPIOA_P14_F2_ADC_CH6 = 2U, + GPIOA_P14_F3_PWM6_ECT6 = 3U, + GPIOA_P14_F4_I2S_DI = 4U, + GPIOA_P14_F5_IR_RX = 5U, + GPIOA_P14_F6_EINTA14 = 6U, + + GPIOA_P15_F2_ADC_CH7 = 2U, + GPIOA_P15_F3_PWM7_ECT7 = 3U, + GPIOA_P15_F4_I2S_DO = 4U, + GPIOA_P15_F5_UART1_CTS = 5U, + GPIOA_P15_F6_EINTA15 = 6U, + + GPIOA_P16_F2_IR_TX = 2U, + GPIOA_P16_F3_IR_RX = 3U, + GPIOA_P16_F4_I2S_LRCLK = 4U, + GPIOA_P16_F5_UART1_RTS = 5U, + GPIOA_P16_F6_EINTA16 = 6U, + + GPIOA_P17_F2_I2C0_SCL = 2U, + GPIOA_P17_F3_IR_RX = 3U, + GPIOA_P17_F4_I2C1_SCL = 4U, + GPIOA_P17_F5_UART1_TX = 5U, + GPIOA_P17_F6_EINTA17 = 6U, + + GPIOA_P18_F2_I2C0_SDA = 2U, + GPIOA_P18_F3_IR_TX = 3U, + GPIOA_P18_F4_I2C1_SDA = 4U, + GPIOA_P18_F5_UART1_RX = 5U, + GPIOA_P18_F6_EINTA18 = 6U, + + GPIOA_P19_F2_NUART_CTS = 2U, + GPIOA_P19_F3_NSWD_TMS = 3U, + GPIOA_P19_F4_PWM0_ECT0 = 4U, + GPIOA_P19_F5_SPI1_MOSI = 5U, + GPIOA_P19_F6_EINTA19 = 6U, + + GPIOA_P20_F2_NUART_RTS = 2U, + GPIOA_P20_F3_NSWD_TCK = 3U, + GPIOA_P20_F4_PWM1_ECT1 = 4U, + GPIOA_P20_F5_SPI1_MISO = 5U, + GPIOA_P20_F6_EINTA20 = 6U, + + GPIOA_P21_F2_NUART_TX = 2U, + GPIOA_P21_F3_DMIC_CLK = 3U, + GPIOA_P21_F4_PWM2_ECT2 = 4U, + GPIOA_P21_F5_SPI1_CLK = 5U, + GPIOA_P21_F6_EINTA21 = 6U, + + GPIOA_P22_F2_NUART_RX = 2U, + GPIOA_P22_F3_DMIC_DATA = 3U, + GPIOA_P22_F4_PWM3_ECT3 = 4U, + GPIOA_P22_F5_SPI1_CS0 = 5U, + GPIOA_P22_F6_EINTA22 = 6U, + + GPIOB_P0_F2_UART0_TX = 2U, + GPIOB_P0_F3_JTAG_TMS = 3U, + GPIOB_P0_F4_PWM4_ECT4 = 4U, + GPIOB_P0_F5_SWD_TMS = 5U, + GPIOB_P0_F6_EINTB0 = 6U, + + GPIOB_P1_F2_UART0_RX = 2U, + GPIOB_P1_F3_JTAG_TCK = 3U, + GPIOB_P1_F4_PWM5_ECT5 = 4U, + GPIOB_P1_F5_SWD_TCK = 5U, + GPIOB_P1_F6_EINTB1 = 6U, + + GPIOB_P2_F2_SWD_TMS = 2U, + GPIOB_P2_F3_JTAG_TD0 = 3U, + GPIOB_P2_F4_PWM6_ECT6 = 4U, + GPIOB_P2_F5_FLASH_WP = 5U, + GPIOB_P2_F6_EINTB2 = 6U, + + GPIOB_P3_F2_SWD_TCK = 2U, + GPIOB_P3_F3_JTAG_TDI = 3U, + GPIOB_P3_F4_PWM7_ECT7 = 4U, + GPIOB_P3_F5_FLASH_HOLD = 5U, + GPIOB_P3_F6_EINTB3 = 6U, + + GPIOB_P4_F2_SPI0_MOSI = 2U, + GPIOB_P4_F3_SD_CMD = 3U, + GPIOB_P4_F5_FLASH_MOSI = 5U, + GPIOB_P4_F6_EINTB4 = 6U, + + GPIOB_P5_F2_SPI0_MISO = 2U, + GPIOB_P5_F3_SD_DATA0 = 3U, + GPIOB_P5_F5_FLASH_MISO = 5U, + GPIOB_P5_F6_EINTB5 = 6U, + + GPIOB_P6_F2_SPI0_CS0 = 2U, + GPIOB_P6_F5_FLASH_CS = 5U, + GPIOB_P6_F6_EINTB6 = 6U, + + GPIOB_P7_F2_SPI0_CLK = 2U, + GPIOB_P7_F3_SD_CLK = 3U, + GPIOB_P7_F5_FLASH_CLK = 5U, + GPIOB_P7_F6_EINTB7 = 6U, +} GPIO_WorkMode; + +/** + * @brief GPIO driving level definition + */ +typedef enum { + GPIO_DRIVING_LEVEL_0 = 0U, + GPIO_DRIVING_LEVEL_1 = 1U, + GPIO_DRIVING_LEVEL_2 = 2U, + GPIO_DRIVING_LEVEL_3 = 3U +} GPIO_DrivingLevel; + +/** + * @brief GPIO pull type definition + */ +typedef enum { + GPIO_PULL_NONE = 0U, + GPIO_PULL_UP = 1U, + GPIO_PULL_DOWN = 2U +} GPIO_PullType; + +/** + * @brief GPIO interrupt register block structure + */ +typedef struct { + __IO uint32_t IRQ_MODE[4]; /* offset: 0x00, GPIO interrupt configuration register */ + __IO uint32_t IRQ_EN; /* offset: 0x10, GPIO interrupt enable register */ + __IO uint32_t IRQ_STATUS; /* offset: 0x14, GPIO interrupt status register */ + __IO uint32_t IRQ_DEBOUNCE; /* offset: 0x18, GPIO interrupt debounce register */ +} GPIO_IRQ_T; + +/** + * @brief GPIO interrupt trigger event definition + */ +typedef enum { + GPIO_IRQ_EVT_RISING_EDGE = 0U, + GPIO_IRQ_EVT_FALLING_EDGE = 1U, + GPIO_IRQ_EVT_HIGH_LEVEL = 2U, + GPIO_IRQ_EVT_LOW_LEVEL = 3U, + GPIO_IRQ_EVT_BOTH_EDGE = 4U +} GPIO_IrqEvent; + +/* GPIO interrupt debounce value */ +#define GPIO_IRQ_DEB_CLK_SCALE_SHIFT 4 /* R/W */ +#define GPIO_IRQ_DEB_CLK_SCALE_MASK (0x7U << GPIO_IRQ_DEB_CLK_SCALE_SHIFT) + +#define GPIO_IRQ_DEB_CLK_SRC_SHIFT 0 /* R/W */ +#define GPIO_IRQ_DEB_CLK_SRC_MASK (0x1U << GPIO_IRQ_DEB_CLK_SRC_SHIFT) +typedef enum { + GPIO_IRQ_DEB_CLK_SRC_LFCLK = 0U, + GPIO_IRQ_DEB_CLK_SRC_HFCLK = 1U +} GPIO_IrqDebounceClkSrc; + +/******************************************************************************/ + +/** + * @brief GPIO pin state definition + */ +typedef enum { + GPIO_PIN_LOW = 0, + GPIO_PIN_HIGH = 1, +} GPIO_PinState; + +/** + * @brief GPIO pin number definition + * - GPIOA: pin [0:23] + * - GPIOB: pin [0:7] + */ +typedef enum { + GPIO_PIN_0 = 0U, + GPIO_PIN_1 = 1U, + GPIO_PIN_2 = 2U, + GPIO_PIN_3 = 3U, + GPIO_PIN_4 = 4U, + GPIO_PIN_5 = 5U, + GPIO_PIN_6 = 6U, + GPIO_PIN_7 = 7U, + GPIO_PIN_8 = 8U, + GPIO_PIN_9 = 9U, + GPIO_PIN_10 = 10U, + GPIO_PIN_11 = 11U, + GPIO_PIN_12 = 12U, + GPIO_PIN_13 = 13U, + GPIO_PIN_14 = 14U, + GPIO_PIN_15 = 15U, + GPIO_PIN_16 = 16U, + GPIO_PIN_17 = 17U, + GPIO_PIN_18 = 18U, + GPIO_PIN_19 = 19U, + GPIO_PIN_20 = 20U, + GPIO_PIN_21 = 21U, + GPIO_PIN_22 = 22U, + + GPIOA_PIN_NUM = 23U, + GPIOB_PIN_NUM = 8U +} GPIO_Pin; + +/** + * @brief Wakeup I/O of GPIO port A definition + */ +typedef enum { + WAKEUP_IO0 = GPIO_PIN_4, + WAKEUP_IO1 = GPIO_PIN_5, + WAKEUP_IO2 = GPIO_PIN_6, + WAKEUP_IO3 = GPIO_PIN_7, + WAKEUP_IO4 = GPIO_PIN_17, + WAKEUP_IO5 = GPIO_PIN_18, + WAKEUP_IO6 = GPIO_PIN_19, + WAKEUP_IO7 = GPIO_PIN_20, + WAKEUP_IO8 = GPIO_PIN_21, + WAKEUP_IO9 = GPIO_PIN_22, + WAKEUP_IO_MAX = 10, /* keep last */ +} WAKEUP_IO; + +/** + * @brief GPIO initialization parameters + */ +typedef struct { + GPIO_WorkMode mode; + GPIO_DrivingLevel driving; + GPIO_PullType pull; +} GPIO_InitParam; + +/** + * @brief GPIO pinmux configuration parameters + */ +typedef struct { + GPIO_Port port; + GPIO_Pin pin; + GPIO_InitParam config; +} GPIO_PinMuxParam; + +/** @brief Type define of GPIO IRQ callback function */ +typedef void (*GPIO_IRQCallback) (void *arg); + +/** + * @brief GPIO interrupt enable parameters + */ +typedef struct { + GPIO_IrqEvent event; + GPIO_IRQCallback callback; + void *arg; +} GPIO_IrqParam; + +void HAL_GPIO_Init(GPIO_Port port, GPIO_Pin pin, const GPIO_InitParam *param); +void HAL_GPIO_DeInit(GPIO_Port port, GPIO_Pin pin); + +void HAL_GPIO_WritePin(GPIO_Port port, GPIO_Pin pin, GPIO_PinState state); +GPIO_PinState HAL_GPIO_ReadPin(GPIO_Port port, GPIO_Pin pin); + +void HAL_GPIO_WritePort(GPIO_Port port, uint32_t portMask); +uint32_t HAL_GPIO_ReadPort(GPIO_Port port); + +void HAL_GPIO_EnableIRQ(GPIO_Port port, GPIO_Pin pin, const GPIO_IrqParam *param); +void HAL_GPIO_DisableIRQ(GPIO_Port port, GPIO_Pin pin); + +void HAL_GPIO_PinMuxConfig(const GPIO_PinMuxParam *param, uint32_t count); +void HAL_GPIO_PinMuxDeConfig(const GPIO_PinMuxParam *param, uint32_t count); + +#ifdef __cplusplus +} +#endif + +#endif /* _DRIVER_CHIP_HAL_GPIO_H_ */ diff --git a/platform/mcu/xr871/include/driver/chip/hal_i2c.h b/platform/mcu/xr871/include/driver/chip/hal_i2c.h new file mode 100644 index 0000000000..bd5d2ef704 --- /dev/null +++ b/platform/mcu/xr871/include/driver/chip/hal_i2c.h @@ -0,0 +1,190 @@ +/** + * @file hal_i2c.h + * @author XRADIO IOT WLAN Team + */ + +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _DRIVER_CHIP_HAL_I2C_H_ +#define _DRIVER_CHIP_HAL_I2C_H_ + +#include "driver/chip/hal_def.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief I2C ID definition + */ +typedef enum { + I2C0_ID = 0, + I2C1_ID = 1, + I2C_NUM = 2 +} I2C_ID; + +/** + * @brief I2C register block structure + */ +typedef struct { + __IO uint32_t I2C_ADDR; /* offset: 0x00, I2C slave address register */ + __IO uint32_t I2C_XADDR; /* offset: 0x04, I2C extend slave address register */ + __IO uint32_t I2C_DATA; /* offset: 0x08, I2C data register */ + __IO uint32_t I2C_CTRL; /* offset: 0x0C, I2C control register */ + __I uint32_t I2C_STATUS; /* offset: 0x10, I2C status register */ + __IO uint32_t I2C_CLK_CTRL; /* offset: 0x14, I2C clock register */ + __IO uint32_t I2C_SOFT_RST; /* offset: 0x18, I2C software reset register */ + __IO uint32_t I2C_EFR; /* offset: 0x1C, I2C enhance feature register */ + __IO uint32_t I2C_LINE_CTRL; /* offset: 0x20, I2C line control register */ +} I2C_T; + +#define I2C0 ((I2C_T *)I2C0_BASE) /* address: 0x40041C00 */ +#define I2C1 ((I2C_T *)I2C1_BASE) /* address: 0x40042000 */ + +/* I2Cx->I2C_ADDR, R/W */ +#define I2C_SLAVE_ADDR_SHIFT 1 +#define I2C_SLAVE_ADDR_MASK (0x7FU << I2C_SLAVE_ADDR_SHIFT) + +#define I2C_GCE_BIT HAL_BIT(0) + +/* I2Cx->I2C_XADDR, R/W */ +#define I2C_SLAVE_XADDR_MASK 0xFFU + +/* I2Cx->I2C_DATA, R/W */ +#define I2C_DATA_MASK 0xFFU + +/* I2Cx->I2C_CTRL, R/W */ +#define I2C_IRQ_EN_BIT HAL_BIT(7) +#define I2C_BUS_EN_BIT HAL_BIT(6) +#define I2C_START_BIT HAL_BIT(5) +#define I2C_STOP_BIT HAL_BIT(4) +#define I2C_IRQ_FLAG_BIT HAL_BIT(3) +#define I2C_ACK_EN_BIT HAL_BIT(2) + +#define I2C_WR_CTRL_MASK (I2C_START_BIT | I2C_STOP_BIT | I2C_IRQ_FLAG_BIT) + +/* I2Cx->I2C_STATUS, R */ +#define I2C_STATUS_MASK 0xFFU + +typedef enum { + I2C_BUS_ERROR = 0x00U, /* bus error */ + I2C_START_TRAN = 0x08U, /* start condition transmitted */ + I2C_RE_START_TRAN = 0x10U, /* repeated start condition transmitted */ + I2C_ADDR_WR_TRAN_ACK = 0x18U, /* address + write bit transmitted, ACK received */ + I2C_ADDR_WR_TRAN_NACK = 0x20U, /* address + write bit transmitted, ACK not received */ + I2C_MASTER_DATA_TRAN_ACK = 0x28U, /* data byte transmitted in master mode, ACK received */ + I2C_MASTER_DATA_TRAN_NACK = 0x30U, /* data byte transmitted in master mode, ACK not received */ + I2C_ARB_LOST = 0x38U, /* arbitration lost in address or data byte */ + I2C_ADDR_RD_TRAN_ACK = 0x40U, /* address + read bit transmitted, ACK received */ + I2C_ADDR_RD_TRAN_NACK = 0x48U, /* address + read bit transmitted, ACK not received */ + I2C_MASTER_DATA_RECV_ACK = 0x50U, /* data byte received in master mode, ACK transmitted */ + I2C_MASTER_DATA_RECV_NACK = 0x58U, /* data byte received in master mode, ACK not transmitted */ + I2C_ADDR_WR_RECV_ACK = 0x60U, /* slave address + write bit received, ACK transmitted */ + I2C_ARB_LOST_ADDR_WR = 0x68U, /* arbitration lost in address as master, slave address + write bit received, ACK transmitted */ + I2C_GC_ADDR_RECV = 0x70U, /* general call adress received, ACK transmitted */ + I2C_ARB_LOST_GC_ADDR = 0x78U, /* arbitration lost in address as master, general call address received, ACK transmitted */ + I2C_DATA_RECV_SLAVE_ACK = 0x80U, /* data byte received after slave address received, ACK transmitted */ + I2C_DATA_RECV_SLAVE_NACK = 0x88U, /* data byte reveived after slave address received, ACK not transmitted */ + I2C_DATA_RECV_GC_ACK = 0x90U, /* data byte received after general call received, ACK transmitted */ + I2C_DATA_RECV_GC_NACK = 0x98U, /* data byte received after general call received, ACK not transmitted */ + I2C_STOP_RE_START_RECV = 0xA0U, /* stop or repeated start condition received in slave mode */ + I2C_ADDR_RD_RECV_ACK = 0xA8U, /* slave address + read bit received, ACK transmitted */ + I2C_ARB_LOST_ADDR_RD = 0xB0U, /* arbitration lost in address as master, slave address + read bit received, ACK transmitted */ + I2C_SLAVE_DATA_TRAN_ACK = 0xB8U, /* data byte transmitted in slave mode, ACK received */ + I2C_SLAVE_DATA_TRAN_NACK = 0xC0U, /* data byte transmitted in slave mode, ACK not received */ + I2C_SLAVE_LAST_TRAN_ACK = 0xC8U, /* last byte transmitted in slave mode, ACK received */ + I2C_SEC_ADDR_WR_ACK = 0xD0U, /* second address byte + write bit transmitted, ACK received */ + I2C_SEC_ADDR_WR_NACK = 0xD8U, /* second address byte + write bit transmitted, ACK not received */ + I2C_NO_STATUS_INFO = 0xF8U /* no relevant status information, IRQ_FLAG = 0 */ +} I2C_IRQStatus; + +/* I2Cx->I2C_CLK_CTRL, R/W */ +#define I2C_CLK_M_SHIFT 3 +#define I2C_CLK_M_MASK (0xFU << I2C_CLK_M_SHIFT) +#define I2C_CLK_M_MAX 15 + +#define I2C_CLK_N_SHIFT 0 +#define I2C_CLK_N_MASK (0x7U << I2C_CLK_N_SHIFT) +#define I2C_CLK_N_MAX 7 + +/* I2Cx->I2C_SOFT_RST, R/W */ +#define I2C_SOFT_RST_BIT HAL_BIT(0) + +/* I2Cx->I2C_EFR, R/W */ +#define I2C_DATA_BYTE_MASK 0x3U +typedef enum { + I2C_DATA_BYTE_0 = 0U, + I2C_DATA_BYTE_1 = 1U, + I2C_DATA_BYTE_2 = 2U, + I2C_DATA_BYTE_3 = 3U +} I2C_DataByte; + +/* I2Cx->I2C_LINE_CTRL, R/W */ +#define I2C_SCL_STATE_BIT HAL_BIT(5) +#define I2C_SDA_STATE_BIT HAL_BIT(4) +#define I2C_SCL_CTRL_BIT HAL_BIT(3) +#define I2C_SCL_CTRL_EN_BIT HAL_BIT(2) +#define I2C_SDA_CTRL_BIT HAL_BIT(1) +#define I2C_SDA_CTRL_EN_BIT HAL_BIT(0) + +/******************************************************************************/ + +/** + * @brief I2C addressing mode + */ +typedef enum { + I2C_ADDR_MODE_7BIT = 0U, /* 7-bit addressing mode */ + I2C_ADDR_MODE_10BIT = 1U /* 10-bit addressing mode */ +} I2C_AddrMode; + +/** + * @brief I2C initialization parameters + */ +typedef struct { + I2C_AddrMode addrMode; /* addressing mode */ + uint32_t clockFreq; /* clock frequency */ +} I2C_InitParam; + +HAL_Status HAL_I2C_Init(I2C_ID i2cID, const I2C_InitParam *initParam); +HAL_Status HAL_I2C_DeInit(I2C_ID i2cID); + +int32_t HAL_I2C_Master_Transmit_IT(I2C_ID i2cID, uint16_t devAddr, uint8_t *buf, int32_t size); +int32_t HAL_I2C_Master_Receive_IT(I2C_ID i2cID, uint16_t devAddr, uint8_t *buf, int32_t size); +int32_t HAL_I2C_Master_Transmit_Mem_IT(I2C_ID i2cID, uint16_t devAddr, uint8_t memAddr, uint8_t *buf, int32_t size); +int32_t HAL_I2C_Master_Receive_Mem_IT(I2C_ID i2cID, uint16_t devAddr, uint8_t memAddr, uint8_t *buf, int32_t size); + +int32_t HAL_I2C_SCCB_Master_Transmit_IT(I2C_ID i2cID, uint8_t devAddr, uint8_t subAddr, uint8_t *buf); +int32_t HAL_I2C_SCCB_Master_Receive_IT(I2C_ID i2cID, uint8_t devAddr, uint8_t subAddr, uint8_t *buf); + +#ifdef __cplusplus +} +#endif + +#endif /* _DRIVER_CHIP_HAL_I2C_H_ */ diff --git a/platform/mcu/xr871/include/driver/chip/hal_i2s.h b/platform/mcu/xr871/include/driver/chip/hal_i2s.h new file mode 100644 index 0000000000..ee9198bcb5 --- /dev/null +++ b/platform/mcu/xr871/include/driver/chip/hal_i2s.h @@ -0,0 +1,523 @@ +/** + * @file hal_i2s.h + * @author XRADIO IOT WLAN Team + */ + +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _DRIVER_CHIP_HAL_I2S_H_ +#define _DRIVER_CHIP_HAL_I2S_H_ + +#include +#include "driver/chip/hal_def.h" +#include "driver/chip/hal_gpio.h" +#include "driver/chip/hal_audio.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Digital Audio Controller + */ +typedef struct +{ + __IO uint32_t DA_CTL; /* I2S Control Register, Address offset: 0x00 */ + __IO uint32_t DA_FMT0; /* I2S Format Register0, Address offset: 0x04 */ + __IO uint32_t DA_FMT1; /* I2S Format Register1, Address offset: 0x08 */ + __IO uint32_t DA_ISTA; /* I2S Interrupt Status Register, Address offset: 0x0C */ + uint32_t DA_RXFIFO; /* I2S RX FIFO Register, Address offset: 0x10 */ + __IO uint32_t DA_FCTL; /* I2S FIFO Control Register, Address offset: 0x14 */ + __I uint32_t DA_FSTA; /* I2S FIFO Stauts Register, Address offset: 0x18 */ + __IO uint32_t DA_INT; /* I2S DMA&Interrupt Control Register, Address offset: 0x1C */ + __O uint32_t DA_TXFIFO; /* I2S TX FIFO Register, Address offset: 0x20 */ + __IO uint32_t DA_CLKD; /* I2S Clock Divide Register, ddress offset: 0x24 */ + __IO uint32_t DA_TXCNT; /* I2S TX Sample Counter Register, Address offset: 0x28 */ + __IO uint32_t DA_RXCNT; /* I2S RX Sample Counter Register, Address offset: 0x2C */ + __IO uint32_t DA_CHCFG; /* I2S Channel Configuration Register, Address offset: 0x30 */ + __IO uint32_t DA_TX0CHSEL; /* I2S TX0 Channel Select Register, Address offset: 0x34 */ + __IO uint32_t DA_TX1CHSEL; /* I2S TX1 Channel Select Register, Address offset: 0x38 */ + __IO uint32_t DA_TX2CHSEL; /* I2S TX2 Channel Select Register, Address offset: 0x3C */ + __IO uint32_t DA_TX3CHSEL; /* I2S TX3 Channel Select Register, Address offset: 0x40 */ + __IO uint32_t DA_TX0CHMAP; /* I2S TX0 Channel Mapping Register, Address offset: 0x44 */ + __IO uint32_t DA_TX1CHMAP; /* I2S TX1 Channel Mapping Register, Address offset: 0x48 */ + __IO uint32_t DA_TX2CHMAP; /* I2S TX2 Channel Mapping Register, Address offset: 0x4C */ + __IO uint32_t DA_TX3CHMAP; /* I2S TX3 Channel Mapping Register, Address offset: 0x50 */ + __IO uint32_t DA_RXCHSEL; /* I2S RX Channel Select Register, Address offset: 0x54 */ + __IO uint32_t DA_RXCHMAP; /* I2S RX Channel Mapping Register, Address offset: 0x58 */ +} I2S_T; + +#define I2S ((I2S_T *)I2S_BASE) + +/* + * Bits definition for I2S_CTL Control Register (0X0000) + */ +#define I2S_GLOBE_EN_BIT HAL_BIT(0) +#define I2S_RX_EN_BIT HAL_BIT(1) +#define I2S_TX_EN_BIT HAL_BIT(2) +#define I2S_LOOP_TSET_EN_EN_BIT HAL_BIT(3) + +#define I2S_MODE_SEL_SHIFT (4) +#define I2S_MODE_SEL_MASK (0x3 << I2S_MODE_SEL_SHIFT) +typedef enum { + I2S_PCM_MODE = (0x0U << I2S_MODE_SEL_SHIFT), + I2S_LEFT_MODE = (0x1U << I2S_MODE_SEL_SHIFT), + I2S_RIGHT_MODE = (0x2U << I2S_MODE_SEL_SHIFT), + RESERVE_VAL = (0x3U << I2S_MODE_SEL_SHIFT) +} I2S_Mode; + +#define I2S_OUT_MUTE_SHIFT (6) +#define I2S_OUT_MUTE_MASK (0x1U << I2S_OUT_MUTE_SHIFT) +typedef enum { + I2S_NOR_TRANSFER = (0x0U << I2S_OUT_MUTE_SHIFT), + I2S_OUT_MUTE = (0x1U << I2S_OUT_MUTE_SHIFT), +} I2S_DoutMute; + +#define I2S_SDO0_EN_BIT HAL_BIT(8) + +#define I2S_LRCK_OUT_SHIFT (17) +#define I2S_LRCK_OUT_MASK (0x1U << I2S_LRCK_OUT_SHIFT) +typedef enum { + I2S_LRCK_INPUT = (0x0U << I2S_LRCK_OUT_SHIFT), + I2S_LRCK_OUTPUT = (0x1U << I2S_LRCK_OUT_SHIFT), +} I2S_LrckMode; + +#define I2S_BCLK_OUT_SHIFT (18) +#define I2S_BCLK_OUT_MASK (0x1U << I2S_BCLK_OUT_SHIFT) +typedef enum { + I2S_BCLK_INPUT = (0x0U << I2S_BCLK_OUT_SHIFT), + I2S_BCLK_OUTPUT = (0x1U << I2S_BCLK_OUT_SHIFT), +} I2S_BclkMode; + +/* + * Bits definition for I2S_FMT0 Format Register0(0x0004) + */ +#define I2S_SW_SEC_SHIFT (0) +#define I2S_SW_SEC_MASK (0x1U << I2S_SW_SEC_SHIFT) +typedef enum { + I2S_SLOT_WIDTH_BIT8 = (0x1U << I2S_SW_SEC_SHIFT), + I2S_SLOT_WIDTH_BIT12 = (0x2U << I2S_SW_SEC_SHIFT), + I2S_SLOT_WIDTH_BIT16 = (0x3U << I2S_SW_SEC_SHIFT), + I2S_SLOT_WIDTH_BIT20 = (0x4U << I2S_SW_SEC_SHIFT), + I2S_SLOT_WIDTH_BIT24 = (0x5U << I2S_SW_SEC_SHIFT), + I2S_SLOT_WIDTH_BIT28 = (0x6U << I2S_SW_SEC_SHIFT), + I2S_SLOT_WIDTH_BIT32 = (0x7U << I2S_SW_SEC_SHIFT), +} I2S_SlotWidth; + +#define I2S_EDGE_TRANSFER_SHIFT (3) +#define I2S_EDGE_TRANSFER_MASK (0x1U << I2S_EDGE_TRANSFER_SHIFT) +typedef enum { + I2S_SEC_NEGATIVE_EDGE = (0x0U << I2S_EDGE_TRANSFER_SHIFT), + I2S_SEC_POSITIVE_EDGE = (0x1U << I2S_EDGE_TRANSFER_SHIFT), +} I2S_EdgeTransfer; + +#define I2S_SR_SHIFT (4) +#define I2S_SR_MASK (0x7U << I2S_SR_SHIFT) +/** + * @brief sampling accuracy + */ +typedef enum { + I2S_SR8BIT = (0x1U << I2S_SR_SHIFT), + I2S_SR12BIT = (0x2U << I2S_SR_SHIFT), + I2S_SR16BIT = (0x3U << I2S_SR_SHIFT), + I2S_SR20BIT = (0x4U << I2S_SR_SHIFT), + I2S_SR24BIT = (0x5U << I2S_SR_SHIFT), + I2S_SR28BIT = (0x6U << I2S_SR_SHIFT), + I2S_SR32BIT = (0x7U << I2S_SR_SHIFT), +} I2S_SampleResolution; + +#define I2S_BCLK_POLARITY_SHIFT (7) +#define I2S_BCLK_POLARITY_MASK (0x1U << I2S_BCLK_POLARITY_SHIFT) +typedef enum { + I2S_SEC_NOR_MODE = (0x0U << I2S_BCLK_POLARITY_SHIFT), + I2S_SEC_INVERT_MODE = (0x1U << I2S_BCLK_POLARITY_SHIFT), +} I2S_BclkPolarity; + +#define I2S_LRCK_PERIOD_SHIFT (8) +#define I2S_LRCK_PERIOD_MASK (0x3FFU << I2S_LRCK_PERIOD_SHIFT) +#define I2S_LRCK_PERIOD(n) ((n-1) << I2S_LRCK_PERIOD_SHIFT) + +#define I2S_LRCK_POLARITY_SHIFT (19) +#define I2S_LRCK_POLARITY_MASK (0x1U << I2S_LRCK_POLARITY_SHIFT) +typedef enum { + I2S_LEFTCHANNEL_LRCKLOW = (0x0U << I2S_LRCK_POLARITY_SHIFT), + I2S_LEFTCHANNEL_LRCKHIGH = (0x1U << I2S_BCLK_POLARITY_SHIFT), +} I2S_LrckPolarity; + + +#define I2S_LRCK_WIDTH_SHIFT (30) +#define I2S_LRCK_WIDTH_MASK (0x1U << I2S_LRCK_WIDTH_SHIFT) +/** + * @brief frame mode + */ +typedef enum { + I2S_SHORT_FRAME = (0x0U << I2S_LRCK_WIDTH_SHIFT), + I2S_LONG_FRAME = (0x1U << I2S_LRCK_WIDTH_SHIFT), +} I2S_FrameMode; + +/* + * Bits definition for I2S_FMT1 Format Register1 (0X0008) + */ +#define I2S_PCM_TXMODE_SHIFT (0) +#define I2S_PCM_TXMODE_MASK (0x3U << I2S_PCM_TXMODE_SHIFT) +typedef enum { + I2S_TX_LINEAR_PCM = (0x0U << I2S_PCM_TXMODE_SHIFT), + I2S_TX_PCM_NOVAL = (0x1U << I2S_PCM_TXMODE_SHIFT), + I2S_TX_PCM_ULAW_BIT8 = (0x2U << I2S_PCM_TXMODE_SHIFT), + I2S_TX_PCM_ALAW_BIT8 = (0x3U << I2S_PCM_TXMODE_SHIFT), +} I2S_TXPDM; + +#define I2S_PCM_RXMODE_SHIFT (2) +#define I2S_PCM_RXMODE_MASK (0x3U << I2S_PCM_RXMODE_SHIFT) +typedef enum { + I2S_RX_LINEAR_PCM = (0U << I2S_PCM_RXMODE_SHIFT), + I2S_RX_PCM_NOVAL = (1U << I2S_PCM_RXMODE_SHIFT), + I2S_RX_PCM_ULAW_BIT8 = (2U << I2S_PCM_RXMODE_SHIFT), + I2S_RX_PCM_ALAW_BIT8 = (3U << I2S_PCM_RXMODE_SHIFT), +} I2S_RXPDM; + +#define I2S_SEXT_SHIFT (4) +#define I2S_SEXT_MASK (0x3U << I2S_SEXT_SHIFT) +typedef enum { + I2S_PADDING_LSB = (0x0U << I2S_SEXT_SHIFT), + I2S_SIGN_MSB = (0x1U << I2S_SEXT_SHIFT), + I2S_NO_SEXT_VAL = (0x2U << I2S_SEXT_SHIFT), + I2S_ZERO_SLOT = (0x3U << I2S_SEXT_SHIFT), +} I2S_SEXT; + +#define I2S_TX_MLS_SHIFT (6) +#define I2S_TX_MLS_MASK (0x1U << I2S_TX_MLS_SHIFT) +typedef enum { + I2S_TX_MSB_FIRST = (0x0U << I2S_TX_MLS_SHIFT), + I2S_TX_LSB_FIRST = (0x1U << I2S_TX_MLS_SHIFT), +} I2S_TxMLS; + +#define I2S_RX_MLS_SHIFT (7) +#define I2S_RX_MLS_MASK (0x1U << I2S_RX_MLS_SHIFT) +typedef enum { + I2S_RX_MSB_FIRST = (0x0U << I2S_RX_MLS_SHIFT), + I2S_RX_LSB_FIRST = (0x1U << I2S_RX_MLS_SHIFT), +} I2S_RxMLS; + +/* + * Bits definition for I2S_ISTA Interrupt Status Register + */ +#define I2S_RX_FIFO_AVABLE_IT_BIT HAL_BIT(0) +#define I2S_RX_FIFO_OVERRUN_IT_BIT HAL_BIT(1) +#define I2S_RX_FIFO_UNDERRUN_IT_BIT HAL_BIT(2) +#define I2S_TX_FIFO_EMPTY_IT_BIT HAL_BIT(4) +#define I2S_TX_FIFO_OVERRUN_IT_BIT HAL_BIT(5) +#define I2S_TX_FIFO_UNDERRUN_IT_BIT HAL_BIT(6) + +/* + * Bits definition for I2S_FCTL FIFO Control Register (0x0014) + */ +#define I2S_RX_FIFO_MODE_SHIFT (0) +#define I2S_RX_FIFO_MODE_MASK (0x3U << I2S_RX_FIFO_MODE_SHIFT) +typedef enum { + I2S_RX_FIFO_MODE0 = (0x0U << I2S_RX_FIFO_MODE_SHIFT), + I2S_RX_FIFO_MODE1 = (0x1U << I2S_RX_FIFO_MODE_SHIFT), + I2S_RX_FIFO_MODE2 = (0x2U << I2S_RX_FIFO_MODE_SHIFT), + I2S_RX_FIFO_MODE3 = (0x3U << I2S_RX_FIFO_MODE_SHIFT), +} I2S_RXOM; + +#define I2S_TX_FIFO_MODE_SHIFT (2) +#define I2S_TX_FIFO_MODE_MASK (0x1U << I2S_TX_FIFO_MODE_SHIFT) +typedef enum { + I2S_TX_FIFO_MODE0 = (0x0U << I2S_TX_FIFO_MODE_SHIFT), + I2S_TX_FIFO_MODE1 = (0x1U << I2S_TX_FIFO_MODE_SHIFT), +} I2S_TXIM; + +#define I2S_RXFIFO_LEVEL_SHIFT (4) +#define I2S_RXFIFO_LEVEL_MASK (0x3FU << I2S_RXFIFO_LEVEL_SHIFT) +#define I2S_RXFIFO_TRIGGER_LEVEL(n) ((n+1) << I2S_RXFIFO_LEVEL_SHIFT) + +#define I2S_TXFIFO_LEVEL_SHIFT (12) +#define I2S_TXFIFO_LEVEL_MASK (0x7FU << I2S_TXFIFO_LEVEL_SHIFT) +#define I2S_TXFIFO_TRIGGER_LEVEL(n) ((n+1) << I2S_TXFIFO_LEVEL_SHIFT) + +#define I2S_RXFIFO_RESET_BIT HAL_BIT(24) +#define I2S_TXFIFO_RESET_BIT HAL_BIT(25) +#define I2S_HUB_EN_BIT HAL_BIT(31) + +/* + * Bits definition for I2S_FSTA FIFO Status Register + */ +#define I2S_RXFIFO_CNT_SHIFT (0) +#define I2S_RXFIFO_CNT_MASK (0x7FU << I2S_RXFIFO_CNT_SHIFT) + +#define I2S_RXFIFO_AVAILABLE_BIT HAL_BIT(8) + +#define I2S_TXFIFO_EMPTY_SHIFT (16) +#define I2S_TXFIFO_EMPTY_MASK (0xFFU << I2S_TXFIFO_EMPTY_SHIFT) + +#define I2S_TXFIFO_EMPTY_BIT HAL_BIT(28) + +/* + * Bits definition for I2S_INT DMA&Interrupt Control Register (0x001c) + */ +#define I2S_RXFIFO_AVABLE_ITEN_BIT HAL_BIT(0) +#define I2S_RXFIFO_OVERRUN_ITEN_BIT HAL_BIT(1) +#define I2S_RXFIFO_UNDERRUN_ITEN_BIT HAL_BIT(2) +#define I2S_RXFIFO_DMA_ITEN_BIT HAL_BIT(3) +#define I2S_TXFIFO_EMPTY_ITEN_BIT HAL_BIT(4) +#define I2S_TXFIFO_OVERRUN_ITEN_BIT HAL_BIT(5) +#define I2S_TXFIFO_UNDERRUN_ITEN_BIT HAL_BIT(6) +#define I2S_TXFIFO_DMA_ITEN_BIT HAL_BIT(7) + +/* + * Bits definition for I2S_CLKD Clock Divide Register (0x24) + */ +#define I2S_MCLKDIV_SHIFT (0) +#define I2S_MCLKDIV_MASK (0xFU << I2S_MCLKDIV_SHIFT) /* MCLK divide ratio [3:0] */ +typedef enum { + I2S_MCLKDIV_1 = (0x1U << I2S_MCLKDIV_SHIFT), /* MCLK divide by 1 */ + I2S_MCLKDIV_2 = (0x2U << I2S_MCLKDIV_SHIFT), /* MCLK divide by 2 */ + I2S_MCLKDIV_4 = (0x3U << I2S_MCLKDIV_SHIFT), /* MCLK divide by 4 */ + I2S_MCLKDIV_6 = (0x4U << I2S_MCLKDIV_SHIFT), /* MCLK divide by 6 */ + I2S_MCLKDIV_8 = (0x5U << I2S_MCLKDIV_SHIFT), /* MCLK divide by 8 */ + I2S_MCLKDIV_12 = (0x6U << I2S_MCLKDIV_SHIFT), /* MCLK divide by 12 */ + I2S_MCLKDIV_16 = (0x7U << I2S_MCLKDIV_SHIFT), /* MCLK divide by 16 */ + I2S_MCLKDIV_24 = (0x8U << I2S_MCLKDIV_SHIFT), /* MCLK divide by 24 */ + I2S_MCLKDIV_32 = (0x9U << I2S_MCLKDIV_SHIFT), /* MCLK divide by 32 */ + I2S_MCLKDIV_48 = (0xAU << I2S_MCLKDIV_SHIFT), /* MCLK divide by 48 */ + I2S_MCLKDIV_64 = (0xBU << I2S_MCLKDIV_SHIFT), /* MCLK divide by 64 */ + I2S_MCLKDIV_96 = (0xCU << I2S_MCLKDIV_SHIFT), /* MCLK divide by 96 */ + I2S_MCLKDIV_128 = (0xDU << I2S_MCLKDIV_SHIFT), /* MCLK divide by 128 */ + I2S_MCLKDIV_176 = (0xEU << I2S_MCLKDIV_SHIFT), /* MCLK divide by 176 */ + I2S_MCLKDIV_192 = (0xFU << I2S_MCLKDIV_SHIFT), /* MCLK divide by 192 */ +} I2S_MCLKDIV; + +#define I2S_BCLKDIV_SHIFT (4) +#define I2S_BCLKDIV_MASK (0xFU << I2S_BCLKDIV_SHIFT) /* BCLK divide ratio [3:0] */ +typedef enum { + I2S_BCLKDIV_1 = (0x1U << I2S_BCLKDIV_SHIFT), /* BCLK divide by 1 */ + I2S_BCLKDIV_2 = (0x2U << I2S_BCLKDIV_SHIFT), /* BCLK divide by 2 */ + I2S_BCLKDIV_4 = (0x3U << I2S_BCLKDIV_SHIFT), /* BCLK divide by 4 */ + I2S_BCLKDIV_6 = (0x4U << I2S_BCLKDIV_SHIFT), /* BCLK divide by 6 */ + I2S_BCLKDIV_8 = (0x5U << I2S_BCLKDIV_SHIFT), /* BCLK divide by 8 */ + I2S_BCLKDIV_12 = (0x6U << I2S_BCLKDIV_SHIFT), /* BCLK divide by 12 */ + I2S_BCLKDIV_16 = (0x7U << I2S_BCLKDIV_SHIFT), /* BCLK divide by 16 */ + I2S_BCLKDIV_24 = (0x8U << I2S_BCLKDIV_SHIFT), /* BCLK divide by 24 */ + I2S_BCLKDIV_32 = (0x9U << I2S_BCLKDIV_SHIFT), /* BCLK divide by 32 */ + I2S_BCLKDIV_48 = (0xAU << I2S_BCLKDIV_SHIFT), /* BCLK divide by 48 */ + I2S_BCLKDIV_64 = (0xBU << I2S_BCLKDIV_SHIFT), /* BCLK divide by 64 */ + I2S_BCLKDIV_96 = (0xCU << I2S_BCLKDIV_SHIFT), /* BCLK divide by 96 */ + I2S_BCLKDIV_128 = (0xDU << I2S_BCLKDIV_SHIFT), /* BCLK divide by 128 */ + I2S_BCLKDIV_176 = (0xEU << I2S_BCLKDIV_SHIFT), /* BCLK divide by 176 */ + I2S_BCLKDIV_192 = (0xFU << I2S_BCLKDIV_SHIFT), /* BCLK divide by 192 */ +} I2S_BCLKDIV; + +#define I2S_MCLK_OUT_EN_BIT HAL_BIT(8) + +/* + * Bits definition for I2S_CHCFG Channel Configuration Register (0x30) + */ +#define I2S_TX_SLOT_NUM_SHIFT (0) +#define I2S_TX_SLOT_NUM_MASK (0x7U << I2S_TX_SLOT_NUM_SHIFT) +typedef enum { + I2S_TX_SLOT_NUM1 = (0x0U << I2S_TX_SLOT_NUM_SHIFT), + I2S_TX_SLOT_NUM2 = (0x1U << I2S_TX_SLOT_NUM_SHIFT), + I2S_TX_SLOT_NUM3 = (0x2U << I2S_TX_SLOT_NUM_SHIFT), + I2S_TX_SLOT_NUM4 = (0x3U << I2S_TX_SLOT_NUM_SHIFT), + I2S_TX_SLOT_NUM5 = (0x4U << I2S_TX_SLOT_NUM_SHIFT), + I2S_TX_SLOT_NUM6 = (0x5U << I2S_TX_SLOT_NUM_SHIFT), + I2S_TX_SLOT_NUM7 = (0x6U << I2S_TX_SLOT_NUM_SHIFT), + I2S_TX_SLOT_NUM8 = (0x7U << I2S_TX_SLOT_NUM_SHIFT), +} I2S_TxSlotNum; + +#define I2S_RX_SLOT_NUM_SHIFT (4) +#define I2S_RX_SLOT_NUM_MASK (0x7U << I2S_RX_SLOT_NUM_SHIFT) +typedef enum { + I2S_RX_SLOT_NUM1 = (0x0U << I2S_RX_SLOT_NUM_SHIFT), + I2S_RX_SLOT_NUM2 = (0x1U << I2S_RX_SLOT_NUM_SHIFT), + I2S_RX_SLOT_NUM3 = (0x2U << I2S_RX_SLOT_NUM_SHIFT), + I2S_RX_SLOT_NUM4 = (0x3U << I2S_RX_SLOT_NUM_SHIFT), + I2S_RX_SLOT_NUM5 = (0x4U << I2S_RX_SLOT_NUM_SHIFT), + I2S_RX_SLOT_NUM6 = (0x5U << I2S_RX_SLOT_NUM_SHIFT), + I2S_RX_SLOT_NUM7 = (0x6U << I2S_RX_SLOT_NUM_SHIFT), + I2S_RX_SLOT_NUM8 = (0x7U << I2S_RX_SLOT_NUM_SHIFT), +} I2S_RxSlotNum; + +#define I2S_TXN_STATE_SHIFT (8) +#define I2S_TXN_STATEMASK (0x1U << I2S_TXN_STATE_SHIFT) +typedef enum { + I2S_TRANSFER0 = (0x0U << I2S_TXN_STATE_SHIFT), + I2S_HIZ_STATE = (0x1U << I2S_TXN_STATE_SHIFT), +} I2S_TxnState; + +#define I2S_TX_SLOT_HIZ_SHIFT (9) +#define I2S_TX_SLOT_HIZ_MASK (0x1U << I2S_TX_SLOT_HIZ_SHIFT) +typedef enum { + I2S_NORMAL_MODE = (0x0U << I2S_RX_SLOT_NUM_SHIFT), + I2S_HI_Z_STATE = (0x1U << I2S_RX_SLOT_NUM_SHIFT), +} I2S_TxSlotHIZ; + +/* + * Bits definition for I2S_TXnCHSEL Channel Select Register (0X34) + */ +#define I2S_TXN_CHANNEL_SEL_SHIFT (0) +#define I2S_TXN_CHANNEL_SEL_MASK (0x7U << I2S_TXN_CHANNEL_SEL_SHIFT) +typedef enum { + I2S_TXN_CHANNEL_NUM1 = (0x0U << I2S_TXN_CHANNEL_SEL_SHIFT), + I2S_TXN_CHANNEL_NUM2 = (0x1U << I2S_TXN_CHANNEL_SEL_SHIFT), + I2S_TXN_CHANNEL_NUM3 = (0x2U << I2S_TXN_CHANNEL_SEL_SHIFT), + I2S_TXN_CHANNEL_NUM4 = (0x3U << I2S_TXN_CHANNEL_SEL_SHIFT), + I2S_TXN_CHANNEL_NUM5 = (0x4U << I2S_TXN_CHANNEL_SEL_SHIFT), + I2S_TXN_CHANNEL_NUM6 = (0x5U << I2S_TXN_CHANNEL_SEL_SHIFT), + I2S_TXN_CHANNEL_NUM7 = (0x6U << I2S_TXN_CHANNEL_SEL_SHIFT), + I2S_TXN_CHANNEL_NUM8 = (0x7U << I2S_TXN_CHANNEL_SEL_SHIFT), +} I2S_TxnChNum; + +#define I2S_TXN_CHANNEL_SLOT_ENABLE_SHIFT (4) +#define I2S_TXN_CHANNEL_SLOT_ENABLE_MASK (0xFFU << I2S_TXN_CHANNEL_SLOT_ENABLE_SHIFT) +#define I2S_TXN_CHANNEL_SLOTS_ENABLE(n) (((1 << n) -1) << I2S_TXN_CHANNEL_SLOT_ENABLE_SHIFT) +#define I2S_TXN_OFFSET_SHIFT (12) +#define I2S_TXN_OFFSET_MASK (0x1U << I2S_TXN_OFFSET_SHIFT) +typedef enum { + I2S_TX_NO_OFFSET = (0x0U << I2S_TXN_OFFSET_SHIFT), + I2S_TX_ONEBCLK_OFFSET = (0x1U << I2S_TXN_OFFSET_SHIFT), +} I2S_TxnOffset; + +/* + * Bits definition for I2S_TXnCHMAP Channel Mapping Register (0x44) + */ +#define I2S_TXN_CHX_MAP_MASK(m) (0x7U << (4*m)) +#define I2S_TXN_CHX_MAP(m) (m << 4*m) + +/* + * Bits definition for I2S_RXCHSEL Channel Mapping Register (0x54) + */ +#define I2S_RXN_CHANNEL_SEL_SHIFT 0 +#define I2S_RXN_CHANNEL_SEL_MASK (0x7U << I2S_RXN_CHANNEL_SEL_SHIFT) + +typedef enum { + I2S_RXN_CHANNEL_NUM1 = (0X0U << I2S_RXN_CHANNEL_SEL_SHIFT), + I2S_RXN_CHANNEL_NUM2 = (0X1U << I2S_RXN_CHANNEL_SEL_SHIFT), + I2S_RXN_CHANNEL_NUM3 = (0X2U << I2S_RXN_CHANNEL_SEL_SHIFT), + I2S_RXN_CHANNEL_NUM4 = (0X3U << I2S_RXN_CHANNEL_SEL_SHIFT), + I2S_RXN_CHANNEL_NUM5 = (0X4U << I2S_RXN_CHANNEL_SEL_SHIFT), + I2S_RXN_CHANNEL_NUM6 = (0X5U << I2S_RXN_CHANNEL_SEL_SHIFT), + I2S_RXN_CHANNEL_NUM7 = (0X6U << I2S_RXN_CHANNEL_SEL_SHIFT), + I2S_RXN_CHANNEL_NUM8 = (0X7U << I2S_RXN_CHANNEL_SEL_SHIFT), +} I2S_RxnChNum; + +#define I2S_RXN_OFFSET_SHIFT (12) +#define I2S_RXN_OFFSET_MASK (0x1U << I2S_RXN_OFFSET_SHIFT) +typedef enum { + I2S_RX_NO_OFFSET = (0x0U << I2S_RXN_OFFSET_SHIFT), + I2S_RX_ONEBCLK_OFFSET = (0x1U << I2S_RXN_OFFSET_SHIFT), +} I2S_RxnOffset; + +/* + * Bits definition for I2S_RXCHMAP Channel Mapping Register (0x58) + */ +#define I2S_RXN_CHX_MAP_MASK(m) (0x7U << (4*m)) +#define I2S_RXN_CHX_MAP(m) (m << (4*m)) + +/** + * @brief I2S sample rate definition + */ +typedef enum { + I2S_SR8K = 0, /* 8000Hz */ + I2S_SR12K = 1, /* 12000Hz */ + I2S_SR16K = 2, /* 16000Hz */ + I2S_SR24K = 3, /* 24000Hz */ + I2S_SR32K = 4, /* 32000Hz */ + I2S_SR48K = 5, /* 48000Hz */ + I2S_SR11K = 6, /* 11025Hz */ + I2S_SR22K = 7, /* 22050Hz */ + I2S_SR44K = 8, /* 44100Hz */ +} I2S_SampleRate; + +/** + * @brief stream direction + */ +typedef enum { + PLAYBACK, + RECORD, +} I2S_StreamDir; + +/** + * @brief i2s data init structure definition + */ +typedef struct { + I2S_SampleRate sampleRate; /*!< Specifies the sampling rate of the transmitted data. */ + I2S_StreamDir direction; /*!< Specifies the direction of the transmitted data. */ + I2S_SampleResolution resolution; /*!< Specifies the sampling accuracy of the transmitted data. */ + uint32_t channels; /*!< Specifies the number of channels to transmit data. */ + uint32_t bufSize; /*!< Specifies the buffer size of the transmitted data. */ +} I2S_DataParam; + +/** + * @brief external device clock structure definition + */ +typedef struct { + uint32_t clkSource; /*!< Specifies the clock frequency provided. */ + bool isDevclk; /*!< Specifies whether i2s provide an external device clock. */ +} DEV_Param; + +/** + * @brief I2S low level hardware init structure definition + */ +typedef struct { + bool i2sFormat; /*!< Specifies the I2S operating format. */ + PCM_ClkMode clkMode; /*!< Specifies the I2S clk mode. */ + PCM_TranFmt transferFormat; /*!< Specifies the I2S transfer format. */ + PCM_SignalInv signalInterval; /*!< Specifies the idle state of the I2S clock. */ + uint32_t lrckPeriod; /*!< Specifies The lrck division factor. */ + I2S_FrameMode frameMode; /*!< Specifies the frame format. */ + I2S_TxMLS txMsbFirst; /*!< Specifies high first pass or low first pass when transmission data. */ + I2S_RxMLS rxMsbFirst; /*!< Specifies high first pass or low first pass when receive data. */ + uint32_t txFifoLevel; /*!< Specifies the depth of tx Fifo. */ + uint32_t rxFifoLevel; /*!< Specifies the depth of rx Fifo. */ + DEV_Param codecClk; /*!< I2S MCLK parameters for external device */ +} I2S_HWParam; + +/** + * @brief I2S module init structure definition + */ +typedef struct { + I2S_HWParam *hwParam; /*!< I2S Hardware init structure. */ +} I2S_Param; + +HAL_Status HAL_I2S_Init(I2S_Param *param); +void HAL_I2S_DeInit(); +HAL_Status HAL_I2S_Open(I2S_DataParam *param); +HAL_Status HAL_I2S_Close(uint32_t dir); +int32_t HAL_I2S_Read_DMA(uint8_t *buf, uint32_t size); +int32_t HAL_I2S_Write_DMA(uint8_t *buf, uint32_t size); +void HAL_I2S_REG_DEBUG(); + +#ifdef __cplusplus +} +#endif + +#endif /* _DRIVER_CHIP_HAL_I2S_H_ */ diff --git a/platform/mcu/xr871/include/driver/chip/hal_irrx.h b/platform/mcu/xr871/include/driver/chip/hal_irrx.h new file mode 100644 index 0000000000..5d9a8caf7b --- /dev/null +++ b/platform/mcu/xr871/include/driver/chip/hal_irrx.h @@ -0,0 +1,133 @@ +/** + * @file hal_irrx.h + * @author XRADIO IOT WLAN Team + */ + +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _DRIVER_CHIP_HAL_IRRX_H_ +#define _DRIVER_CHIP_HAL_IRRX_H_ + +#include "driver/chip/hal_def.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Protocal supported. + * + * @note Only support NEC protocal now, rewrite IRRX_XXXPacket_Handler + * if you want support other protocals. + */ + +/** + * @brief select 32k clock as clk source definition. + * + * @note IR_CLK_32K_USED can be used only external 32768 crystal enabled. + */ +//#define IR_CLK_32K_USED 1 /* keep the same define in hal_irtx.h */ + +/** + * @brief IR Key Match definition. + * + * @note If defined IRRX_CHECK_ADDR_CODE, only addr_code(IRRX_ADDR_CODE) + * will be reported. + */ +//#define IRRX_CHECK_ADDR_CODE 1 +#define IRRX_ADDR_CODE (0x7f80) /* (addr|~addr) */ + +/** + * @brief IRRX use example: + * 1. define IR_CLK_32K_USED as ir clk source if 32k can be used when suspend. + * 2. define IRRX_CHECK_ADDR_CODE if you want to support only one ir remoter + * which addr is defined by IRRX_ADDR_CODE. + * +#include "driver/chip/hal_irrx.h" + +static void irrx_rxcplt_callback(uint32_t addr, uint32_t key) +{ + printf("received ir code addr:0x%02x key:0x%02x\n", addr, key); +} + +void main(const void *arg) +{ + IRRX_InitTypeDef irrx_param; + + irrx_param.PulsePolariyInvert = IRRX_RPPI_INVERT; + irrx_param.rxCpltCallback = &irrx_rxcplt_callback; + HAL_IRRX_Init(&irrx_param); +} + * + */ + +/* + * Bit field definition of IRRX configure register, receiver pulse polarity. + */ +typedef enum +{ + IRRX_RPPI_NONE = 0, + IRRX_RPPI_INVERT = HAL_BIT(2) +} IRRX_RppiTypeDef; + +struct IRRX_HandleDef; +typedef struct IRRX_HandleDef IRRX_HandleTypeDef; + +/** @brief Type define of IRRX IRQ callback function. */ +typedef void (*IRRX_RxCpltCallback)(uint32_t addr, uint32_t key); + +/** @brief IRRX initialization parameters. */ +typedef struct +{ + IRRX_RppiTypeDef PulsePolariyInvert; + IRRX_RxCpltCallback rxCpltCallback; +} IRRX_InitTypeDef; + +/** + * @brief Initializes the IRRX peripheral. + * @param param: + * @arg param->[in] The configuration information. + * @retval None. + */ +extern IRRX_HandleTypeDef *HAL_IRRX_Init(IRRX_InitTypeDef *param); + +/** + * @brief DeInitializes the IRRX peripheral. + * @param irrx: + * @arg irrx->[in] IRRX handler. + * @retval None. + */ +extern void HAL_IRRX_DeInit(IRRX_HandleTypeDef *irrx); + +#ifdef __cplusplus +} +#endif + +#endif /* _DRIVER_CHIP_HAL_IRRX_H_ */ diff --git a/platform/mcu/xr871/include/driver/chip/hal_irtx.h b/platform/mcu/xr871/include/driver/chip/hal_irtx.h new file mode 100644 index 0000000000..f7ffdbf80f --- /dev/null +++ b/platform/mcu/xr871/include/driver/chip/hal_irtx.h @@ -0,0 +1,209 @@ +/** + * @file hal_irtx.h + * @author XRADIO IOT WLAN Team + */ + +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _DRIVER_CHIP_HAL_IRTX_H_ +#define _DRIVER_CHIP_HAL_IRTX_H_ + +#include "driver/chip/hal_def.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief select 32k clock as clk source definition. + * + * @note IR_CLK_32K_USED can be used only external 32768 crystal enabled. + */ +//#define IR_CLK_32K_USED 1 /* keep the same define in hal_irrx.h */ + +/* + * @brief IRTX use example: + * 1 define IR_CLK_32K_USED as ir clk source if 32k can be used when suspend. + * + +#include "driver/chip/hal_irtx.h" +#include "driver/chip/ir_nec.h" + +void main(const void *arg) +{ + IRTX_InitTypeDef irtx_param; + IRTX_HandleTypeDef *irtx; + + irtx_param.PulsePolarity = IRTX_TPPI_NONE; + irtx_param.ModulateDutyLevel = IRTX_DRMC_TIME_1; + irtx_param.SendModeType = IRTX_TTS_NONE; //IRTX_TTS_CYCLICAL for cycle mode + irtx_param.CyclicalCnt = 3; //send 3 times if cycle mode +#if defined (IR_CLK_32K_USED) + irtx_param.IdleDurationCnt = IRTX_32K_NEC_IDC_VALUE; +#else + uint32_t clk = HAL_GetHFClock(); + + if (clk == HOSC_CLOCK_26M) { + irtx_param.IdleDurationCnt = IRTX_26M_NEC_IDC_VALUE; + } else if (clk == HOSC_CLOCK_24M) { + irtx_param.IdleDurationCnt = IRTX_24M_NEC_IDC_VALUE; + } else { + printf("%s unknow clk type(%d)!\n", __func__, clk); + } +#endif + + irtx_param.InternalModulation = IRTX_IMS_ENABLE; + irtx = HAL_IRTX_Init(&irtx_param); + + while (1) { + OS_Sleep(5); + // add=0x59, key=0x16 + HAL_IRTX_Transmit(irtx, IRTX_NEC_PROTO, IR_NEC_CODE(0x59, 0x16)); + } +} + * + */ + +/** + * @brief Add a new ir protocal. + * @note Define a new ir protocal in IRTX_ProtocalsDef and IRTX_PROTOS_FUN_INIT. + */ +typedef enum +{ + IRTX_NEC_PROTO = 0, + //IRTX_ITT_PROTO = 1, + IRTX_PROTO_NUM /* keep last */ +} IRTX_ProtocalsDef; + +typedef uint32_t (*IRTX_Proto_Code)(uint8_t *txBuff, uint32_t ir_tx_code); + +#define IRTX_PROTOS_FUN_INIT(handler) \ + do { \ + (handler)->Protos[IRTX_NEC_PROTO] = IRTX_NECPacket_Code; \ + /*(handler)->Protos[IRTX_ITT_PROTO] = IRTX_ITTPacket_Code;*/ \ + } while (0) + +/* + * @brief The high/low level modulated carrier duty ratio select. + * @note Bit field definition of IRTX Transmit Global register of bit5~6. + * DRMC_TIME_1 can trasmit longer, DRMC_TIME_3 has less power. + */ +typedef enum +{ + IRTX_DRMC_TIME_1 = (0), /* 1:1 */ + IRTX_DRMC_TIME_2 = (1<<5), /* 1:2 */ + IRTX_DRMC_TIME_3 = (2<<5), /* 1:3 */ + IRTX_DRMC_TIME_MASK = (3<<5) +} IRTX_DRMC_TIME; + +/* + * @brief Bit field definition of IRTX Transmit Global register of bit2. + * @note Transmit pulse polarity. + */ +typedef enum +{ + IRTX_TPPI_NONE = (0), + IRTX_TPPI_INVERT = (1<<2), + IRTX_TPPI_MASK = (1<<2) +} IRTX_TPPI_Type; + +/** + * @brief IRTX internal modulation signal enable. + * @note Select disable when connect tx/rx directly in test mode. + */ +typedef enum +{ + IRTX_IMS_DISABLE = (0), + IRTX_IMS_ENABLE = (1<<7) +} IRTX_IMS_Type; + +/* + * @bried Transmit mode select. + * + * @note Bit field definition of IRTX Transmit Control register of bit0, + * transmit type: signal or cyclical mode. + * The NEC repeat code start with S0 is 4.5mS not 2.25mS, for hardware + * not support change S0. + */ +typedef enum +{ + IRTX_TTS_NONE = (0), + IRTX_TTS_CYCLICAL = (1), + IRTX_TTS_MASK = (1) +} IRTX_TTS_Type; + +/** @brief IRTX initialization parameters. */ +typedef struct +{ + IRTX_DRMC_TIME ModulateDutyLevel; /* 1/3(less power cosumption) ~ 1/1(more tx lenght) */ + IRTX_TTS_Type SendModeType; /* signal or cyclical mode */ + uint32_t CyclicalCnt; /* count of cyclical mode */ + IRTX_TPPI_Type PulsePolarity; /* pulse polarity */ + IRTX_IMS_Type InternalModulation; /* internal modulation signal enable */ + uint32_t IdleDurationCnt; /* idle time for cyclical */ +} IRTX_InitTypeDef; + +struct IRTX_HandleDef; + +typedef struct IRTX_HandleDef IRTX_HandleTypeDef; + +/** + * @brief Send a code by IRTX peripheral. + * @param irtx: + * @arg irtx-> IRTX handler. + * @param protos_sel: + * @arg protos_sel->[in] The protocal used. + * @param ir_tx_code: + * @arg ir_tx_code->[in] The add and key(add|~addr|key|~key) will send. + * @retval None. + */ +extern void HAL_IRTX_Transmit(IRTX_HandleTypeDef *irtx, uint32_t protos_sel, uint32_t ir_tx_code); + +/** + * @brief Initializes the IRTX peripheral. + * @param param: + * @arg param->[in] The configuration information. + * @retval IRTX handler. + */ +extern IRTX_HandleTypeDef *HAL_IRTX_Init(IRTX_InitTypeDef *param); + +/** + * @brief DeInitializes the IRTX peripheral. + * @param irtx: + * @arg irtx->IRTX handler. + * @retval None. + */ +extern void HAL_IRTX_DeInit(IRTX_HandleTypeDef *irtx); + +#ifdef __cplusplus +} +#endif + +#endif /* _DRIVER_CHIP_HAL_IRTX_H_ */ diff --git a/platform/mcu/xr871/include/driver/chip/hal_mbox.h b/platform/mcu/xr871/include/driver/chip/hal_mbox.h new file mode 100644 index 0000000000..21dd235426 --- /dev/null +++ b/platform/mcu/xr871/include/driver/chip/hal_mbox.h @@ -0,0 +1,160 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _DRIVER_CHIP_HAL_MBOX_H_ +#define _DRIVER_CHIP_HAL_MBOX_H_ + +#include "driver/chip/hal_def.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* queue index */ +typedef enum { + MBOX_QUEUE_0 = 0, + MBOX_QUEUE_1 = 1, + MBOX_QUEUE_2 = 2, + MBOX_QUEUE_3 = 3, + MBOX_QUEUE_4 = 4, + MBOX_QUEUE_5 = 5, + MBOX_QUEUE_6 = 6, + MBOX_QUEUE_7 = 7, + MBOX_QUEUE_NUM = 8 +} MBOX_Queue; + +typedef struct { + __IO uint32_t CTRL[2]; /* offset: 0x0000 */ + uint32_t RESERVED0[14]; + __IO uint32_t IRQ0_EN; /* offset: 0x0040 */ + uint32_t RESERVED1[3]; + __IO uint32_t IRQ0_STATUS; /* offset: 0x0050 */ + uint32_t RESERVED2[3]; + __IO uint32_t IRQ1_EN; /* offset: 0x0060 */ + uint32_t RESERVED3[3]; + __IO uint32_t IRQ1_STATUS; /* offset: 0x0070 */ + uint32_t RESERVED4[35]; + __I uint32_t FIFO_STATUS[MBOX_QUEUE_NUM]; /* offset: 0x0100 */ + uint32_t RESERVED5[8]; + __I uint32_t MSG_STATUS[MBOX_QUEUE_NUM]; /* offset: 0x0140 */ + uint32_t RESERVED6[8]; + __IO uint32_t MSG[MBOX_QUEUE_NUM]; /* offset: 0x0180 */ + uint32_t RESERVED7[8]; + __IO uint32_t DEBUG; /* offset: 0x01C0 */ +} MBOX_T; + +#define MBOX_A ((MBOX_T *)MBOX_A_BASE) +#define MBOX_N ((MBOX_T *)MBOX_N_BASE) + +/* user + * - MBOX_A: app core is MBOX_USER0, net core is MBOX_USER1 + * - MBOX_N: app core is MBOX_USER1, net core is MBOX_USER0 + */ +typedef enum { + MBOX_USER0 = 0, + MBOX_USER1 = 1, + MBOX_USER_NUM = 2 +} MBOX_User; + +/* direction */ +typedef enum { + MBOX_DIR_RX = 0, + MBOX_DIR_TX = 1, + MBOX_DIR_NUM = 2 +} MBOX_Direction; + +/* queue size */ +#define MBOX_QUEUE_SIZE 4 + +/* + * bit field definition of MBOX->FIFO_STATUS + */ +#define MBOX_QUEUE_FULL_BIT HAL_BIT(0) + +/* + * bit field definition of MBOX->MSG_STATUS + */ +#define MBOX_QUEUE_MSG_NUM_SHIFT 0 +#define MBOX_QUEUE_MSG_NUM_VMASK 0x7 + +/* MBOX->MSG */ + +/* + * bit field definition of MBOX->DEBUG + */ +#define MBOX_DEBUG_FIFO_CTRL_SHIFT 8 +#define MBOX_DEBUG_EN_BIT HAL_BIT(0) + +/******************************************************************************/ + +#define HAL_MBOX_PM_PATCH 1 + +void HAL_MBOX_Init(MBOX_T *mbox); +void HAL_MBOX_DeInit(MBOX_T *mbox); +void HAL_MBOX_QueueInit(MBOX_T *mbox, MBOX_User user, MBOX_Queue queue, MBOX_Direction dir); +void HAL_MBOX_QueueDeInit(MBOX_T *mbox, MBOX_User user, MBOX_Queue queue, MBOX_Direction dir); +void HAL_MBOX_QueueEnableIRQ(MBOX_T *mbox, MBOX_User user, MBOX_Queue queue, MBOX_Direction dir); +void HAL_MBOX_QueueDisableIRQ(MBOX_T *mbox, MBOX_User user, MBOX_Queue queue, MBOX_Direction dir); +int HAL_MBOX_QueueIsPendingIRQ(MBOX_T *mbox, MBOX_User user, MBOX_Queue queue, MBOX_Direction dir); +void HAL_MBOX_QueueClrPendingIRQ(MBOX_T *mbox, MBOX_User user, MBOX_Queue queue, MBOX_Direction dir); +#if HAL_MBOX_PM_PATCH +void HAL_MBOX_EnableIRQ(MBOX_T *mbox); +void HAL_MBOX_DisableIRQ(MBOX_T *mbox); +#endif + +int HAL_MBOX_QueueIsFull(MBOX_T *mbox, MBOX_Queue queue); +uint32_t HAL_MBOX_QueueGetMsgNum(MBOX_T *mbox, MBOX_Queue queue); +void HAL_MBOX_QueuePutMsg(MBOX_T *mbox, MBOX_Queue queue, uint32_t msg); +uint32_t HAL_MBOX_QueueGetMsg(MBOX_T *mbox, MBOX_Queue queue); + +__STATIC_INLINE void HAL_MBOX_QueueEnableFIFO(MBOX_T *mbox, MBOX_Queue queue) +{ + HAL_CLR_BIT(mbox->DEBUG, HAL_BIT((uint32_t)queue + MBOX_DEBUG_FIFO_CTRL_SHIFT)); +} + +__STATIC_INLINE void HAL_MBOX_QueueDisableFIFO(MBOX_T *mbox, MBOX_Queue queue) +{ + HAL_SET_BIT(mbox->DEBUG, HAL_BIT((uint32_t)queue + MBOX_DEBUG_FIFO_CTRL_SHIFT)); +} + +__STATIC_INLINE void HAL_MBOX_EnableDebugMode(MBOX_T *mbox) +{ + HAL_SET_BIT(mbox->DEBUG, MBOX_DEBUG_EN_BIT); +} + +__STATIC_INLINE void HAL_MBOX_DisableDebugMode(MBOX_T *mbox) +{ + HAL_CLR_BIT(mbox->DEBUG, MBOX_DEBUG_EN_BIT); +} + +#ifdef __cplusplus +} +#endif + +#endif /* _DRIVER_CHIP_HAL_MBOX_H_ */ diff --git a/platform/mcu/xr871/include/driver/chip/hal_nvic.h b/platform/mcu/xr871/include/driver/chip/hal_nvic.h new file mode 100644 index 0000000000..b6de2041ab --- /dev/null +++ b/platform/mcu/xr871/include/driver/chip/hal_nvic.h @@ -0,0 +1,85 @@ +/** + * @file hal_nvic.h + * @author XRADIO IOT WLAN Team + */ + +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _DRIVER_CHIP_HAL_NVIC_H_ +#define _DRIVER_CHIP_HAL_NVIC_H_ + +#include "driver/chip/hal_def.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define NVIC_PRIORITYGROUP_0 (0x00000007U) /*!< 0 bits for preempt priority, 3 bits for subpriority */ +#define NVIC_PRIORITYGROUP_1 (0x00000006U) /*!< 1 bits for preempt priority, 2 bits for subpriority */ +#define NVIC_PRIORITYGROUP_2 (0x00000005U) /*!< 2 bits for preempt priority, 1 bits for subpriority */ +#define NVIC_PRIORITYGROUP_3 (0x00000004U) /*!< 3 bits for preempt priority, 0 bits for subpriority */ + +/* Default NVIC priority grouping */ +#define NVIC_PRIORITYGROUP_DEFAULT NVIC_PRIORITYGROUP_3 + +/** + * @brief Default priority for all peripherals + * @note + * - Rang from 0 to 7 due to __NVIC_PRIO_BITS is 3 + * - Should be set to [1, 7], MUST not be set to 0 + */ +#define NVIC_PERIPHERAL_PRIORITY_DEFAULT (4) + +/** @brief Type define of NVIC interrupt handler */ +typedef void (*NVIC_IRQHandler) (void); + +void HAL_NVIC_SetIRQHandler(IRQn_Type IRQn, NVIC_IRQHandler handler); +NVIC_IRQHandler HAL_NVIC_GetIRQHandler(IRQn_Type IRQn); + +void HAL_NVIC_SetPriorityGrouping(uint32_t priorityGroup); +uint32_t HAL_NVIC_GetPriorityGrouping(void); + +void HAL_NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority); +uint32_t HAL_NVIC_GetPriority(IRQn_Type IRQn); + +void HAL_NVIC_EnableIRQ(IRQn_Type IRQn); +void HAL_NVIC_DisableIRQ(IRQn_Type IRQn); + +void HAL_NVIC_SetPendingIRQ(IRQn_Type IRQn); +int HAL_NVIC_IsPendingIRQ(IRQn_Type IRQn); +void HAL_NVIC_ClearPendingIRQ(IRQn_Type IRQn); + +void HAL_NVIC_Init(void); + +#ifdef __cplusplus +} +#endif + +#endif /* _DRIVER_CHIP_HAL_NVIC_H_ */ diff --git a/platform/mcu/xr871/include/driver/chip/hal_prcm.h b/platform/mcu/xr871/include/driver/chip/hal_prcm.h new file mode 100644 index 0000000000..db0fa429ec --- /dev/null +++ b/platform/mcu/xr871/include/driver/chip/hal_prcm.h @@ -0,0 +1,637 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _DRIVER_CHIP_HAL_PRCM_H_ +#define _DRIVER_CHIP_HAL_PRCM_H_ + +#include "driver/chip/hal_def.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* prcm system module control */ +typedef struct { + __IO uint32_t SYS_DCDC_CTRL; /* offset: 0x0000, System DCDC control register */ + __IO uint32_t SYS_LDO_SW_CTRL; /* offset: 0x0004, System LDO and switch control register */ + __IO uint32_t SYS_LFCLK_CTRL; /* offset: 0x0008, System LFCLK control register */ + __IO uint32_t SYS_HOSC_CTRL; /* offset: 0x000C, System HOSC type register */ + __IO uint32_t SYS_RCOSC_CALIB_CTRL; /* offset: 0x0010, System RCOSC calibration control register */ + uint32_t RESERVED0[3]; + __IO uint32_t SYS_PLL_CTRL; /* offset: 0x0020, System PLL control register */ + __IO uint32_t SYS_CLK1_CTRL; /* offset: 0x0024, System clock 1 control register */ + __I uint32_t SYS_CLK2_CTRL; /* offset: 0x0028, System clock 2 control register */ + __I uint32_t SYS_CLK3_CTRL; /* offset: 0x002C, System clock 3 control register */ + __IO uint32_t AUD_PLL_CTRL; /* offset: 0x0030, Audio PLL control register */ + __IO uint32_t DEV_CLK_CTRL; /* offset: 0x0034, Device clock control register */ + uint32_t RESERVED1[3]; + __IO uint32_t AUD_PLL_BIAS; /* offset: 0x0044, Audio PLL bias register */ + uint32_t RESERVED2[3]; + __IO uint32_t AUD_PLL_PAT_CTRL; /* offset: 0x0054, Audio PLL pattern control register */ + __IO uint32_t DCXO_CTRL; /* offset: 0x0058, DCXO control register */ + uint32_t RESERVED3[9]; + __I uint32_t SYS1_CTRL; /* offset: 0x0080, System 1 control register */ + __IO uint32_t SYS1_STATUS; /* offset: 0x0084, System 1 status register */ + __IO uint32_t SYS2_CTRL; /* offset: 0x0088, System 2 control register */ + __I uint32_t SYS2_STATUS; /* offset: 0x008C, System 2 status register */ + __I uint32_t SYS3_CTRL; /* offset: 0x0090, System 3 control register */ + __I uint32_t SYS3_STATUS; /* offset: 0x0094, System 3 status register */ + __IO uint32_t SYS1_WAKEUP_CTRL; /* offset: 0x0098, System 1 wakeup control register */ + __I uint32_t SYS2_WAKEUP_CTRL; /* offset: 0x009C, System 2 wakeup control register */ + __IO uint32_t SYS1_SLEEP_CTRL; /* offset: 0x00A0, System 1 sleep control register */ + __I uint32_t SYS2_SLEEP_CTRL; /* offset: 0x00A4, System 2 sleep control register */ + __IO uint32_t DCXO_STABLE_REF_TIME; /* offset: 0x00A8, DCXO stable reference time register */ + __IO uint32_t DPLL_STABLE_REF_TIME; /* offset: 0x00AC, DPLL stable reference time register */ + __IO uint32_t LDO_STABLE_REF_TIME; /* offset: 0x00B0, LDO stable reference time register */ + __IO uint32_t DIG_SWITCH_REF_TIME; /* offset: 0x00B4, Digital switch reference time register */ + __IO uint32_t SRAM_VOLT_CTRL; /* offset: 0x00B8, SRAM voltage control register */ + __IO uint32_t BANDGAP_STABLE_REF_TIME; /* offset: 0x00BC, Band gap stable reference time register */ + __IO uint32_t DCDC_STABLE_REF_TIME; /* offset: 0x00C0, DCDC stable reference time register */ + uint32_t RESERVED4[15]; + __IO uint32_t CPUA_BOOT_FLAG; /* offset: 0x0100, CPUA boot flag register */ + __IO uint32_t CPUA_BOOT_ADDR; /* offset: 0x0104, CPUA boot address register */ + __IO uint32_t CPUA_BOOT_ARG; /* offset: 0x0108, CPUA boot argument register */ + __I uint32_t CPUN_BOOT_FLAG; /* offset: 0x010C, CPUN boot flag register */ + __I uint32_t CPUN_BOOT_ADDR; /* offset: 0x0110, CPUN boot address register */ + __I uint32_t CPUN_BOOT_ARG; /* offset: 0x0114, CPUN boot argument register */ + __IO uint32_t CPUA_PRIV_REG; /* offset: 0x0118, CPUA private register */ + uint32_t CPUN_PRIV_REG; /* offset: 0x011C, CPUN private register */ + __IO uint32_t CPUA_WAKE_TIMER_CNT; /* offset: 0x0120, CPUA wakeup timer counter register */ + __IO uint32_t CPUA_WAKE_TIMER_CMP; /* offset: 0x0124, CPUA wakeup timer value register */ + __I uint32_t CPUN_WAKE_TIMER_CNT; /* offset: 0x0128, CPUN wakeup timer counter register */ + __I uint32_t CPUN_WAKE_TIMER_CMP; /* offset: 0x012C, CPUN wakeup timer value register */ + __IO uint32_t CPUA_WAKE_IO_EN; /* offset: 0x0130, CPUA IO wakeup enable register */ + __IO uint32_t CPUA_WAKE_IO_MODE; /* offset: 0x0134, CPUA IO wakeup mode register */ + __IO uint32_t CPUA_WAKE_IO_STATUS; /* offset: 0x0138, CPUA IO wakeup status register */ + __IO uint32_t CPUA_WAKE_IO_HOLD; /* offset: 0x013C, CPUA IO hold control register */ + __IO uint32_t CPUA_WAKE_IO_GLOBAL_EN; /* offset: 0x0140, CPUA IO wakeup global enable register */ + __IO uint32_t CPUA_PRCM_REG; /* offset: 0x0144, CPUA PRCM register */ + uint32_t RESERVED6[46]; + __IO uint32_t DCDC_PARAM_CTRL; /* offset: 0x0200, DCDC parameter control register */ + __IO uint32_t ANA_BANDGAP; /* offset: 0x0204, Analog band gap control register */ + __I uint32_t CLK_LDO_PARAM; /* offset: 0x0208, Clock LDO parameter register */ + __IO uint32_t DIG_LDO_PARAM; /* offset: 0x020C, Digital LDO parameter register */ + __IO uint32_t DPLL_STATUS; /* offset: 0x0210, DPLL status register */ +} PRCM_T; + +#define PRCM ((PRCM_T *)PRCM_BASE) /* address: 0x40040000 */ + +/* + * bit field definition of PRCM->SYS_DCDC_CTRL + */ +#define PRCM_DCDC_VOLT_SHIFT 16 /* R/W */ +#define PRCM_DCDC_VOLT_MASK (0xFU << PRCM_DCDC_VOLT_SHIFT) +typedef enum { + PRCM_DCDC_VOLT_1V80 = (0x0U << PRCM_DCDC_VOLT_SHIFT), + PRCM_DCDC_VOLT_1V77 = (0x1U << PRCM_DCDC_VOLT_SHIFT), + PRCM_DCDC_VOLT_1V75 = (0x2U << PRCM_DCDC_VOLT_SHIFT), + PRCM_DCDC_VOLT_1V72 = (0x3U << PRCM_DCDC_VOLT_SHIFT), + PRCM_DCDC_VOLT_1V69 = (0x4U << PRCM_DCDC_VOLT_SHIFT), + PRCM_DCDC_VOLT_1V67 = (0x5U << PRCM_DCDC_VOLT_SHIFT), + PRCM_DCDC_VOLT_1V64 = (0x6U << PRCM_DCDC_VOLT_SHIFT), + PRCM_DCDC_VOLT_1V61 = (0x7U << PRCM_DCDC_VOLT_SHIFT), + PRCM_DCDC_VOLT_1V58 = (0x8U << PRCM_DCDC_VOLT_SHIFT), + PRCM_DCDC_VOLT_1V56 = (0x9U << PRCM_DCDC_VOLT_SHIFT), + PRCM_DCDC_VOLT_1V53 = (0xAU << PRCM_DCDC_VOLT_SHIFT), + PRCM_DCDC_VOLT_1V51 = (0xBU << PRCM_DCDC_VOLT_SHIFT), + PRCM_DCDC_VOLT_1V48 = (0xCU << PRCM_DCDC_VOLT_SHIFT), + PRCM_DCDC_VOLT_1V45 = (0xDU << PRCM_DCDC_VOLT_SHIFT), + PRCM_DCDC_VOLT_1V43 = (0xEU << PRCM_DCDC_VOLT_SHIFT), + PRCM_DCDC_VOLT_1V40 = (0xFU << PRCM_DCDC_VOLT_SHIFT) +} PRCM_DCDCVolt; + +#define PRCM_DCDC_EN_BIT HAL_BIT(0) /* R */ + +/* + * bit field definition of PRCM->SYS_LDO_SW_CTRL + */ +#define PRCM_SYS3_SRAM_PWR3_EN_BIT HAL_BIT(15) /* R */ +#define PRCM_SYS3_PWR5_EN_BIT HAL_BIT(14) /* R */ +#define PRCM_SYS3_PWR4_EN_BIT HAL_BIT(13) /* R */ + +#define PRCM_SYS2_PWR3_EN_BIT HAL_BIT(12) /* R/W */ +#define PRCM_SYS2_SRAM_PWR2_EN_BIT HAL_BIT(11) /* R/W */ + +#define PRCM_SYS1_SRAM_PWR1_EN_BIT HAL_BIT(10) /* R */ +#define PRCM_SYS1_PWR2_EN_BIT HAL_BIT(9) /* R */ +#define PRCM_SYS1_PWR1_EN_BIT HAL_BIT(8) /* R */ + +#define PRCM_LDO1_VOLT_SHIFT 4 /* R/W */ +#define PRCM_LDO1_VOLT_MASK (0x7U << PRCM_LDO1_VOLT_SHIFT) +typedef enum { + PRCM_LDO1_VOLT_1V10 = (0x0U << PRCM_LDO1_VOLT_SHIFT), + PRCM_LDO1_VOLT_1V05 = (0x1U << PRCM_LDO1_VOLT_SHIFT), + PRCM_LDO1_VOLT_1V00 = (0x2U << PRCM_LDO1_VOLT_SHIFT), + PRCM_LDO1_VOLT_0V95 = (0x3U << PRCM_LDO1_VOLT_SHIFT), + PRCM_LDO1_VOLT_0V90 = (0x4U << PRCM_LDO1_VOLT_SHIFT) +} PRCM_LDO1Volt; + +#define PRCM_SRAM_LDO_EN_BIT HAL_BIT(2) /* R */ +#define PRCM_PLL_LDO_EN_BIT HAL_BIT(1) /* R/W */ +#define PRCM_LDO1_EN_BIT HAL_BIT(0) /* R */ + +/* + * bit field definition of PRCM->SYS_LFCLK_CTRL + */ +#define PRCM_LFCLK_EXT32K_EN_BIT HAL_BIT(31) /* R/W */ +#define PRCM_LFCLK_INTER32K_EN_BIT HAL_BIT(30) /* R/W */ + +#define PRCM_LFCLK_SRC_SHIFT 24 /* R/W */ +#define PRCM_LFCLK_SRC_MASK (0x1U << PRCM_LFCLK_SRC_SHIFT) +typedef enum { + PRCM_LFCLK_SRC_INTER32K = (0x0U << PRCM_LFCLK_SRC_SHIFT), + PRCM_LFCLK_SRC_EXT32K = (0x1U << PRCM_LFCLK_SRC_SHIFT) +} PRCM_LFCLKSrc; + +/* + * bit field definition of PRCM->SYS_HOSC_CTRL + */ +#define PRCM_HOSC_TYPE_SHIFT 0 /* R/W */ +#define PRCM_HOSC_TYPE_VMASK 0x3U +#define PRCM_HOSC_TYPE_MASK (PRCM_HOSC_TYPE_VMASK << PRCM_HOSC_TYPE_SHIFT) +typedef enum { + PRCM_HOSC_TYPE_26M = (0U << PRCM_HOSC_TYPE_SHIFT), + PRCM_HOSC_TYPE_40M = (1U << PRCM_HOSC_TYPE_SHIFT), + PRCM_HOSC_TYPE_24M = (2U << PRCM_HOSC_TYPE_SHIFT), + PRCM_HOSC_TYPE_52M = (3U << PRCM_HOSC_TYPE_SHIFT) +} PRCM_HOSCType; + +/* + * bit field definition of PRCM->SYS_RCOSC_CALIB_CTRL + */ +#define PRCM_RCOSC_CALIB_FREQ_SHIFT 8 /* R */ +#define PRCM_RCOSC_CALIB_FREQ_VMASK 0xFFFFFU + +#define PRCM_RCOSC_CALIB_EN_BIT HAL_BIT(0) /* R/W */ + +/* + * bit field definition of PRCM->SYS_PLL_CTRL + */ +#define PRCM_SYS_PLL_EN_BIT HAL_BIT(31) /* R/W */ + +#define PRCM_SYS_PLL_PARAM_SHIFT 0 +#define PRCM_SYS_PLL_PARAM_MASK 0x7FFFFFFFU +typedef enum { + PRCM_SYS_PLL_PARAM_HOSC24M = 0x00001141U, + PRCM_SYS_PLL_PARAM_HOSC26M = 0x0EC4F121U, + PRCM_SYS_PLL_PARAM_HOSC40M = 0x000010C1U, + PRCM_SYS_PLL_PARAM_HOSC52M = 0x07627091U +} PRCM_SysPLLParam; + +/* + * bit field definition of + * - PRCM->SYS_CLK1_CTRL (R/W) + * - PRCM->SYS_CLK2_CTRL (R) + * - PRCM->SYS_CLK3_CTRL (R) + */ +#define PRCM_SYS_CLK_EN_BIT HAL_BIT(31) /* R/W, R, R */ + +/* NB: the following bits are for SYS_CLK1_CTRL, SYS_CLK2_CTRL only */ +#define PRCM_CPU_CLK_SRC_SHIFT 16 /* R/W, R */ +#define PRCM_CPU_CLK_SRC_MASK (0x3 << PRCM_CPU_CLK_SRC_SHIFT) +typedef enum { + PRCM_CPU_CLK_SRC_HFCLK = (0x0U << PRCM_CPU_CLK_SRC_SHIFT), + PRCM_CPU_CLK_SRC_LFCLK = (0x1U << PRCM_CPU_CLK_SRC_SHIFT), + PRCM_CPU_CLK_SRC_SYSCLK = (0x2U << PRCM_CPU_CLK_SRC_SHIFT) +} PRCM_CPUClkSrc; + +/* NB: the following bits are for SYS_CLK1_CTRL, SYS_CLK2_CTRL only */ +#define PRCM_SYS_CLK_FACTOR_SHIFT 0 /* R/W */ +#define PRCM_SYS_CLK_FACTOR_VMASK 0xFU +#define PRCM_SYS_CLK_FACTOR_MASK (PRCM_SYS_CLK_FACTOR_VMASK << PRCM_SYS_CLK_FACTOR_SHIFT) +typedef enum { +// PRCM_SYS_CLK_FACTOR_960M = (0U << PRCM_SYS_CLK_FACTOR_SHIFT), +// PRCM_SYS_CLK_FACTOR_480M = (1U << PRCM_SYS_CLK_FACTOR_SHIFT), +// PRCM_SYS_CLK_FACTOR_320M = (2U << PRCM_SYS_CLK_FACTOR_SHIFT), + PRCM_SYS_CLK_FACTOR_240M = (3U << PRCM_SYS_CLK_FACTOR_SHIFT), + PRCM_SYS_CLK_FACTOR_192M = (4U << PRCM_SYS_CLK_FACTOR_SHIFT), + PRCM_SYS_CLK_FACTOR_160M = (5U << PRCM_SYS_CLK_FACTOR_SHIFT), + PRCM_SYS_CLK_FACTOR_137M = (6U << PRCM_SYS_CLK_FACTOR_SHIFT), + PRCM_SYS_CLK_FACTOR_120M = (7U << PRCM_SYS_CLK_FACTOR_SHIFT), + PRCM_SYS_CLK_FACTOR_107M = (8U << PRCM_SYS_CLK_FACTOR_SHIFT), + PRCM_SYS_CLK_FACTOR_96M = (9U << PRCM_SYS_CLK_FACTOR_SHIFT), + PRCM_SYS_CLK_FACTOR_87M = (10U << PRCM_SYS_CLK_FACTOR_SHIFT), + PRCM_SYS_CLK_FACTOR_80M = (11U << PRCM_SYS_CLK_FACTOR_SHIFT), + PRCM_SYS_CLK_FACTOR_74M = (12U << PRCM_SYS_CLK_FACTOR_SHIFT), + PRCM_SYS_CLK_FACTOR_69M = (13U << PRCM_SYS_CLK_FACTOR_SHIFT), + PRCM_SYS_CLK_FACTOR_64M = (14U << PRCM_SYS_CLK_FACTOR_SHIFT), + PRCM_SYS_CLK_FACTOR_60M = (15U << PRCM_SYS_CLK_FACTOR_SHIFT), +} PRCM_SysClkFactor; + +/* + * bit field definition of PRCM->AUD_PLL_CTRL + */ +#define PRCM_AUD_PLL_EN_BIT HAL_BIT(31) /* R/W */ + +#define PRCM_AUD_PLL_PARAM_SHIFT 0 +#define PRCM_AUD_PLL_PARAM_MASK 0x7FFFFFFFU +typedef enum { + PRCM_AUD_PLL24M_PARAM_HOSC13M = 0x8003780FU, + PRCM_AUD_PLL24M_PARAM_HOSC19M2 = 0x80037F18U, + PRCM_AUD_PLL24M_PARAM_HOSC24M = 0x80035514U, + PRCM_AUD_PLL24M_PARAM_HOSC26M = 0x8003781FU, + PRCM_AUD_PLL24M_PARAM_HOSC40M = 0x80033A17U, + PRCM_AUD_PLL24M_PARAM_HOSC50M = 0x8003381CU, + PRCM_AUD_PLL24M_PARAM_HOSC52M = 0x81030603U, + /*22.5792*/ + PRCM_AUD_PLL22M_PARAM_HOSC13M = 0x81031A03U, + PRCM_AUD_PLL22M_PARAM_HOSC19M2 = 0x80037E1AU, + PRCM_AUD_PLL22M_PARAM_HOSC24M = 0x80034E14U, + PRCM_AUD_PLL22M_PARAM_HOSC26M = 0x80034112U, + PRCM_AUD_PLL22M_PARAM_HOSC40M = 0x8003451EU, + PRCM_AUD_PLL22M_PARAM_HOSC50M = 0x8003371EU, + PRCM_AUD_PLL22M_PARAM_HOSC52M = 0x80032012U +} PRCM_AudPLLParam; + +/* + * bit field definition of PRCM->DEV_CLK_CTRL + */ +#define PRCM_DEV_CLK_FACTOR_SHIFT 0 /* R/W */ +#define PRCM_DEV_CLK_FACTOR_VMASK 0xFU +#define PRCM_DEV_CLK_FACTOR_MASK (PRCM_DEV_CLK_FACTOR_VMASK << PRCM_DEV_CLK_FACTOR_SHIFT) +typedef enum { +// PRCM_DEV_CLK_FACTOR_960M = (0U << PRCM_DEV_CLK_FACTOR_SHIFT), +// PRCM_DEV_CLK_FACTOR_480M = (1U << PRCM_DEV_CLK_FACTOR_SHIFT), +// PRCM_DEV_CLK_FACTOR_320M = (2U << PRCM_DEV_CLK_FACTOR_SHIFT), +// PRCM_DEV_CLK_FACTOR_240M = (3U << PRCM_DEV_CLK_FACTOR_SHIFT), + PRCM_DEV_CLK_FACTOR_192M = (4U << PRCM_DEV_CLK_FACTOR_SHIFT), + PRCM_DEV_CLK_FACTOR_160M = (5U << PRCM_DEV_CLK_FACTOR_SHIFT), + PRCM_DEV_CLK_FACTOR_137M = (6U << PRCM_DEV_CLK_FACTOR_SHIFT), + PRCM_DEV_CLK_FACTOR_120M = (7U << PRCM_DEV_CLK_FACTOR_SHIFT), + PRCM_DEV_CLK_FACTOR_107M = (8U << PRCM_DEV_CLK_FACTOR_SHIFT), + PRCM_DEV_CLK_FACTOR_96M = (9U << PRCM_DEV_CLK_FACTOR_SHIFT), + PRCM_DEV_CLK_FACTOR_87M = (10U << PRCM_DEV_CLK_FACTOR_SHIFT), + PRCM_DEV_CLK_FACTOR_80M = (11U << PRCM_DEV_CLK_FACTOR_SHIFT), + PRCM_DEV_CLK_FACTOR_74M = (12U << PRCM_DEV_CLK_FACTOR_SHIFT), + PRCM_DEV_CLK_FACTOR_69M = (13U << PRCM_DEV_CLK_FACTOR_SHIFT), + PRCM_DEV_CLK_FACTOR_64M = (14U << PRCM_DEV_CLK_FACTOR_SHIFT), + PRCM_DEV_CLK_FACTOR_60M = (15U << PRCM_DEV_CLK_FACTOR_SHIFT), +} PRCM_DevClkFactor; + +/* + * bit field definition of PRCM->AUD_PLL_BIAS + */ +#define PRCM_AUD_PLL_VCO_BIAS_SHIFT 24 /* R/W */ +#define PRCM_AUD_PLL_VCO_BIAS_MASK (0x1F << PRCM_AUD_PLL_VCO_BIAS_SHIFT) + +#define PRCM_AUD_PLL_CUR_BIAS_SHIFT 16 /* R/W */ +#define PRCM_AUD_PLL_CUR_BIAS_MASK (0x1F << PRCM_AUD_PLL_CUR_BIAS_SHIFT) + +/* + * bit field definition of PRCM->AUD_PLL_PAT_CTRL + */ +#define PRCM_AUD_DIG_DELT_PAT_EN_BIT HAL_BIT(31) /* R/W */ + +#define PRCM_AUD_PLL_PAT_PARAM_SHIFT 0 +#define PRCM_AUD_PLL_PAT_PARAM_MASK 0x7FFFFFFFU +typedef enum { + PRCM_AUD_PLL24M_PAT_PARAM_HOSC13M = 0x00000000U, + PRCM_AUD_PLL24M_PAT_PARAM_HOSC19M2 = 0x00000000U, + PRCM_AUD_PLL24M_PAT_PARAM_HOSC24M = 0x00000000U, + PRCM_AUD_PLL24M_PAT_PARAM_HOSC26M = 0x00000000U, + PRCM_AUD_PLL24M_PAT_PARAM_HOSC40M = 0x00000000U, + PRCM_AUD_PLL24M_PAT_PARAM_HOSC50M = 0x00000000U, + PRCM_AUD_PLL24M_PAT_PARAM_HOSC52M = 0xc0011FAAU, + PRCM_AUD_PLL22M_PAT_PARAM_HOSC13M = 0xC0019468U, + PRCM_AUD_PLL22M_PAT_PARAM_HOSC19M2 = 0x00000000U, + PRCM_AUD_PLL22M_PAT_PARAM_HOSC24M = 0x00000000U, + PRCM_AUD_PLL22M_PAT_PARAM_HOSC26M = 0x00000000U, + PRCM_AUD_PLL22M_PAT_PARAM_HOSC40M = 0x00000000U, + PRCM_AUD_PLL22M_PAT_PARAM_HOSC50M = 0x00000000U, + PRCM_AUD_PLL22M_PAT_PARAM_HOSC52M = 0x00000000U +} PRCM_AudPLLPatParam; + +/* + * bit field definition of PRCM->DCXO_CTRL + */ +#define PRCM_DCXO_EN_BIT HAL_BIT(31) /* R/W */ + +/* + * bit field definition of PRCM->SYS1_CTRL + */ +#define PRCM_CPUA_RESET_BIT HAL_BIT(1) /* R */ +#define PRCM_SYS1_RESET_BIT HAL_BIT(0) /* R */ + +/* + * bit field definition of PRCM->SYS1_STATUS + */ +#define PRCM_CPUN_DEEPSLEEP_LOCK_BIT HAL_BIT(3) /* R/W */ +#define PRCM_CPUA_SLEEP_STATUS_BIT HAL_BIT(2) /* R */ +#define PRCM_CPUA_DEEPSLEEP_STATUS_BIT HAL_BIT(1) /* R */ +#define PRCM_SYS1_ALIVE_BIT HAL_BIT(0) /* R */ + +/* + * bit field definition of PRCM->SYS2_CTRL + */ +#define PRCM_SYS2_ISOLATION_EN_BIT HAL_BIT(2) /* R/W */ +#define PRCM_CPUN_RESET_BIT HAL_BIT(1) /* R/W */ +#define PRCM_SYS2_RESET_BIT HAL_BIT(0) /* R/W */ + +/* + * bit field definition of PRCM->SYS2_STATUS + */ +#define PRCM_CPUA_DEEPSLEEP_LOCK_BIT HAL_BIT(3) /* R */ +#define PRCM_CPUN_SLEEP_STATUS_BIT HAL_BIT(2) /* R */ +#define PRCM_CPUN_DEEPSLEEP_STATUS_BIT HAL_BIT(1) /* R */ +#define PRCM_SYS2_ALIVE_BIT HAL_BIT(0) /* R */ + +/* + * bit field definition of PRCM->SYS3_CTRL + */ +#define PRCM_SYS3_RESET_BIT HAL_BIT(0) /* R */ + +/* + * bit field definition of PRCM->SYS3_STATUS + */ +#define PRCM_CPUW_SLEEP_STATUS_BIT HAL_BIT(2) /* R */ +#define PRCM_CPUW_DEEPSLEEP_STATUS_BIT HAL_BIT(1) /* R */ +#define PRCM_SYS3_ALIVE_BIT HAL_BIT(0) /* R */ + +/* + * bit field definition of + * - PRCM->SYS1_WAKEUP_CTRL (R/W) + * - PRCM->SYS2_WAKEUP_CTRL (R) + * - PRCM->SYS1_SLEEP_CTRL (R/W) + * - PRCM->SYS2_SLEEP_CTRL (R) + */ +#define PRCM_SYS_WS_PWR_VAL_KEEP 0U /* for all */ +#define PRCM_SYS_WS_PWR_VAL_ON 1U /* for PRCM->SYSx_WAKEUP_CTRL */ +#define PRCM_SYS_WS_PWR_VAL_OFF 1U /* for PRCM->SYSx_SLEEP_CTRL */ + +#define PRCM_SYS_WS_PWR_TYPE_LDO1 0U +#define PRCM_SYS_WS_PWR_TYPE_SYS1_PWR1 1U +#define PRCM_SYS_WS_PWR_TYPE_SYS1_PWR2 2U +#define PRCM_SYS_WS_PWR_TYPE_SYS1_SRAM_PWR1 3U +#define PRCM_SYS_WS_PWR_TYPE_SYS2_SRAM_PWR2 4U +#define PRCM_SYS_WS_PWR_TYPE_SYS2_PWR3 5U + +#define PRCM_SYS_WS_PWR_FLAG(val, type) ((val) << (type)) +#define PRCM_SYS_WS_PWR_FLAGS_MASK 0x3FU + +/* DCXO_STABLE_REF_TIME */ + +/* DPLL_STABLE_REF_TIME */ + +/* LDO_STABLE_REF_TIME */ + +/* DIG_SWITCH_REF_TIME */ + +/* + * bit field definition of PRCM->SRAM_VOLT_CTRL + */ +typedef enum { + PRCM_SRAM_VOLT_1V10 = 0U, + PRCM_SRAM_VOLT_1V00 = 1U, + PRCM_SRAM_VOLT_0V90 = 2U, + PRCM_SRAM_VOLT_0V80 = 3U, + PRCM_SRAM_VOLT_0V75 = 4U, + PRCM_SRAM_VOLT_0V70 = 5U, + PRCM_SRAM_VOLT_0V65 = 6U, + PRCM_SRAM_VOLT_0V60 = 7U +} PRCM_SRAMVolt; + +#define PRCM_SRAM_WORK_VOLT_SHIFT 16 /* R/W */ +#define PRCM_SRAM_WORK_VOLT_MASK (0x7U << PRCM_SRAM_WORK_VOLT_SHIFT) + +#define PRCM_SRAM_RETEN_VOLT_SHIFT 0 /* R/W */ +#define PRCM_SRAM_RETEN_VOLT_MASK (0x7U << PRCM_SRAM_RETEN_VOLT_SHIFT) + +/* BANDGAP_STABLE_REF_TIME */ +#define PRCM_BANDGAP_STABLE_REF_TIME_MASK 0x0FU + +/* DCDC_STABLE_REF_TIME */ +#define PRCM_LDOTOPWM_STABLE_RFE_TIME_SHIFT 16 /* R/W */ +#define PRCM_LDOTOPWM_STABLE_RFE_TIME_MASK 0x3FFU +#define PRCM_DCDC_STABLE_REF_TIME_MASK 0x3FU + +/* + * bit field definition of PRCM->CPUA_BOOT_FLAG + */ +#define PRCM_CPUA_BOOT_FLAG_WR_LOCK (0x429BU << 16) /* W */ + +#define PRCM_CPUA_BOOT_FLAG_SHIFT 0 /* R/W */ +#define PRCM_CPUA_BOOT_FLAG_MASK (0xFU << PRCM_CPUA_BOOT_FLAG_SHIFT) +typedef enum { + PRCM_CPUA_BOOT_FROM_COLD_RESET = (0U << PRCM_CPUA_BOOT_FLAG_SHIFT), + PRCM_CPUA_BOOT_FROM_DEEPSLEEP = (1U << PRCM_CPUA_BOOT_FLAG_SHIFT), + PRCM_CPUA_BOOT_FROM_SYS_UPDATE = (2U << PRCM_CPUA_BOOT_FLAG_SHIFT) +} PRCM_CPUABootFlag; + +/* PRCM->CPUA_BOOT_ADDR */ + +/* PRCM->CPUA_BOOT_ARG */ + +/* + * bit field definition of PRCM->CPUN_BOOT_FLAG + */ +#define PRCM_CPUN_BOOT_FLAG_WR_LOCK (0x429CU << 16) /* R */ + +#define PRCM_CPUN_BOOT_FLAG_SHIFT 0 /* R */ +#define PRCM_CPUN_BOOT_FLAG_MASK (0xFU << PRCM_CPUN_BOOT_FLAG_SHIFT) +typedef enum { + PRCM_CPUN_BOOT_FROM_COLD_RESET = (0 << PRCM_CPUN_BOOT_FLAG_SHIFT), + PRCM_CPUN_BOOT_FROM_DEEPSLEEP = (1 << PRCM_CPUN_BOOT_FLAG_SHIFT) +} PRCM_CPUNBootFlag; + +/* PRCM->CPUN_BOOT_ADDR */ + +/* PRCM->CPUN_BOOT_ARG */ + +/* PRCM->CPUA_PRIV_REG */ + +/* + * bit field definition of + * - PRCM->CPUA_WAKE_TIMER_CNT + * - PRCM->CPUN_WAKE_TIMER_CNT + */ +#define PRCM_CPUx_WAKE_TIMER_EN_BIT HAL_BIT(31) /* R/W */ +#define PRCM_CPUx_WAKE_TIMER_CUR_VAL_MASK 0x7FFFFFFFU /* R */ + +/* + * bit field definition of + * - PRCM->CPUA_WAKE_TIMER_CMP + * - PRCM->CPUN_WAKE_TIMER_CMP + */ +#define PRCM_CPUx_WAKE_TIMER_PENDING_BIT HAL_BIT(31) /* R/W */ +#define PRCM_CPUx_WAKE_TIMER_CMP_VAL_MASK 0x7FFFFFFFU /* R/W */ + +/* + * bit field definition of + * - PRCM->CPUA_WAKE_IO_EN + * - PRCM->CPUA_WAKE_IO_MODE + * - PRCM->CPUA_WAKE_IO_STATUS + * - PRCM->CPUA_WAKE_IO_HOLD + */ + +#define PRCM_WAKE_IO_MASK (0x3FFU) +typedef enum { + PRCM_WAKE_IO_0 = HAL_BIT(0), + PRCM_WAKE_IO_1 = HAL_BIT(1), + PRCM_WAKE_IO_2 = HAL_BIT(2), + PRCM_WAKE_IO_3 = HAL_BIT(3), + PRCM_WAKE_IO_4 = HAL_BIT(4), + PRCM_WAKE_IO_5 = HAL_BIT(5), + PRCM_WAKE_IO_6 = HAL_BIT(6), + PRCM_WAKE_IO_7 = HAL_BIT(7), + PRCM_WAKE_IO_8 = HAL_BIT(8), + PRCM_WAKE_IO_9 = HAL_BIT(9) +} PRCM_WakeIO; + +typedef enum { + PRCM_WAKE_IO_EVT_FALLING_EDGE = 0U, + PRCM_WAKE_IO_EVT_RISING_EDGE = 1U, +} PRCM_WakeIOEvent; + +/* + * bit field definition of PRCM->CPUA_WAKE_IO_GLOBAL_EN + */ +#define PRCM_WAKE_IO_GLOBAL_EN_BIT HAL_BIT(0) + +/* + * bit field definition of PRCM->CPUA_PRCM_REG + */ +#define PRCM_CPUA_PRCM_REG_BIT HAL_BIT(0) + +/* DCDC_PARAM_CTRL */ +#define PRCM_DCDC_BANDGAP_TRIM_SHIFT 25 +#define PRCM_DCDC_BANDGAP_TRIM_MASK (0xFU << PRCM_DCDC_BANDGAP_TRIM_SHIFT) + +/* ANA_BANDGAP */ + +/* CLK_LDO_PARAM */ + +/* DIG_LDO_PARAM */ +#define PRCM_DIG_LDO_BANDGAP_TRIM_SHIFT 8 +#define PRCM_DIG_LDO_BANDGAP_TRIM_MASK (0xFU << PRCM_DIG_LDO_BANDGAP_TRIM_SHIFT) + +/* DPLL_STATUS */ + +/******************************************************************************/ +void HAL_PRCM_SetDCDCVoltage(PRCM_DCDCVolt volt); +uint32_t HAL_PRCM_GetSysPowerEnableFlags(void); +void HAL_PRCM_EnableSys2Power(void); +void HAL_PRCM_DisableSys2Power(void); +void HAL_PRCM_SetLDO1Voltage(PRCM_LDO1Volt volt); +uint32_t HAL_PRCM_GetLDOEnableFlags(void); + +void HAL_PRCM_SetLFCLKSource(PRCM_LFCLKSrc src); +void HAL_PRCM_SetHOSCType(PRCM_HOSCType type); +uint32_t HAL_PRCM_GetHOSCType(void); +uint32_t HAL_PRCM_GetHFClock(void); +uint32_t HAL_PRCM_GetInter32KFreq(void); +uint32_t HAL_PRCM_EnableInter32KCalib(void); +uint32_t HAL_PRCM_DisableInter32KCalib(void); +#if 0 +void HAL_PRCM_SetSysPLLParam(PRCM_SysPLLParam param); +void HAL_PRCM_EnableSysPLL(void); +#else +void HAL_PRCM_SetSysPLL(PRCM_SysPLLParam param); +#endif +void HAL_PRCM_DisableSysPLL(void); +void HAL_PRCM_SetCPUAClk(PRCM_CPUClkSrc src, PRCM_SysClkFactor factor); +void HAL_PRCM_DisCLK1(PRCM_SysClkFactor factor); +uint32_t HAL_PRCM_GetCPUAClk(void); +void HAL_PRCM_SetAudioPLLParam(PRCM_AudPLLParam param); +void HAL_PRCM_EnableAudioPLL(void); +void HAL_PRCM_DisableAudioPLL(void); +void HAL_PRCM_SetDevClock(PRCM_DevClkFactor factor); +uint32_t HAL_PRCM_GetDevClock(void); +void HAL_PRCM_SetAudioPLLPatternParam(PRCM_AudPLLPatParam param); +void HAL_PRCM_EnableAudioPLLPattern(void); +void HAL_PRCM_DisableAudioPLLPattern(void); + +int HAL_PRCM_IsCPUAResetRelease(void); +int HAL_PRCM_IsSys1ResetRelease(void); +void HAL_PRCM_AllowCPUNDeepSleep(void); +void HAL_PRCM_DisallowCPUNDeepSleep(void); +int HAL_PRCM_IsCPUNDeepSleepAllowed(void); +int HAL_PRCM_IsCPUASleep(void); +int HAL_PRCM_IsCPUADeepSleep(void); +int HAL_PRCM_IsSys1Alive(void); + +void HAL_PRCM_DisableSys2(void); +void HAL_PRCM_EnableSys2Isolation(void); +void HAL_PRCM_DisableSys2Isolation(void); +void HAL_PRCM_ForceCPUNReset(void); +void HAL_PRCM_ReleaseCPUNReset(void); +int HAL_PRCM_IsCPUNReleased(void); +void HAL_PRCM_ForceSys2Reset(void); +void HAL_PRCM_ReleaseSys2Reset(void); + +int HAL_PRCM_IsCPUADeepSleepAllowed(void); +int HAL_PRCM_IsCPUNSleep(void); +int HAL_PRCM_IsCPUNDeepSleep(void); +int HAL_PRCM_IsSys2Alive(void); +int HAL_PRCM_IsSys3Alive(void); +void HAL_PRCM_SetSys1WakeupPowerFlags(uint32_t flags); +uint32_t HAL_PRCM_GetSys2WakeupPowerFlags(void); +uint32_t HAL_PRCM_GetSys1SleepPowerFlags(void); +void HAL_PRCM_SetSys1SleepPowerFlags(uint32_t flags); +uint32_t HAL_PRCM_GetSys2SleepPowerFlags(void); +void HAL_PRCM_SetSRAMVoltage(PRCM_SRAMVolt workVolt, PRCM_SRAMVolt retenVolt); +void HAL_PRCM_SetBANDGAPSTABLE_TIME(uint32_t time); +void HAL_PRCM_SetDCDCSTABLE_TIME(uint32_t time); +uint32_t HAL_PRCM_GetBANDGAPSTABLE_TIME(void); +uint32_t HAL_PRCM_GetDCDCSTABLE_TIME(void); +void HAL_PRCM_SetCPUABootFlag(PRCM_CPUABootFlag flag); +uint32_t HAL_PRCM_GetCPUABootFlag(void); +void HAL_PRCM_SetCPUABootAddr(uint32_t addr); +uint32_t HAL_PRCM_GetCPUABootAddr(void); +void HAL_PRCM_SetCPUABootArg(uint32_t arg); +uint32_t HAL_PRCM_GetCPUABootArg(void); +void HAL_PRCM_SetCPUAPrivateData(uint32_t data); +uint32_t HAL_PRCM_GetCPUAPrivateData(void); + +uint32_t HAL_PRCM_GetWakeupTimerEnable(void); +void HAL_PRCM_WakeupTimerEnable(void); +void HAL_PRCM_WakeupTimerDisable(void); +uint32_t HAL_PRCM_WakeupTimerGetCurrentValue(void); +uint32_t HAL_PRCM_GetWakeupTimerPending(void); +void HAL_PRCM_ClearWakeupTimerPending(void); +void HAL_PRCM_WakeupTimerSetCompareValue(uint32_t val); +uint32_t HAL_PRCM_WakeupTimerGetCompareValue(void); + +void HAL_PRCM_WakeupIOEnable(uint32_t ioMask); +void HAL_PRCM_WakeupIODisable(uint32_t ioMask); +void HAL_PRCM_WakeupIOSetRisingEvent(uint32_t ioMask); +void HAL_PRCM_WakeupIOSetFallingEvent(uint32_t ioMask); +uint32_t HAL_PRCM_WakeupIOGetEventStatus(void); +int HAL_PRCM_WakeupIOIsEventDetected(uint32_t ioMask); +void HAL_PRCM_WakeupIOClearEventDetected(uint32_t ioMask); +void HAL_PRCM_WakeupIOEnableCfgHold(uint32_t ioMask); +void HAL_PRCM_WakeupIODisableCfgHold(uint32_t ioMask); +void HAL_PRCM_WakeupIOEnableGlobal(void); +void HAL_PRCM_WakeupIODisableGlobal(void); +void HAL_PRCM_Start(void); + +#ifdef __cplusplus +} +#endif + +#endif /* _DRIVER_CHIP_HAL_PRCM_H_ */ diff --git a/platform/mcu/xr871/include/driver/chip/hal_pwm.h b/platform/mcu/xr871/include/driver/chip/hal_pwm.h new file mode 100644 index 0000000000..da1992bd59 --- /dev/null +++ b/platform/mcu/xr871/include/driver/chip/hal_pwm.h @@ -0,0 +1,285 @@ +/** + * @file hal_pwm.h + * @author XRADIO IOT WLAN Team + */ + +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _DRIVER_CHIP_HAL_PWM_H_ +#define _DRIVER_CHIP_HAL_PWM_H_ + +#include "driver/chip/hal_def.h" +#include "driver/chip/hal_gpio.h" +#include "driver/chip/hal_clock.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief PWM group id. + */ +typedef enum { + PWM_GROUP_0, /*!CTRL + */ +#define RTC_TEST_MODE_BIT HAL_BIT(31) +#define RTC_SIMULATION_BIT HAL_BIT(30) +#define RTC_WDAY_ALARM_HHMMSS_ACCESS_BIT HAL_BIT(2) +#define RTC_DDHHMMSS_ACCESS_BIT HAL_BIT(1) +#define RTC_YYMMDD_ACCESS_BIT HAL_BIT(0) + +/* + * Bit field definition of RTC->YYMMDD + */ +#define RTC_LEAP_YEAR_BIT HAL_BIT(24) + +#define RTC_YEAR_SHIFT 16 /* R/W, [0, 255] */ +#define RTC_YEAR_VMASK 0xFF +#define RTC_YEAR_MIN 0 +#define RTC_YEAR_MAX 255 + +#define RTC_MONTH_SHIFT 8 /* R/W, [1, 12] */ +#define RTC_MONTH_VMASK 0xF +#define RTC_MONTH_MIN 1 +#define RTC_MONTH_MAX 12 + +#define RTC_MDAY_SHIFT 0 /* R/W, [1, 31] */ +#define RTC_MDAY_VMASK 0x1F +#define RTC_MDAY_MIN 1 +#define RTC_MDAY_MAX 31 + +/* + * Bit field definition of RTC->DDHHMMSS + */ +#define RTC_WDAY_SHIFT 29 /* R/W */ +#define RTC_WDAY_VMASK 0x7 +typedef enum { + RTC_WDAY_MONDAY = 0U, + RTC_WDAY_TUESDAY = 1U, + RTC_WDAY_WEDNESDAY = 2U, + RTC_WDAY_THURSDAY = 3U, + RTC_WDAY_FRIDAY = 4U, + RTC_WDAY_SATURDAY = 5U, + RTC_WDAY_SUNDAY = 6U +} RTC_WeekDay; + +#define RTC_HOUR_SHIFT 16 /* R/W, [0, 23] */ +#define RTC_HOUR_VMASK 0x1F +#define RTC_HOUR_MIN 0 +#define RTC_HOUR_MAX 23 + +#define RTC_MINUTE_SHIFT 8 /* R/W, [0, 59] */ +#define RTC_MINUTE_VMASK 0x3F +#define RTC_MINUTE_MIN 0 +#define RTC_MINUTE_MAX 59 + +#define RTC_SECOND_SHIFT 0 /* R/W, [0, 59] */ +#define RTC_SECOND_VMASK 0x3F +#define RTC_SECOND_MIN 0 +#define RTC_SECOND_MAX 59 + +/* RTC->SEC_ALARM_LOAD_VAL */ + +/* RTC->SEC_ALARM_CUR_VAL */ + +/* RTC->SEC_ALARM_EN */ +#define RTC_SEC_ALARM_EN_BIT HAL_BIT(0) + +/* RTC->SEC_ALARM_IRQ_EN */ +#define RTC_SEC_ALARM_IRQ_EN_BIT HAL_BIT(0) + +/* RTC->SEC_ALARM_IRQ_STATUS */ +#define RTC_SEC_ALARM_IRQ_PENDING_BIT HAL_BIT(0) + +/* RTC->WDAY_ALARM_DDHHMMSS */ +#define RTC_WDAY_ALARM_HOUR_SHIFT 16 /* R/W, [0, 23] */ +#define RTC_WDAY_ALARM_HOUR_VMASK 0x1F + +#define RTC_WDAY_ALARM_MINUTE_SHIFT 8 /* R/W, [0, 59] */ +#define RTC_WDAY_ALARM_MINUTE_VMASK 0x3F + +#define RTC_WDAY_ALARM_SECOND_SHIFT 0 /* R/W, [0, 59] */ +#define RTC_WDAY_ALARM_SECOND_VMASK 0x3F + +/* RTC->WDAY_ALARM_EN */ +#define RTC_WDAY_ALARM_EN_BIT(wday) HAL_BIT(wday) /* day is RTC_WeekDay */ +#define RTC_WDAY_ALARM_EN_MASK 0x7F + +/* RTC->WDAY_ALARM_IRQ_EN */ +#define RTC_WDAY_ALARM_IRQ_EN_BIT HAL_BIT(0) + +/* RTC->WDAY_ALARM_IRQ_STATUS */ +#define RTC_WDAY_ALARM_IRQ_PENDING_BIT HAL_BIT(0) + +/******************************************************************************/ + +/** @brief Type define of RTC alarm IRQ callback function */ +typedef void (*RTC_AlarmIRQCallback) (void *arg); + +/** + * @brief RTC second alarm starting parameters + */ +typedef struct { + uint32_t alarmSeconds; /* RTC second alarm's count down value */ + RTC_AlarmIRQCallback callback; /* RTC second alarm IRQ callback function */ + void *arg; /* Argument of RTC second alarm IRQ callback function */ +} RTC_SecAlarmStartParam; + +/** + * @brief RTC weekday alarm starting parameters + */ +typedef struct { + uint8_t alarmHour; /* RTC weekday alarm's hour, [0, 23] */ + uint8_t alarmMinute; /* RTC weekday alarm's minute, [0, 59] */ + uint8_t alarmSecond; /* RTC weekday alarm's second, [0, 59] */ + uint8_t alarmWDayMask; /* RTC weekday alarm's weekday, bit mask of RTC_WDAY_ALARM_EN_BIT(RTC_WeekDay) */ + RTC_AlarmIRQCallback callback; /* RTC weekday alarm IRQ callback function */ + void *arg; /* Argument of RTC weekday alarm IRQ callback function */ +} RTC_WDayAlarmStartParam; + +void HAL_RTC_SetYYMMDD(uint8_t isLeapYear, uint8_t year, uint8_t month, uint8_t mday); +void HAL_RTC_SetDDHHMMSS(RTC_WeekDay wday, uint8_t hour, uint8_t minute, uint8_t second); + +void HAL_RTC_GetYYMMDD(uint8_t *isLeapYear, uint8_t *year, uint8_t *month, uint8_t *mday); +void HAL_RTC_GetDDHHMMSS(RTC_WeekDay *wday, uint8_t *hour, uint8_t *minute, uint8_t *second) ; + +void HAL_RTC_StartSecAlarm(const RTC_SecAlarmStartParam *param); +void HAL_RTC_StopSecAlarm(void); + +void HAL_RTC_StartWDayAlarm(const RTC_WDayAlarmStartParam *param); +void HAL_RTC_StopWDayAlarm(void); + +uint64_t HAL_RTC_GetFreeRunTime(void); + +#ifdef __cplusplus +} +#endif + +#endif /* _DRIVER_CHIP_HAL_RTC_H_ */ diff --git a/platform/mcu/xr871/include/driver/chip/hal_spi.h b/platform/mcu/xr871/include/driver/chip/hal_spi.h new file mode 100644 index 0000000000..f92af35026 --- /dev/null +++ b/platform/mcu/xr871/include/driver/chip/hal_spi.h @@ -0,0 +1,482 @@ +/** + * @file hal_spi.h + * @author XRADIO IOT WLAN Team + */ + +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _DRIVER_CHIP_HAL_SPI_H_ +#define _DRIVER_CHIP_HAL_SPI_H_ + +#include +#include "driver/chip/hal_def.h" +#include "driver/chip/hal_dma.h" +#include "driver/chip/hal_gpio.h" +#include "sys/xr_debug.h" + +#ifdef __cplusplus +extern "C" { +#endif +/********************** private ************************************/ + +/** + * @brief Serial Peripheral Interface + */ +typedef struct +{ + __I uint32_t VER; /*!< SPI Version number Register, Address offset: 0x00 */ + __IO uint32_t CTRL; /*!< SPI Global Control Register, Address offset: 0x04 */ + __IO uint32_t TCTRL; /*!< SPI Transfer Control Register, Address offset: 0x08 */ + uint32_t RESERVED1[1]; /*!< Reserved, Address offset: 0x0C */ + __IO uint32_t IER; /*!< SPI Interrupt Control Register, Address offset: 0x10 */ + __IO uint32_t STA; /*!< SPI Interrupt Status Register, Address offset: 0x14 */ + __IO uint32_t FCTL; /*!< SPI FIFO Control Register, Address offset: 0x18 */ + __I uint32_t FST; /*!< SPI FIFO Status Register, Address offset: 0x1C */ + __IO uint32_t WAIT; /*!< SPI Wait Clock Counter Register, Address offset: 0x20 */ + __IO uint32_t CCTR; /*!< SPI Clock Rate Control Register, Address offset: 0x24 */ + uint32_t RESERVED2[2]; /*!< Reserved, Address offset: 0x28-0x2C */ + __IO uint32_t BC; /*!< SPI Master mode Burst Control Register, Address offset: 0x30 */ + __IO uint32_t TC; /*!< SPI Master mode Transmit Counter Register, Address offset: 0x34 */ + __IO uint32_t BCC; /*!< SPI Burst Control Register, Address offset: 0x38 */ + uint32_t RESERVED3[19]; /*!< Reserved, Address offset: 0x3C-0x84 */ + __IO uint32_t NDMA_MODE_CTRL; /*!< SPI Nomal DMA Mode Control Regist Address offset: 0x88 */ + uint32_t RESERVED4[93]; /*!< Reserved, Address offset: 0x8C-0x1FC */ + __IO uint32_t TXD; /*!< SPI TX Date Register, Address offset: 0x200 */ + uint32_t RESERVED5[63]; /*!< Reserved, Address offset: 0x204-0x2FC */ + __I uint32_t RXD; /*!< SPI RX Date Register, Address offset: 0x300 */ +} SPI_T; + +/* + * @brief SPI Global Control Register + */ +#define SPI_CTRL_RST_SHIFT (31) +#define SPI_CTRL_RST_MASK (0x1U << SPI_CTRL_RST_SHIFT) + +#define SPI_CTRL_TP_EN_SHIFT (7) +#define SPI_CTRL_TP_EN_MASK (0x1U << SPI_CTRL_TP_EN_SHIFT) + +#define SPI_CTRL_MODE_SHIFT (1) +#define SPI_CTRL_MODE_MASK (0x1U << SPI_CTRL_MODE_SHIFT) +typedef enum { + SPI_CTRL_MODE_SLAVE = 0 << SPI_CTRL_MODE_SHIFT, + SPI_CTRL_MODE_MASTER = 1 << SPI_CTRL_MODE_SHIFT +} SPI_CTRL_Mode; + +#define SPI_CTRL_EN_SHIFT (0) +#define SPI_CTRL_EN_MASK (0x1U << SPI_CTRL_EN_SHIFT) +typedef enum { + SPI_CTRL_EN_DISABLE = 0 << SPI_CTRL_EN_SHIFT, + SPI_CTRL_EN_ENABLE = 1 << SPI_CTRL_EN_SHIFT +} SPI_CTRL_En; + + +/* + * @brief SPI Transfer Control Register + */ +#define SPI_TCTRL_XCH_SHIFT (31) +#define SPI_TCTRL_XCH_MASK (0x1U << SPI_TCTRL_XCH_SHIFT) +typedef enum { + SPI_TCTRL_XCH_IDLE = 0 << SPI_TCTRL_XCH_SHIFT, + SPI_TCTRL_XCH_START = 1 << SPI_TCTRL_XCH_SHIFT +} SPI_TCTRL_Xch; + +#define SPI_TCTRL_SDDM_SHIFT (14) +#define SPI_TCTRL_SDDM_MASK (0x0U << SPI_TCTRL_SDDM_SHIFT) +typedef enum { + SPI_TCTRL_SDDM_SEND_NODELAY = 0 << SPI_TCTRL_SDDM_SHIFT, + SPI_TCTRL_SDDM_SEND_DELAY = 1 << SPI_TCTRL_SDDM_SHIFT +} SPI_TCTRL_Sddm; + +#define SPI_TCTRL_SDM_SHIFT (13) +#define SPI_TCTRL_SDM_MASK (0x1U << SPI_TCTRL_SDM_SHIFT) +typedef enum { + SPI_TCTRL_SDM_SAMPLE_NODELAY = 1 << SPI_TCTRL_SDM_SHIFT, + SPI_TCTRL_SDM_SAMPLE_DELAY = 0 << SPI_TCTRL_SDM_SHIFT +} SPI_TCTRL_Sdm; + +#define SPI_TCTRL_FBS_SHIFT (12) +#define SPI_TCTRL_FBS_MASK (0x1U << SPI_TCTRL_FBS_SHIFT) +typedef enum { + SPI_TCTRL_FBS_MSB = 0 << SPI_TCTRL_FBS_SHIFT, + SPI_TCTRL_FBS_LSB = 1 << SPI_TCTRL_FBS_SHIFT +} SPI_TCTRL_Fbs; + +#define SPI_TCTRL_SDC_SHIFT (11) +#define SPI_TCTRL_SDC_MASK (0x1U << SPI_TCTRL_SDC_SHIFT) + +#define SPI_TCTRL_RPSM_SHIFT (10) +#define SPI_TCTRL_RPSM_MASK (0x1U << SPI_TCTRL_RPSM_SHIFT) + +#define SPI_TCTRL_DDB_SHIFT (9) +#define SPI_TCTRL_DDB_MASK (0x1U << SPI_TCTRL_DDB_SHIFT) + +#define SPI_TCTRL_DHB_SHIFT (8) +#define SPI_TCTRL_DHB_MASK (0x1U << SPI_TCTRL_DHB_SHIFT) +typedef enum { + SPI_TCTRL_DHB_FULL_DUPLEX = 0 << SPI_TCTRL_DHB_SHIFT, + SPI_TCTRL_DHB_HALF_DUPLEX = 1 << SPI_TCTRL_DHB_SHIFT +} SPI_TCTRL_DHB_Duplex; + +#define SPI_TCTRL_SS_LEVEL_SHIFT (7) +#define SPI_TCTRL_SS_LEVEL_MASK (0x1U << SPI_TCTRL_SS_LEVEL_SHIFT) + +#define SPI_TCTRL_SS_OWNER_SHIFT (6) +#define SPI_TCTRL_SS_OWNER_MASK (0x1U << SPI_TCTRL_SS_OWNER_SHIFT) +typedef enum { + SPI_TCTRL_SS_OWNER_CONTROLLER = 0 << SPI_TCTRL_SS_OWNER_SHIFT, + SPI_TCTRL_SS_OWNER_SOFTWARE = 1 << SPI_TCTRL_SS_OWNER_SHIFT +} SPI_TCTRL_SS_OWNER; + +#define SPI_TCTRL_SS_SEL_SHIFT (4) +#define SPI_TCTRL_SS_SEL_MASK (0x3U << SPI_TCTRL_SS_SEL_SHIFT) +typedef enum { + SPI_TCTRL_SS_SEL_SS0 = 0 << SPI_TCTRL_SS_SEL_SHIFT, + SPI_TCTRL_SS_SEL_SS1 = 1 << SPI_TCTRL_SS_SEL_SHIFT, + SPI_TCTRL_SS_SEL_SS2 = 2 << SPI_TCTRL_SS_SEL_SHIFT, + SPI_TCTRL_SS_SEL_SS3 = 3 << SPI_TCTRL_SS_SEL_SHIFT +} SPI_TCTRL_SS_Sel; + +#define SPI_TCTRL_SS_CTL_SHIFT (3) +#define SPI_TCTRL_SS_CTL_MASK (0x1U << SPI_TCTRL_SS_CTL_SHIFT) + +#define SPI_TCTRL_SPOL_SHIFT (2) +#define SPI_TCTRL_SPOL_MASK (0x1U << SPI_TCTRL_SPOL_SHIFT) + +#define SPI_TCTRL_CPOL_SHIFT (1) +#define SPI_TCTRL_CPOL_MASK (0x1U << SPI_TCTRL_CPOL_SHIFT) +typedef enum { + SPI_TCTRL_CPOL_HIGH = 0 << SPI_TCTRL_CPOL_SHIFT, + SPI_TCTRL_CPOL_LOW = 1 << SPI_TCTRL_CPOL_SHIFT +} SPI_TCTRL_Cpol; + +#define SPI_TCTRL_CPHA_SHIFT (0) +#define SPI_TCTRL_CPHA_MASK (0x1U << SPI_TCTRL_CPHA_SHIFT) +typedef enum { + SPI_TCTRL_CPHA_PHASE0 = 0 << SPI_TCTRL_CPHA_SHIFT, + SPI_TCTRL_CPHA_PHASE1 = 1 << SPI_TCTRL_CPHA_SHIFT +} SPI_TCTRL_Cpha; + +typedef enum { + SPI_SCLK_Mode0 = 0 << SPI_TCTRL_CPHA_SHIFT, + SPI_SCLK_Mode1 = 1 << SPI_TCTRL_CPHA_SHIFT, + SPI_SCLK_Mode2 = 2 << SPI_TCTRL_CPHA_SHIFT, + SPI_SCLK_Mode3 = 3 << SPI_TCTRL_CPHA_SHIFT +} SPI_SCLK_Mode; +/* + * @brief SPI Interrupt Control Register + */ +#define SPI_IER_SS_INT_EN_SHIFT (13) +#define SPI_IER_SS_INT_EN_MASK (0x1U << SPI_IER_SS_INT_EN_SHIFT) + +#define SPI_IER_TC_INT_EN_SHIFT (12) +#define SPI_IER_TC_INT_EN_MASK (0x1U << SPI_IER_TC_INT_EN_SHIFT) + +#define SPI_IER_TF_UDR_INT_EN_SHIFT (11) +#define SPI_IER_TF_UDR_INT_EN_MASK (0x1U << SPI_IER_TF_UDR_INT_EN_SHIFT) + +#define SPI_IER_TF_OVF_INT_EN_SHIFT (10) +#define SPI_IER_TF_OVF_INT_EN_MASK (0x1U << SPI_IER_TF_OVF_INT_EN_SHIFT) + +#define SPI_IER_RF_UDR_INT_EN_SHIFT (9) +#define SPI_IER_RF_UDR_INT_EN_MASK (0x1U << SPI_IER_RF_UDR_INT_EN_SHIFT) + +#define SPI_IER_RF_OVF_INT_EN_SHIFT (8) +#define SPI_IER_RF_OVF_INT_EN_MASK (0x1U << SPI_IER_RF_OVF_INT_EN_SHIFT) + +#define SPI_IER_TF_FUL_INT_EN_SHIFT (6) +#define SPI_IER_TF_FUL_INT_EN_MASK (0x1U << SPI_IER_TF_FUL_INT_EN_SHIFT) + +#define SPI_IER_TX_EMP_INT_EN_SHIFT (5) +#define SPI_IER_TX_EMP_INT_EN_MASK (0x1U << SPI_IER_TX_EMP_INT_EN_SHIFT) + +#define SPI_IER_TX_ERQ_INT_EN_SHIFT (4) +#define SPI_IER_TX_ERQ_INT_EN_MASK (0x1U << SPI_IER_TX_ERQ_INT_EN_SHIFT) + +#define SPI_IER_RF_FUL_INT_EN_SHIFT (2) +#define SPI_IER_RF_FUL_INT_EN_MASK (0x1U << SPI_IER_RF_FUL_INT_EN_SHIFT) + +#define SPI_IER_RX_EMP_INT_EN_SHIFT (1) +#define SPI_IER_RX_EMP_INT_EN_MASK (0x1U << SPI_IER_RX_EMP_INT_EN_SHIFT) + +#define SPI_IER_RF_RDY_INT_EN_SHIFT (0) +#define SPI_IER_RF_RDY_INT_EN_MASK (0x1U << SPI_IER_RF_RDY_INT_EN_SHIFT) + +/* + * @brief SPI Interrupt Status Register + */ +#define SPI_STA_SSI_SHIFT (13) +#define SPI_STA_SSI_MASK (0x1U << SPI_STA_SSI_SHIFT) + +#define SPI_STA_TC_SHIFT (12) +#define SPI_STA_TC_MASK (0x1U << SPI_STA_TC_SHIFT) + +#define SPI_STA_TF_UDF_SHIFT (11) +#define SPI_STA_TF_UDF_MASK (0x1U << SPI_STA_TF_UDF_SHIFT) + +#define SPI_STA_TF_OVF_SHIFT (10) +#define SPI_STA_TF_OVF_MASK (0x1U << SPI_STA_TF_OVF_SHIFT) + +#define SPI_STA_RX_UDF_SHIFT (9) +#define SPI_STA_RX_UDF_MASK (0x1U << SPI_STA_RX_UDF_SHIFT) + +#define SPI_STA_RX_OVF_SHIFT (8) +#define SPI_STA_RX_OVF_MASK (0x1U << SPI_STA_RX_OVF_SHIFT) + +#define SPI_STA_TX_FULL_SHIFT (6) +#define SPI_STA_TX_FULL_MASK (0x1U << SPI_STA_TX_FULL_SHIFT) + +#define SPI_STA_TX_EMP_SHIFT (5) +#define SPI_STA_TX_EMP_MASK (0x1U << SPI_STA_TX_EMP_SHIFT) + +#define SPI_STA_TX_READY_SHIFT (4) +#define SPI_STA_TX_READY_MASK (0x1U << SPI_STA_TX_READY_SHIFT) + +#define SPI_STA_RX_FULL_SHIFT (2) +#define SPI_STA_RX_FULL_MASK (0x1U << SPI_STA_RX_FULL_SHIFT) + +#define SPI_STA_RX_EMP_SHIFT (1) +#define SPI_STA_RX_EMP_MASK (0x1U << SPI_STA_RX_EMP_SHIFT) + +#define SPI_STA_RX_RDY_SHIFT (0) +#define SPI_STA_RX_RDY_MASK (0x1U << SPI_STA_RX_RDY_SHIFT) + +/* + * @brief SPI FIFO Control Register + */ +#define SPI_FCTL_TF_RST_SHIFT (31) +#define SPI_FCTL_TF_RST_MASK (0x1U << SPI_FCTL_TF_RST_SHIFT) + +#define SPI_FCTL_TF_TEST_EN_SHIFT (30) +#define SPI_FCTL_TF_TEST_EN_MASK (0x1U << SPI_FCTL_TF_TEST_EN_SHIFT) + +#define SPI_FCTL_TF_DRQ_EN_SHIFT (24) +#define SPI_FCTL_TF_DRQ_EN_MASK (0x1U << SPI_FCTL_TF_DRQ_EN_SHIFT) +#define SPI_FCTL_TF_DRQ_EN_BIT HAL_BIT(24) + +#define SPI_FCTL_TX_TRIG_LEVEL_SHIFT (16) +#define SPI_FCTL_TX_TRIG_LEVEL_MASK (0xFFU << SPI_FCTL_TX_TRIG_LEVEL_SHIFT) + +#define SPI_FCTL_RF_RST_SHIFT (15) +#define SPI_FCTL_RF_RST_MASK (0x1U << SPI_FCTL_RF_RST_SHIFT) + +#define SPI_FCTL_RF_TEST_SHIFT (14) +#define SPI_FCTL_RF_TEST_MASK (0x1U << SPI_FCTL_RF_TEST_SHIFT) + +#define SPI_FCTL_RF_DRQ_EN_SHIFT (8) +#define SPI_FCTL_RF_DRQ_EN_MASK (0x1U << SPI_FCTL_RF_DRQ_EN_SHIFT) + +#define SPI_FCTL_RX_TRIG_LEVEL_SHIFT (0) +#define SPI_FCTL_RX_TRIG_LEVEL_MASK (0xFFU << SPI_FCTL_RX_TRIG_LEVEL_SHIFT) + +/* + * @brief SPI FIFO Status Registe + */ +#define SPI_FST_TB_WR_SHIFT (31) +#define SPI_FST_TB_WR_MASK (0x1U << SPI_FST_TB_WR_SHIFT) + +#define SPI_FST_TB_CNT_SHIFT (28) +#define SPI_FST_TB_CNT_MASK (0x7U << SPI_FST_TB_CNT_SHIFT) + +#define SPI_FST_TF_CNT_SHIFT (16) +#define SPI_FST_TF_CNT_MASK (0xFFU << SPI_FST_TF_CNT_SHIFT) + +#define SPI_FST_RB_WR_SHIFT (15) +#define SPI_FST_RB_WR_MASK (0x1U << SPI_FST_RB_WR_SHIFT) + +#define SPI_FST_RB_CNT_SHIFT (12) +#define SPI_FST_RB_CNT_MASK (0x7U << SPI_FST_RB_CNT_SHIFT) + +#define SPI_FST_RF_CNT_SHIFT (0) +#define SPI_FST_RF_CNT_MASK (0xFFU << SPI_FST_RF_CNT_SHIFT) + +/* + * @brief SPI Wait Clock Counter Register + */ +#define SPI_WAIT_SWC_SHIFT (16) +#define SPI_WAIT_SWC_MASK (0xFU << SPI_WAIT_SWC_SHIFT) + +#define SPI_WAIT_WCC_SHIFT (0) +#define SPI_WAIT_WCC_MASK (0xFFFFU << SPI_WAIT_WCC_SHIFT) + +/* + * @brief SPI Clock Rate Control Register + */ +#define SPI_CCTR_DRS_SHIFT (12) +#define SPI_CCTR_DRS_MASK (0x1U << SPI_CCTR_DRS_SHIFT) +typedef enum { + SPI_CCTR_DRS_type_divRate1 = 0 << SPI_CCTR_DRS_SHIFT, + SPI_CCTR_DRS_type_divRate2 = 1 << SPI_CCTR_DRS_SHIFT +} SPI_CCTR_DRS_type; + +#define SPI_CCTR_CDR1_SHIFT (8) +#define SPI_CCTR_CDR1_MASK (0xFU << SPI_CCTR_CDR1_SHIFT) + +#define SPI_CCTR_CDR2_SHIFT (0) +#define SPI_CCTR_CDR2_MASK (0xFFU << SPI_CCTR_CDR2_SHIFT) + +/* + * @brief SPI Master mode Burst Control Register + */ +#define SPI_BC_MBC_SHIFT (0) +#define SPI_BC_MBC_MASK (0xFFFFFFU << SPI_BC_MBC_SHIFT) + +/* + * @brief SPI Master mode Transmit Counter Register + */ +#define SPI_TC_MWTC_SHIFT (0) +#define SPI_TC_MWTC_MASK (0xFFFFFFU << SPI_TC_MWTC_SHIFT) + +/* + * @brief SPI Burst Control Register + */ +#define SPI_BCC_DRM_SHIFT (28) +#define SPI_BCC_DRM_MASK (0x1U << SPI_BCC_DRM_SHIFT) + +#define SPI_BCC_DBC_SHIFT (24) +#define SPI_BCC_DBC_MASK (0xFU << SPI_BCC_DBC_SHIFT) + +#define SPI_BCC_STC_SHIFT (0) +#define SPI_BCC_STC_MASK (0xFFFFFFU << SPI_BCC_STC_SHIFT) + +/* + * @brief SPI Nomal DMA Mode Control Regist + */ +#define SPI_NDMA_MODE_CTRL_SHIFT (0) +#define SPI_NDMA_MODE_CTRL_MASK (0xFFU << SPI_NDMA_MODE_CTRL_SHIFT) + +/* + * @brief SPI TX Date Register + */ +#define SPI_TXD_SHIFT (0) +#define SPI_TXD_MASK (0xFFFFFFFFU << SPI_TXD_SHIFT) + +/* + * @brief SPI RX Date Register + */ +#define SPI_RXD_SHIFT (0) +#define SPI_RXD_MASK (0xFFFFFFFFU << SPI_RXD_SHIFT) + + +#define SPI_FIFO_SIZE (64) + +/************************ public **************************************/ +typedef enum { + SPI0 = 0, /*!< SPI0 controller */ + SPI1 = 1, /*!< SPI1 controller */ + SPI_NUM = 2 /*!< only support 2 SPI controller for now */ +} SPI_Port; + +typedef SPI_CTRL_Mode SPI_Mode; + +typedef SPI_TCTRL_Fbs SPI_FirstBit; + +typedef SPI_TCTRL_DHB_Duplex SPI_Duplex; + +typedef enum { + SPI_CS_MODE_AUTO, /*!< auto chip select mode */ + SPI_CS_MODE_MANUAL /*!< manual chip select mode */ +} SPI_CS_Mode; + +typedef SPI_TCTRL_SS_Sel SPI_CS; + +typedef SPI_SCLK_Mode SPI_SclkMode; + +typedef enum { + SPI_OPERATION_MODE_DMA, /*!< dma mode moving data */ + SPI_OPERATION_MODE_POLL /*!< cpu mode moving data */ +} SPI_Operation_Mode; + +typedef enum { + SPI_IO_MODE_NORMAL, /*!< MOSI to transmit data, MISO to receive data */ + SPI_IO_MODE_DUAL_RX, /*!< MOSI and MISO to receive data */ + SPI_IO_MODE_DUAL_TX, /*!< not support */ + SPI_IO_MODE_QUAD_RX, /*!< not support */ + SPI_IO_MODE_QUAD_TX /*!< not support */ +} SPI_IO_Mode; + +typedef enum { + SPI_ATTRIBUTION_IO_MODE +} SPI_Attribution; + +typedef struct { + SPI_Mode mode; /*!< SPI master mode or slave mode, only master mode for now */ + SPI_Operation_Mode opMode; /*!< dma mode or poll(cpu) mode */ + SPI_FirstBit firstBit; /*!< msb or lsb on line */ + uint32_t sclk; /*!< device sclk frequency */ + SPI_SclkMode sclkMode; /*!< device sclk mode */ +} SPI_Config; + +#define SPI_CONFIG_DEFAULT \ + { \ + .sclk = 6000000, \ + .sclkMode = SPI_SCLK_Mode0, \ + .firstBit = SPI_TCTRL_FBS_MSB, \ + .mode = SPI_CTRL_MODE_MASTER, \ + .opMode = SPI_OPERATION_MODE_POLL \ + } + +typedef struct { + SPI_Port port; /*!< spi port */ + SPI_CS cs; /*!< spi cs pin */ + SPI_Config config; /*!< spi device config */ +} SPI_Device; + +typedef struct { + uint32_t mclk; /*!< SPI main working frequency */ + bool cs_level; /*!< the cs voltage level of chip running */ +} SPI_Global_Config; + +/* + * @brief + * reset(not init) <--> ready(inited/closed) <--> busy(opened) <--> tx/rx/tx_rx(transmitting) + * + * each state --> error + */ +HAL_Status HAL_SPI_Init(SPI_Port port, const SPI_Global_Config *gconfig); +HAL_Status HAL_SPI_Deinit(SPI_Port port); +HAL_Status HAL_SPI_Receive(SPI_Port port, uint8_t *data, uint32_t size); +HAL_Status HAL_SPI_Transmit(SPI_Port port, uint8_t *data, uint32_t size); +HAL_Status HAL_SPI_TransmitReceive(SPI_Port port, uint8_t *tx_data, uint8_t *rx_data, uint32_t size); +HAL_Status HAL_SPI_Open(SPI_Port port, SPI_CS cs, SPI_Config *config, uint32_t msec); +HAL_Status HAL_SPI_Close(SPI_Port port); +HAL_Status HAL_SPI_CS(SPI_Port port, bool select); +HAL_Status HAL_SPI_Config(SPI_Port port, SPI_Attribution attr, uint32_t arg); +void HAL_SPI_Test(); +void HAL_SPI_TestByFlash(); + +#define SPI_MAX_WAIT_MS (2000) + +//#define SPI_SOURCE_CLK (24 * 1000 * 1000) + +#ifdef __cplusplus +} +#endif + +#endif /* _DRIVER_CHIP_HAL_SPI_H_ */ diff --git a/platform/mcu/xr871/include/driver/chip/hal_spinlock.h b/platform/mcu/xr871/include/driver/chip/hal_spinlock.h new file mode 100644 index 0000000000..a02687eb96 --- /dev/null +++ b/platform/mcu/xr871/include/driver/chip/hal_spinlock.h @@ -0,0 +1,71 @@ +/** + * @file hal_spinlock.h + * @author XRADIO IOT WLAN Team + */ + +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _DRIVER_CHIP_HAL_SPINLOCK_H_ +#define _DRIVER_CHIP_HAL_SPINLOCK_H_ + +#include "driver/chip/hal_def.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define SPINLOCK_SYSTATUS_REG (__IO uint32_t*) (SPINLOCK_BASE + 0X0) +#define SPINLOCK_STATUS_REG (__IO uint32_t*) (SPINLOCK_BASE + 0X10) +#define SPINLOCKN_LOCK_REG(n) (__IO uint32_t*) (SPINLOCK_BASE + 0X100 + (n) * 4) + +#define SPINLOCK_NUM (0X03 << 28) +#define IU0 HAL_BIT(8) +#define SPINLOCK_TAKEN HAL_BIT(0) + + +/** + * @brief spinlock info. + */ +typedef struct SPIN_Lock_t { + int lock; +} SPIN_Lock_t; + +HAL_Status HAL_SPIN_Init(); +HAL_Status HAL_SPIN_Deinit(); +HAL_Status HAL_SPIN_RequestLock(SPIN_Lock_t *lock); +HAL_Status HAL_SPIN_Lock(SPIN_Lock_t *lock); +HAL_Status HAL_SPIN_TryLock(SPIN_Lock_t *lock); +HAL_Status HAL_SPIN_Unlock(SPIN_Lock_t *lock); + +#ifdef __cplusplus +} +#endif + +#endif //_DRIVER_CHIP_HAL_SPINLOCK_H_ \ No newline at end of file diff --git a/platform/mcu/xr871/include/driver/chip/hal_timer.h b/platform/mcu/xr871/include/driver/chip/hal_timer.h new file mode 100644 index 0000000000..6a5a362b8e --- /dev/null +++ b/platform/mcu/xr871/include/driver/chip/hal_timer.h @@ -0,0 +1,187 @@ +/** + * @file hal_timer.h + * @author XRADIO IOT WLAN Team + */ + +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _DRIVER_CHIP_HAL_TIMER_H_ +#define _DRIVER_CHIP_HAL_TIMER_H_ + +#include "driver/chip/hal_def.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Timer ID definition + */ +typedef enum { + TIMER0_ID = 0, + TIMER1_ID = 1, + TIMER_NUM = 2 +} TIMER_ID; + +/** + * @brief TIMERx register block structure + */ +typedef struct { + __IO uint32_t CTRL; /* offset: 0x00, Timer control register */ + __IO uint32_t LOAD_VAL; /* offset: 0x04, Timer load/interval value register */ + __I uint32_t CUR_VAL; /* offset: 0x08, Timer current value register */ + uint32_t RESERVED[1]; /* offset: 0x0C */ +} TIMERx_T; + +/** + * @brief TIMER register block structure + */ +typedef struct { + __IO uint32_t IRQ_EN; /* offset: 0x00, Timer IRQ enable register */ + __IO uint32_t IRQ_STATUS; /* offset: 0x04, Timer IRQ status register */ + uint32_t RESERVED[2]; /* offset: 0x08 */ + TIMERx_T TIMERx[TIMER_NUM]; /* offset: 0x10, TIMERx register block */ +} TIMER_T; + +#define TIMER ((TIMER_T *)TIMER_BASE) /* address: 0x40040800 */ + +/* + * Bit field definition of TIMER->TIMERx[x].CTRL + */ + +/* timer mode */ +#define TIMER_MODE_SHIFT 7 /* R/W */ +#define TIMER_MODE_MASK (0x1U << TIMER_MODE_SHIFT) +typedef enum { + TIMER_MODE_REPEAT = (0U << TIMER_MODE_SHIFT), + TIMER_MODE_ONCE = (1U << TIMER_MODE_SHIFT) +} TIMER_Mode; + +/* Timer clock prescaler */ +#define TIMER_CLK_PRESCALER_SHIFT 4 /* R/W */ +#define TIMER_CLK_PRESCALER_MASK (0x7U << TIMER_CLK_PRESCALER_SHIFT) +typedef enum { + TIMER_CLK_PRESCALER_1 = (0U << TIMER_CLK_PRESCALER_SHIFT), + TIMER_CLK_PRESCALER_2 = (1U << TIMER_CLK_PRESCALER_SHIFT), + TIMER_CLK_PRESCALER_4 = (2U << TIMER_CLK_PRESCALER_SHIFT), + TIMER_CLK_PRESCALER_8 = (3U << TIMER_CLK_PRESCALER_SHIFT), + TIMER_CLK_PRESCALER_16 = (4U << TIMER_CLK_PRESCALER_SHIFT), + TIMER_CLK_PRESCALER_32 = (5U << TIMER_CLK_PRESCALER_SHIFT), + TIMER_CLK_PRESCALER_64 = (6U << TIMER_CLK_PRESCALER_SHIFT), + TIMER_CLK_PRESCALER_128 = (7U << TIMER_CLK_PRESCALER_SHIFT) +} TIMER_ClkPrescaler; + +/* Timer clock source */ +#define TIMER_CLK_SRC_SHIFT 2 /* R/W */ +#define TIMER_CLK_SRC_MASK (0x3U << TIMER_CLK_SRC_SHIFT) +typedef enum { + TIMER_CLK_SRC_LFCLK = (0U << TIMER_CLK_SRC_SHIFT), + TIMER_CLK_SRC_HFCLK = (1U << TIMER_CLK_SRC_SHIFT) +} TIMER_ClkSrc; + +#define TIMER_RELOAD_BIT HAL_BIT(1) + +#define TIMER_START_BIT HAL_BIT(0) + +/* + * TIMER->TIMERx[x].LOAD_VAL + */ + +/* + * TIMER->TIMERx[x].CUR_VAL + */ + +/******************************************************************************/ + +/** @brief Type define of Timer IRQ callback function */ +typedef void (*TIMER_IRQCallback)(void *arg); + +/** + * @brief Timer initialization parameters + * + * @note Timer counts down from period to zero + * - counting interval = period / (clkSrc / clkPrescaler) + */ +typedef struct { + uint32_t cfg; /* Created by HAL_TIMER_MakeInitCfg() */ + uint32_t period; /* Hardware reload value */ + + int8_t isEnableIRQ; /* Enable IRQ or not when TIMER counts down to zero */ + TIMER_IRQCallback callback; /* TIMER IRQ callback function */ + void *arg; /* Argument of TIMER IRQ callback function */ +} TIMER_InitParam; + +/** + * @brief Make configuration value for TIMER_InitParam::cfg + * @param[in] mode Timer working mode + * @param[in] clkSrc Timer clock source + * @param[in] clkPrescaler Timer clock prescaler + * @return configuration value for TIMER_InitParam::cfg + */ +__STATIC_INLINE uint32_t HAL_TIMER_MakeInitCfg(TIMER_Mode mode, + TIMER_ClkSrc clkSrc, + TIMER_ClkPrescaler clkPrescaler) +{ + return (mode | clkSrc | clkPrescaler); +} + +/** + * @brief Get the current counting value of the specified timer + * @param[in] timerID ID of the specified Timer + * @return Current counting value of the specified timer + */ +__STATIC_INLINE uint32_t HAL_TIMER_GetCurrentValue(TIMER_ID timerID) +{ + return TIMER->TIMERx[timerID].CUR_VAL; +} + +HAL_Status HAL_TIMER_Init(TIMER_ID timerID, const TIMER_InitParam *param); +HAL_Status HAL_TIMER_DeInit(TIMER_ID timerID); + +void HAL_TIMER_Start(TIMER_ID timerID); +void HAL_TIMER_Stop(TIMER_ID timerID); + +/** + * @brief Pause the timer's counting, the current value of timer is kept + * @param[in] timerID ID of the specified timer + * @return None + */ +__STATIC_INLINE void HAL_TIMER_Pause(TIMER_ID timerID) +{ + HAL_TIMER_Stop(timerID); +} + +void HAL_TIMER_Continue(TIMER_ID timerID); + +#ifdef __cplusplus +} +#endif + +#endif /* _DRIVER_CHIP_HAL_TIMER_H_ */ diff --git a/platform/mcu/xr871/include/driver/chip/hal_uart.h b/platform/mcu/xr871/include/driver/chip/hal_uart.h new file mode 100644 index 0000000000..378c9df001 --- /dev/null +++ b/platform/mcu/xr871/include/driver/chip/hal_uart.h @@ -0,0 +1,290 @@ +/** + * @file hal_uart.h + * @author XRADIO IOT WLAN Team + */ + +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _DRIVER_CHIP_HAL_UART_H_ +#define _DRIVER_CHIP_HAL_UART_H_ + +#include "driver/chip/hal_def.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief UART ID definition + */ +typedef enum { + UART0_ID = 0, + UART1_ID = 1, + UART_NUM = 2, + UART_INVALID_ID = UART_NUM +} UART_ID; + +/** + * @brief UART register block structure + */ +typedef struct { + union { + __I uint32_t RX_BUF; /* offset: 0x00, UART receive buffer register, 8-bit valid */ + __O uint32_t TX_HOLD; /* offset: 0x00, UART transmit holding register, 8-bit valid */ + __IO uint32_t DIV_LOW; /* offset: 0x00, UART divisor latch low register, 8-bit valid */ + } RBR_THR_DLL; /* offset: 0x00, UART receive buffer/transmit holding/divisor latch low register */ + union { + __IO uint32_t DIV_HIGH; /* offset: 0x04, UART divisor latch high register, 8-bit valid */ + __IO uint32_t IRQ_EN; /* offset: 0x04, UART interrupt enable register */ + } DLH_IER; /* offset: 0x04, UART divisor latch high/IRQ enable register */ + union { + __I uint32_t IRQ_ID; /* offset: 0x08, UART interrupt identity register */ + __O uint32_t FIFO_CTRL; /* offset: 0x08, UART FIFO control register */ + } IIR_FCR; /* offset: 0x08, UART interrupt identity/FIFO control register */ + __IO uint32_t LINE_CTRL; /* offset: 0x0C, UART line control register */ + __IO uint32_t MODEM_CTRL; /* offset: 0x10, UART modem control register */ + __I uint32_t LINE_STATUS; /* offset: 0x14, UART line status register */ + __I uint32_t MODEM_STATUS; /* offset: 0x18, UART modem stauts register */ + __IO uint32_t SCRATCH; /* offset: 0x1C, UART scratch register */ + uint32_t RESERVED1[23]; + __I uint32_t STATUS; /* offset: 0x7C, UART status register */ + __I uint32_t TX_FIFO_LEVEL; /* offset: 0x80, UART transmit FIFO level register */ + __I uint32_t RX_FIFO_LEVEL; /* offset: 0x84, UART receive FIFO level register */ + uint32_t RESERVED2[7]; + __IO uint32_t HALT; /* offset: 0xA4, UART halt register */ + uint32_t RESERVED3[9]; + __IO uint32_t TX_DELAY; /* offset: 0xCC, UART TX delay register */ + uint32_t RESERVED4[1]; + __IO uint32_t BAUD_DECT_CTRL; /* offset: 0xD4, UART baudrate detection control register */ + __IO uint32_t BAUD_DECT_VAL_LOW; /* offset: 0xD8, UART baudrate detection counter low register */ + __IO uint32_t BAUD_DECT_VAL_HIGH; /* offset: 0xDC, UART baudrate detection counter high register */ +} UART_T; + +#define UART0 ((UART_T *)UART0_BASE) /* address: 0x40040C00 */ +#define UART1 ((UART_T *)UART1_BASE) /* address: 0x40041000 */ + +/* UARTx->RBR_THR_DLL.RX_BUF, R */ +#define UART_RX_DATA_MASK 0xFFU + +/* UARTx->RBR_THR_DLL.TX_HOLD, W */ +#define UART_TX_DATA_MASK 0xFFU + +/* UARTx->RBR_THR_DLL.DIV_LOW, R/W */ +#define UART_DIV_LOW_MASK 0xFFU + +/* UARTx->DLH_IER.DIV_HIGH, R/W */ +#define UART_DIV_HIGH_MASK 0xFFU + +/* UARTx->DLH_IER.IRQ_EN, R/W */ +#define UART_TX_FIFO_TRIG_MODE_EN_BIT HAL_BIT(7) +#define UART_RS485_IRQ_EN_BIT HAL_BIT(4) +#define UART_MODEM_STATUS_IRQ_EN_BIT HAL_BIT(3) +#define UART_LINE_STATUS_IRQ_EN_BIT HAL_BIT(2) +#define UART_TX_READY_IRQ_EN_BIT HAL_BIT(1) +#define UART_RX_READY_IRQ_EN_BIT HAL_BIT(0) + +/* UARTx->IIR_FCR.IRQ_ID, R */ +#define UART_IID_SHIFT 0 +#define UART_IID_MASK (0xFU << UART_IID_SHIFT) +typedef enum { + UART_IID_MODEM_STATUS = (0x0U << UART_IID_SHIFT), + UART_IID_NONE = (0x1U << UART_IID_SHIFT), + UART_IID_TX_READY = (0x2U << UART_IID_SHIFT), + UART_IID_RS485 = (0x3U << UART_IID_SHIFT), + UART_IID_RX_READY = (0x4U << UART_IID_SHIFT), + UART_IID_LINE_STATUS = (0x6U << UART_IID_SHIFT), + UART_IID_BUSY_DETECT = (0x7U << UART_IID_SHIFT), + UART_IID_CHAR_TIMEOUT = (0xCU << UART_IID_SHIFT), +} UART_IIDType; + +/* UARTx->IIR_FCR.FIFO_CTRL, W */ +#define UART_RX_FIFO_TRIG_LEVEL_SHIFT 6 +#define UART_RX_FIFO_TRIG_LEVEL_MASK (0x3 << UART_RX_FIFO_TRIG_LEVEL_SHIFT) +typedef enum { + UART_RX_FIFO_TRIG_LEVEL_ONE_CHAR = (0x0 << UART_RX_FIFO_TRIG_LEVEL_SHIFT), + UART_RX_FIFO_TRIG_LEVEL_QUARTER_FULL = (0x1 << UART_RX_FIFO_TRIG_LEVEL_SHIFT), + UART_RX_FIFO_TRIG_LEVEL_HALF_FULL = (0x2 << UART_RX_FIFO_TRIG_LEVEL_SHIFT), + UART_RX_FIFO_TRIG_LEVEL_NEARLY_FULL = (0x3 << UART_RX_FIFO_TRIG_LEVEL_SHIFT), +} UART_RxFifoTrigLevel; + +#define UART_TX_FIFO_TRIG_LEVEL_SHIFT 4 +#define UART_TX_FIFO_TRIG_LEVEL_MASK (0x3 << UART_TX_FIFO_TRIG_LEVEL_SHIFT) +typedef enum { + UART_TX_FIFO_TRIG_LEVEL_EMPTY = (0x0 << UART_TX_FIFO_TRIG_LEVEL_SHIFT), + UART_TX_FIFO_TRIG_LEVEL_TWO_CHAR = (0x1 << UART_TX_FIFO_TRIG_LEVEL_SHIFT), + UART_TX_FIFO_TRIG_LEVEL_QUARTER_FULL = (0x2 << UART_TX_FIFO_TRIG_LEVEL_SHIFT), + UART_TX_FIFO_TRIG_LEVEL_HALF_FULL = (0x3 << UART_TX_FIFO_TRIG_LEVEL_SHIFT), +} UART_TxFifoTrigLevel; + +#define UART_DMA_MODE_SHIFT 3 +#define UART_DMA_MODE_MASK (1U << UART_DMA_MODE_SHIFT) +typedef enum { + UART_DMA_MODE_0 = (0U << UART_DMA_MODE_SHIFT), + UART_DMA_MODE_1 = (1U << UART_DMA_MODE_SHIFT) +} UART_DMAMode; + +#define UART_TX_FIFO_RESET_BIT HAL_BIT(2) +#define UART_RX_FIFO_RESET_BIT HAL_BIT(1) +#define UART_FIFO_EN_BIT HAL_BIT(0) + +/* UARTx->LINE_CTRL, R/W */ +#define UART_DIV_ACCESS_BIT HAL_BIT(7) +#define UART_BREAK_CTRL_BIT HAL_BIT(6) + +#define UART_PARITY_EN_BIT HAL_BIT(3) +#define UART_PARITY_SEL_SHIFT 4 +#define UART_PARITY_SEL_MASK (0x3U << UART_PARITY_SEL_SHIFT) +#define UART_PARITY_MASK (UART_PARITY_EN_BIT | UART_PARITY_SEL_MASK) +typedef enum { + UART_PARITY_NONE = 0U, + UART_PARITY_ODD = UART_PARITY_EN_BIT | (0x0U << UART_PARITY_SEL_SHIFT), + UART_PARITY_EVEN = UART_PARITY_EN_BIT | (0x1U << UART_PARITY_SEL_SHIFT) +} UART_Parity; + +#define UART_STOP_BITS_SHIFT 2 +#define UART_STOP_BITS_MASK (0x1U << UART_STOP_BITS_SHIFT) +typedef enum { + UART_STOP_BITS_1 = (0x0U << UART_STOP_BITS_SHIFT), + UART_STOP_BITS_2 = (0x1U << UART_STOP_BITS_SHIFT) +} UART_StopBits; /* UART_STOP_BITS_2 is 1.5 stop bits for UART_DATA_BITS_5 */ + +#define UART_DATA_BITS_SHIFT 0 +#define UART_DATA_BITS_MASK (0x3U << UART_DATA_BITS_SHIFT) +typedef enum { + UART_DATA_BITS_5 = (0x0U << UART_DATA_BITS_SHIFT), + UART_DATA_BITS_6 = (0x1U << UART_DATA_BITS_SHIFT), + UART_DATA_BITS_7 = (0x2U << UART_DATA_BITS_SHIFT), + UART_DATA_BITS_8 = (0x3U << UART_DATA_BITS_SHIFT) +} UART_DataBits; + +/* UARTx->MODEM_CTRL, R/W */ +#define UART_WORK_MODE_SHIFT 6 +#define UART_WORK_MODE_MASK (0x3U << UART_WORK_MODE_SHIFT) +typedef enum { + UART_WORK_MODE_UART = (0x0U << UART_WORK_MODE_SHIFT), + UART_WORK_MODE_IRDA = (0x1U << UART_WORK_MODE_SHIFT), + UART_WORK_MODE_RS485 = (0x2U << UART_WORK_MODE_SHIFT), +} UART_WorkMode; + +#define UART_AUTO_FLOW_CTRL_EN_BIT HAL_BIT(5) +#define UART_LOOP_BACK_EN_BIT HAL_BIT(4) +#define UART_RTS_ASSERT_BIT HAL_BIT(1) +#define UART_DTR_ASSERT_BIT HAL_BIT(0) + +/* UARTx->LINE_STATUS, R */ +#define UART_FIFO_ERROR_BIT HAL_BIT(7) +#define UART_TX_EMPTY_BIT HAL_BIT(6) +#define UART_TX_HOLD_EMPTY_BIT HAL_BIT(5) +#define UART_BREAK_IRQ_BIT HAL_BIT(4) +#define UART_FRAME_ERROR_BIT HAL_BIT(3) +#define UART_PARITY_ERROR_BIT HAL_BIT(2) +#define UART_OVERRUN_ERROR_BIT HAL_BIT(1) +#define UART_RX_READY_BIT HAL_BIT(0) +#define UART_LINE_STATUS_MASK 0xFFU + +/* UARTx->MODEM_STATUS, R */ +#define UART_CTS_ASSERTED_BIT HAL_BIT(4) +#define UART_DELTA_CTS_BIT HAL_BIT(0) +#define UART_MODEM_STATUS_MASK 0xF3U + +/* UARTx->STATUS, R */ +#define UART_RX_FIFO_FULL_BIT HAL_BIT(4) +#define UART_RX_FIFO_NOT_EMPTY_BIT HAL_BIT(3) +#define UART_TX_FIFO_EMPTY_BIT HAL_BIT(2) +#define UART_TX_FIFO_NOT_FULL_BIT HAL_BIT(1) +#define UART_BUSY_BIT HAL_BIT(0) +#define UART_STATUS_MASK 0x1FU + +/* UARTx->TX_FIFO_LEVEL, R */ +#define UART_TX_FIFO_LEVEL_SHIFT 0 +#define UART_TX_FIFO_LEVEL_VMASK 0x7FU + +/* UARTx->RX_FIFO_LEVEL, R */ +#define UART_RX_FIFO_LEVEL_SHIFT 0 +#define UART_RX_FIFO_LEVEL_VMASK 0x7FU + +/* UARTx->HALT, R/W */ +#define UART_DMA_PTE_TX_BIT HAL_BIT(7) +#define UART_DMA_PTE_RX_BIT HAL_BIT(6) + +#define UART_CHANGE_UPDATE_BIT HAL_BIT(2) +#define UART_CHANGE_AT_BUSY_BIT HAL_BIT(1) + +#define UART_HALT_TX_EN_BIT HAL_BIT(0) + +/******************************************************************************/ + +/** + * @brief UART initialization parameters + */ +typedef struct { + uint32_t baudRate; /* Baud rate, in bps */ + UART_Parity parity; /* Parity */ + UART_StopBits stopBits; /* Stop bits */ + UART_DataBits dataBits; /* Data bits */ + int8_t isAutoHwFlowCtrl; /* Enable auto hardware flow control or not */ +} UART_InitParam; + +/** @brief Type define of UART receive ready callback function */ +typedef void (*UART_RxReadyCallback) (void *arg); + +UART_T *HAL_UART_GetInstance(UART_ID uartID); +int HAL_UART_IsTxReady(UART_T *uart); +int HAL_UART_IsTxEmpty(UART_T *uart); +int HAL_UART_IsRxReady(UART_T *uart); +uint8_t HAL_UART_GetRxData(UART_T *uart); +void HAL_UART_PutTxData(UART_T *uart, uint8_t data); + +HAL_Status HAL_UART_Init(UART_ID uartID, const UART_InitParam *param); +HAL_Status HAL_UART_DeInit(UART_ID uartID); + +int32_t HAL_UART_Transmit_IT(UART_ID uartID, uint8_t *buf, int32_t size); +int32_t HAL_UART_Receive_IT(UART_ID uartID, uint8_t *buf, int32_t size, uint32_t msec); + +HAL_Status HAL_UART_EnableRxCallback(UART_ID uartID, UART_RxReadyCallback cb, void *arg); +HAL_Status HAL_UART_DisableRxCallback(UART_ID uartID); + +HAL_Status HAL_UART_EnableTxDMA(UART_ID uartID); +HAL_Status HAL_UART_EnableRxDMA(UART_ID uartID); +HAL_Status HAL_UART_DisableTxDMA(UART_ID uartID); +HAL_Status HAL_UART_DisableRxDMA(UART_ID uartID); +int32_t HAL_UART_Transmit_DMA(UART_ID uartID, uint8_t *buf, int32_t size); +int32_t HAL_UART_Receive_DMA(UART_ID uartID, uint8_t *buf, int32_t size, uint32_t msec); + +int32_t HAL_UART_Transmit_Poll(UART_ID uartID, uint8_t *buf, int32_t size); +int32_t HAL_UART_Receive_Poll(UART_ID uartID, uint8_t *buf, int32_t size, uint32_t msec); + +void HAL_UART_SetBreakCmd(UART_ID uartID, int8_t isSet); + +#ifdef __cplusplus +} +#endif + +#endif /* _DRIVER_CHIP_HAL_UART_H_ */ diff --git a/platform/mcu/xr871/include/driver/chip/hal_util.h b/platform/mcu/xr871/include/driver/chip/hal_util.h new file mode 100644 index 0000000000..feb5cd537b --- /dev/null +++ b/platform/mcu/xr871/include/driver/chip/hal_util.h @@ -0,0 +1,50 @@ +/** + * @file hal_util.h + * @author XRADIO IOT WLAN Team + */ + +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _DRIVER_CHIP_HAL_UTIL_H_ +#define _DRIVER_CHIP_HAL_UTIL_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +void HAL_UDelay(uint32_t us); + +#ifdef __cplusplus +} +#endif + +#endif /* _DRIVER_CHIP_HAL_UTIL_H_ */ diff --git a/platform/mcu/xr871/include/driver/chip/hal_wakeup.h b/platform/mcu/xr871/include/driver/chip/hal_wakeup.h new file mode 100644 index 0000000000..ddd6d85da7 --- /dev/null +++ b/platform/mcu/xr871/include/driver/chip/hal_wakeup.h @@ -0,0 +1,155 @@ +/** + * @file hal_wakeup.h + * @author XRADIO IOT WLAN Team + */ + +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _DRIVER_CHIP_HAL_WAKEUP_H_ +#define _DRIVER_CHIP_HAL_WAKEUP_H_ + +#include "sys/io.h" + +#include "driver/chip/hal_gpio.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief ignore the second set if the second set time is longer than the + * first time. + */ +//#define WAKEUP_TIMER_CHECK_TIME 1 + +/** @brief the minimum time of wakeup timer support, based on ms */ +#define WAKEUP_TIMER_MIN_TIME (10) + +/** + * @brief wakeup events. + * @note WKIO0~9 should define from BIT(0) to BIT(9). + */ +#ifdef __CONFIG_ARCH_APP_CORE +#define PM_WAKEUP_SRC_WKIO0 BIT(0 ) /* 0x00000001 */ +#define PM_WAKEUP_SRC_WKIO1 BIT(1 ) /* 0x00000002 */ +#define PM_WAKEUP_SRC_WKIO2 BIT(2 ) /* 0x00000004 */ +#define PM_WAKEUP_SRC_WKIO3 BIT(3 ) /* 0x00000008 */ +#define PM_WAKEUP_SRC_WKIO4 BIT(4 ) /* 0x00000010 */ +#define PM_WAKEUP_SRC_WKIO5 BIT(5 ) /* 0x00000020 */ +#define PM_WAKEUP_SRC_WKIO6 BIT(6 ) /* 0x00000040 */ +#define PM_WAKEUP_SRC_WKIO7 BIT(7 ) /* 0x00000080 */ +#define PM_WAKEUP_SRC_WKIO8 BIT(8 ) /* 0x00000100 */ +#define PM_WAKEUP_SRC_WKIO9 BIT(9 ) /* 0x00000200 */ +#endif +#define PM_WAKEUP_SRC_WKTIMER BIT(10) /* 0x00000400 */ +#define PM_WAKEUP_SRC_WKSEV BIT(11) /* 0x00000800 */ +#define PM_WAKEUP_SRC_NETCPU (PM_WAKEUP_SRC_WKSEV) /* 0x00000800 */ +#define PM_WAKEUP_SRC_DEVICES BIT(12) /* 0x00001000 */ + +/** + * @brief Get last wakeup event. + * retval Events defined in PM_WAKEUP_SRC_XX. + */ +extern uint32_t HAL_Wakeup_GetEvent(void); + +#ifdef __CONFIG_ARCH_APP_CORE +/** + * @brief Set IO hold. + * @note Set all IO hold before poweroff to prevent IO output low level voltage. + * @param hold_io: + * @arg hold_io-> IO hold mask. + * retval 0 if success or other if failed. + */ +extern int32_t HAL_Wakeup_SetIOHold(uint32_t hold_io); + +/** + * @brief Set wakeup IO enable and mode. + * @note This won't change IO config immediately, the enabled IO will be setted + * to input and wakeup mode before system enter lowpower mode. And the IO + * will be disabled after wakeup. So reinit IO if you want this IO used + * as other function. The IO will used as wakeup IO until be cleaned. + * @param pn: + * @arg pn-> 0~9. + * @param mode: + * @arg mode-> 0:negative edge, 1:positive edge. + * retval None. + */ +extern void HAL_Wakeup_SetIO(uint32_t pn, uint32_t mode); + +/** + * @brief Clear wakeup IO enable. + * @param pn: + * @arg pn-> 0~9. + * retval None. + */ +extern void HAL_Wakeup_ClrIO(uint32_t pn); +#endif + +/** + * @brief Set wakeup timer. + * @note This will config wakeup timer counting immediately, and this will be + * used only once. The wakeup timer will be disabled when time out no + * matter it wakeup system or not. Wakeup timer should be setted + * everytime if you want wake up system from suspend. + * @param count_32k: + * @arg count_32k-> counter to wakeup system based on 32k counter. from + * WAKEUP_TIMER_MIN_TIME*32(WAKEUP_TIMER_MIN_TIME mS) to 134217727(4194.303S). + * retval 0 if success or other if failed. + */ +extern int32_t HAL_Wakeup_SetTimer(uint32_t count_32k); + +/** + * @brief Set wakeup timer. + * @param ms: + * @arg ms-> counter to wakeup system based on ms. + * retval 0 if success or other if failed. + */ +#define HAL_Wakeup_SetTimer_mS(ms) HAL_Wakeup_SetTimer(ms*32) + +/** + * @brief Config and enable wakeup io. + * retval 0 if success or other if failed. + */ +extern int32_t HAL_Wakeup_SetSrc(void); + +/** @brief Disable wakeup io. */ +extern void HAL_Wakeup_ClrSrc(void); + +/** @brief Init wakeup IO and Timer as disable mode. */ +extern void HAL_Wakeup_Init(void); + +/** @brief Deinit wakeup IO and Timer. */ +extern void HAL_Wakeup_DeInit(void); + +#ifdef __cplusplus +} +#endif + +#endif /* _DRIVER_CHIP_HAL_WAKEUP_H_ */ diff --git a/platform/mcu/xr871/include/driver/chip/hal_wdg.h b/platform/mcu/xr871/include/driver/chip/hal_wdg.h new file mode 100644 index 0000000000..81faf3f40a --- /dev/null +++ b/platform/mcu/xr871/include/driver/chip/hal_wdg.h @@ -0,0 +1,127 @@ +/** + * @file hal_wdg.h + * @author XRADIO IOT WLAN Team + */ + +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _DRIVER_CHIP_HAL_WDG_H_ +#define _DRIVER_CHIP_HAL_WDG_H_ + +#include "driver/chip/hal_def.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Watchdog register block structure + */ +typedef struct { + __IO uint32_t IRQ_EN; /* offset: 0xA0, Watchdog IRQ Enable Register */ + __IO uint32_t IRQ_STATUS; /* offset: 0xA4, Watchdog IRQ Status Register */ + uint32_t RESERVED0[2]; + __IO uint32_t CTRL; /* offset: 0xB0, Watchdog Control Register */ + __IO uint32_t CFG; /* offset: 0xB4, Watchdog Configuration Register */ + __IO uint32_t MODE; /* offset: 0xB8, Watchdog Mode Register */ + __IO uint32_t RESET_CTRL; /* offset: 0xBC, Watchdog Output Control Register */ +} WDG_T; + +#define WDG ((WDG_T *)(TIMER_BASE + 0xA0)) /* address: 0x400408A0 */ + +/* WDG->IRQ_EN */ +#define WDG_IRQ_EN_BIT HAL_BIT(0) + +/* WDG->IRQ_STATUS */ +#define WDG_IRQ_PENDING_BIT HAL_BIT(0) + +/* WDG->CFG */ +#define WDG_EVT_TYPE_SHIFT 0 +#define WDG_EVT_TYPE_MASK (0x3U << WDG_EVT_TYPE_SHIFT) +typedef enum { + WDG_EVT_RESET = (1U << WDG_EVT_TYPE_SHIFT), /* reset system */ + WDG_EVT_INTERRUPT = (2U << WDG_EVT_TYPE_SHIFT) /* trigger interrupt */ +} WDG_EventType; + +/* WDG->MODE */ +#define WDG_TIMEOUT_SHIFT 4 +#define WDG_TIMEOUT_MASK (0xF << WDG_TIMEOUT_SHIFT) +typedef enum { + WDG_TIMEOUT_500MS = (0U << WDG_TIMEOUT_SHIFT), + WDG_TIMEOUT_1SEC = (1U << WDG_TIMEOUT_SHIFT), + WDG_TIMEOUT_2SEC = (2U << WDG_TIMEOUT_SHIFT), + WDG_TIMEOUT_3SEC = (3U << WDG_TIMEOUT_SHIFT), + WDG_TIMEOUT_4SEC = (4U << WDG_TIMEOUT_SHIFT), + WDG_TIMEOUT_5SEC = (5U << WDG_TIMEOUT_SHIFT), + WDG_TIMEOUT_6SEC = (6U << WDG_TIMEOUT_SHIFT), + WDG_TIMEOUT_8SEC = (7U << WDG_TIMEOUT_SHIFT), + WDG_TIMEOUT_10SEC = (8U << WDG_TIMEOUT_SHIFT), + WDG_TIMEOUT_12SEC = (9U << WDG_TIMEOUT_SHIFT), + WDG_TIMEOUT_14SEC = (10U << WDG_TIMEOUT_SHIFT), + WDG_TIMEOUT_16SEC = (11U << WDG_TIMEOUT_SHIFT) +} WDG_Timeout; + +#define WDG_EN_BIT HAL_BIT(0) + +/* WDG->RESET_CTRL */ +#define WDG_RESET_CYCLE_SHIFT 0 +#define WDG_RESET_CYCLE_MASK (0x1F << WDG_RESET_CYCLE_SHIFT) +#define WDG_DEFAULT_RESET_CYCLE 0xA + +/******************************************************************************/ + +/** @brief Type define of watchdog IRQ callback function */ +typedef void (*WDG_IRQCallback) (void *arg); + +/** + * @brief Watchdog initialization parameters + */ +typedef struct { + WDG_EventType event; /* Watchdog trigger event type */ + WDG_Timeout timeout; /* Interval to trigger event after last feed */ + uint8_t resetCycle; /* Reset signal cycles, for WDG_EVT_RESET only. + Set to WDG_DEFAULT_RESET_CYCLE generally, its range is [0, 31] */ + WDG_IRQCallback callback; /* Watchdog IRQ callback fucntion, for WDG_EVT_INTERRUPT only */ + void *arg; /* Argument of Watchdog IRQ callback fucntion, for WDG_EVT_INTERRUPT only */ +} WDG_InitParam; + +HAL_Status HAL_WDG_Init(const WDG_InitParam *param); +HAL_Status HAL_WDG_DeInit(void); + +void HAL_WDG_Feed(void); +void HAL_WDG_Start(void); +void HAL_WDG_Stop(void); +void HAL_WDG_Reboot(void); + +#ifdef __cplusplus +} +#endif + +#endif /* _DRIVER_CHIP_HAL_WDG_H_ */ diff --git a/platform/mcu/xr871/include/driver/chip/hal_xip.h b/platform/mcu/xr871/include/driver/chip/hal_xip.h new file mode 100644 index 0000000000..3cbdb43a52 --- /dev/null +++ b/platform/mcu/xr871/include/driver/chip/hal_xip.h @@ -0,0 +1,60 @@ +/** + * @file hal_xip.h + * @author XRADIO IOT WLAN Team + */ + +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef HAL_XIP_H_ +#define HAL_XIP_H_ + +#include "driver/chip/hal_flashctrl.h" +#include "driver/chip/flashchip/flash_chip.h" + +//typedef struct XipDriverBase XipDriverBase; + +struct XipDriverBase +{ + int (*setCmd)(struct XipDriverBase *base, InstructionField *cmd, InstructionField *addr, InstructionField *dummy, InstructionField *data); + int (*setDelay)(struct XipDriverBase *base, Flashc_Delay *delay); + int (*setAddr)(struct XipDriverBase *base, uint32_t addr); + int (*setContinue)(struct XipDriverBase *base, uint32_t continueMode, void *arg); + + XIP_Config mCfg; + uint32_t flash; + FlashDev *dev; +}; + + +HAL_Status HAL_Xip_Init(uint32_t flash, uint32_t xaddr); + +HAL_Status HAL_Xip_Deinit(void); + +#endif /* HAL_XIP_H_ */ diff --git a/platform/mcu/xr871/include/driver/chip/ir_nec.h b/platform/mcu/xr871/include/driver/chip/ir_nec.h new file mode 100644 index 0000000000..58423733e0 --- /dev/null +++ b/platform/mcu/xr871/include/driver/chip/ir_nec.h @@ -0,0 +1,199 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _DRIVER_CHIP_IR_NEC_H_ +#define _DRIVER_CHIP_IR_NEC_H_ + +#include "driver/chip/hal_def.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * NEC protocal + * 8 bits addr, 8 bits cmd code. + * send addr and cmd code twice every time to improve reliable. + * pluse modulation time is long or short. + * 38KHz carrier frequency. + * bit time is 1.12ms or 2.25ms. + * send time is 68ms, send cycle is 110ms. + * ------------------------------------------------------------------------------ + * | Logical '1' | Logical '0' | Logical 'x' | + * ------------------------------------------------------------------------------ + * | 1(pulses) | 0 | 1(pulses) | 0 | 1(pulses) | - | + * ------------------------------------------------------------------------------ + * | 560uS | 1690uS | 560uS | 560uS | 560uS | | + * | 2.25mS | 1.12mS | | | + * ------------------------------------------------------------------------------ + * 1(pulses): f=38KHz, T=26.3us, N=560/26.3=21 + * + * example: send 0x59(10011010 LSB first) addr, 0x16(01101000 LSB first) cmd code. + * --------------------------------------------------------------------------------------------------------------------- + * | S1 | S0 |L1|L0|L0|L1|L1|L0|L1|L0|L0|L1|L1|L0|L0|L1|L0|L1|L0|L1|L1|L0|L1|L0|L0|L0|L1|L0|L0|L1|L0|L1|L1|L1| E1 | + * | 9mS |4.5mS|LSB MSB|LSB MSB|LSB MSB|LSB MSB|560uS| + * | Start | 0x59 Address | ~0x59 Address | 0x16 Command | ~0x16 Command | End | + * --------------------------------------------------------------------------------------------------------------------- + * the S0 of repeat code is 2.25mS. + */ + +#if defined (IR_CLK_32K_USED) +#define IRRX_32K_ACTIVE_T (0) /* ActionTreshold, Active Threshold */ +#define IRRX_32K_ACTIVE_T_C (0) /* SampleClock, Active Threshold Control, same with IRRX_ATHC_CLOCKUNIT_1 */ +#define IRRX_32K_L1_MIN (112) /* 112*30.5 = ~3.4ms, Lead1(9.0ms) Lead1R(4.5ms) > IR_L1_MIN */ +#define IRRX_32K_L0_MIN (56) /* 56*30.5 = ~1.7ms, Lead0(4.5ms) Lead0R(2.25ms) > IR_L0_MIN */ +#define IRRX_32K_PMAX (36) /* 36*30.5 = ~1098us ~= 549*2, Pluse < IRRX_PMAX */ +#define IRRX_32K_DMID (36) /* 36*30.5 = ~1098us ~= 549*2, D1 > IRRX_DMID, D0 =< IRRX_DMID */ +#define IRRX_32K_DMAX (74) /* 74*30.5 = ~2257us ~= 564*4, D < IRRX_DMAX */ + +#define IRTX_32K_TS_US (61) /* Ts frome IRTX_SAMPLE */ + /* IRTX_9MS_NUM 0x94, 148*Ts=9028uS */ + /* IRTX_9MS_NUM1 127*Ts=7747uS */ + /* IRTX_9MS_NUM2 21*Ts=1281uS */ + /* IRTX_4P5MS_NUM 0x4A, 74*Ts=4514uS */ + /* IRTX_560US_NUM 0x09, 9*Ts=549uS */ + /* IRTX_1680US_NUM 0x1C, 28*Ts=1708uS */ +#define IRTX_32K_9MS_NUM (9000/IRTX_32K_TS_US) +#define IRTX_32K_9MS_NUM1 (0x7f) +#define IRTX_32K_9MS_NUM2 (IRTX_32K_9MS_NUM-IRTX_32K_9MS_NUM1) +#define IRTX_32K_4P5MS_NUM (4500/IRTX_32K_TS_US) +#define IRTX_32K_4P5MS_NUM1 (IRTX_32K_4P5MS_NUM) +#define IRTX_32K_2P25MS_NUM (IRTX_32K_4P5MS_NUM/2) +#define IRTX_32K_560US_NUM (560/IRTX_32K_TS_US) +#define IRTX_32K_1680US_NUM (1680/IRTX_32K_TS_US) + +#define IRTX_32K_NEC_CYCLE_MS (110) +#define IRTX_32K_NEC_IDC_VALUE ((IRTX_32K_NEC_CYCLE_MS*1000)/128/IRTX_32K_TS_US + 1) +#else +#define IRRX_26M_ACTIVE_T (0) /* ActionTreshold, Active Threshold */ +#define IRRX_26M_ACTIVE_T_C (1) /* SampleClock, Active Threshold Control, same with IRRX_ATHC_CLOCKUNIT_128 */ +#define IRRX_26M_L1_MIN (80) /* 80*39.4 = ~3.2ms, Lead1(9.0ms) Lead1R(4.5ms) > IR_L1_MIN */ +#define IRRX_26M_L0_MIN (41) /* 41*39.4 = ~1.6ms, Lead0(4.5ms) Lead0R(2.25ms)> IR_L0_MIN */ +#define IRRX_26M_PMAX (27) /* 27*39.4 = ~1064us ~= 532*2, Pluse < IRRX_PMAX */ +#define IRRX_26M_DMID (27) /* 27*39.4 = ~1064us ~= 532*2, D1 > IRRX_DMID, D0 =< IRRX_DMID */ +#define IRRX_26M_DMAX (55) /* 55*39.4 = ~2167us ~= 542*4, D < IRRX_DMAX */ + +#define IRTX_26M_TS_US (39) /* Ts frome IRTX_SAMPLE */ + /* IRTX_9MS_NUM 0xE4, 228*Ts=8994.6uS */ + /* IRTX_9MS_NUM1 127*Ts=5003.8uS */ + /* IRTX_9MS_NUM2 101*Ts=3979.4uS */ + /* IRTX_4P5MS_NUM 0x72, 114*Ts=4491.6uS */ + /* IRTX_560US_NUM 0x0E, 14*Ts=551.6uS */ + /* IRTX_1680US_NUM 0x2B, 43*Ts=1694.2uS */ +#define IRTX_26M_9MS_NUM (9000/IRTX_26M_TS_US) +#define IRTX_26M_9MS_NUM1 (0x7f) +#define IRTX_26M_9MS_NUM2 (IRTX_26M_9MS_NUM-IRTX_26M_9MS_NUM1) +#define IRTX_26M_4P5MS_NUM (4500/IRTX_26M_TS_US) +#define IRTX_26M_4P5MS_NUM1 (IRTX_26M_4P5MS_NUM) +#define IRTX_26M_2P25MS_NUM (IRTX_26M_4P5MS_NUM/2) +#define IRTX_26M_560US_NUM (560/IRTX_26M_TS_US) +#define IRTX_26M_1680US_NUM (1680/IRTX_26M_TS_US) + +#define IRTX_26M_NEC_CYCLE_MS (110) +#define IRTX_26M_NEC_IDC_VALUE ((IRTX_26M_NEC_CYCLE_MS*1000)/128/IRTX_26M_TS_US + 1) + +#define IRRX_24M_ACTIVE_T (0) /* ActionTreshold, Active Threshold */ +#define IRRX_24M_ACTIVE_T_C (1) /* SampleClock, Active Threshold Control, same with IRRX_ATHC_CLOCKUNIT_128 */ +#define IRRX_24M_L1_MIN (70) /* 70*42.7 = ~3.0ms, Lead1(9.0ms) Lead1R(4.5ms) > IR_L1_MIN */ +#define IRRX_24M_L0_MIN (40) /* 40*42.7 = ~1.7ms, Lead0(4.5ms) Lead0R(2.25ms)> IR_L0_MIN */ +#define IRRX_24M_PMAX (26) /* 26*42.7 = ~1109us ~= 561*2, Pluse < IRRX_PMAX */ +#define IRRX_24M_DMID (26) /* 26*42.7 = ~1109us ~= 561*2, D1 > IRRX_DMID, D0 =< IRRX_DMID */ +#define IRRX_24M_DMAX (53) /* 53*42.7 = ~2263us ~= 561*4, D < IRRX_DMAX */ + +#define IRTX_24M_TS_US (43) /* Ts frome IRTX_SAMPLE */ + /* IRTX_9MS_NUM 0xD1, 209*Ts=8924.3uS */ + /* IRTX_9MS_NUM1 127*Ts=5422.9uS */ + /* IRTX_9MS_NUM2 81*Ts=3458.7uS */ + /* IRTX_4P5MS_NUM 0x69, 105*Ts=4483.5uS */ + /* IRTX_560US_NUM 0x0D, 13*Ts=555.1uS */ + /* IRTX_1680US_NUM 0x27, 39*Ts=1665.3uS */ + +#define IRTX_24M_9MS_NUM (9000/IRTX_24M_TS_US) +#define IRTX_24M_9MS_NUM1 (0x7f) +#define IRTX_24M_9MS_NUM2 (IRTX_24M_9MS_NUM-IRTX_24M_9MS_NUM1) +#define IRTX_24M_4P5MS_NUM (4500/IRTX_24M_TS_US) +#define IRTX_24M_4P5MS_NUM1 (IRTX_24M_4P5MS_NUM) +#define IRTX_24M_2P25MS_NUM (IRTX_24M_4P5MS_NUM/2) +#define IRTX_24M_560US_NUM (560/IRTX_24M_TS_US) +#define IRTX_24M_1680US_NUM (1680/IRTX_24M_TS_US) + +#define IRTX_24M_NEC_CYCLE_MS (110) +#define IRTX_24M_NEC_IDC_VALUE ((IRTX_24M_NEC_CYCLE_MS*1000)/128/IRTX_24M_TS_US + 1) +#endif + +/** + * @brief Format code by NEC protocal. + * @param add: + * @arg add->[in] The address. + * @param key: + * @arg key->[in] The key. + * @retval Formated code. + */ +static __inline uint32_t IR_NEC_CODE(uint8_t add, uint8_t key) +{ + uint32_t code = (add << 24) | (((~add) << 16) & 0x00ff0000); + + code |= (key << 8) | ((~key) & 0x00ff); + + return code; +} + +/** + * @brief Check code valied. + * @param code: + * @arg code->[in] The code wanted be checked. + * @retval 1 if valid or 0 if unvalid. + */ +extern int32_t IRRX_NECCode_Valid(uint32_t code); + +/** + * @brief DePacket code by NEC protocal. + * @param buf: + * @arg buf->[in]Raw code buffer. + * @param dcnt: + * @arg dcnt->[in] Num of Raw code. + * @retval DePacket code if success or 0xffffffff if depacket failed. + */ +extern uint32_t IRRX_NECPacket_DeCode(uint8_t *buf, uint32_t dcnt); + +/** + * @brief Packet code by NEC protocal. + * @param txBuff: + * @arg txBuff->[out] Raw code will put in. + * @param ir_tx_code: + * @arg ir_tx_code->[in] The code will be Packeted. + * @retval Raw code num. + */ +extern uint32_t IRTX_NECPacket_Code(uint8_t *txBuff, uint32_t ir_tx_code); + +#ifdef __cplusplus +} +#endif + +#endif /* _DRIVER_CHIP_IR_NEC_H_ */ diff --git a/platform/mcu/xr871/include/driver/chip/sdmmc/card.h b/platform/mcu/xr871/include/driver/chip/sdmmc/card.h new file mode 100644 index 0000000000..fbc3c3e1ca --- /dev/null +++ b/platform/mcu/xr871/include/driver/chip/sdmmc/card.h @@ -0,0 +1,210 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _DRIVER_CHIP_HAL_SDMMC_CARD_H_ +#define _DRIVER_CHIP_HAL_SDMMC_CARD_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#define CONFIG_USE_SD + +struct mmc_ocr { + union { + uint32_t vol_window : 24, + to_1v8_acpt : 1, + : 5, + high_capacity : 1, + : 1; + uint32_t ocr; + }; +}; + +struct mmc_cid { + uint16_t oemid; + uint8_t manfid; + uint32_t serial; + uint16_t year; + uint8_t month; + uint8_t hwrev; + uint8_t fwrev; + uint8_t prod_name[6]; +}; + +struct mmc_csd { + uint8_t csd_ver; + uint8_t read_blk_len; + uint8_t c_size_mult; + uint32_t max_dtr; + uint16_t c_size; + uint16_t cmdclass; + uint32_t capacity; +}; + +struct sd_scr { + uint8_t sda_vsn; + uint8_t sda_spec3; + uint8_t sda_spec4; + uint8_t sda_spec5; + uint8_t bus_widths; + uint8_t security_sup; +#define SD_SCR_BUS_WIDTH_1 (1<<0) +#define SD_SCR_BUS_WIDTH_4 (1<<2) + uint8_t cmds; +#define SD_SCR_CMD20_SUPPORT (1<<0) +#define SD_SCR_CMD23_SUPPORT (1<<1) +}; + +struct sd_switch_caps { + uint32_t hs_max_dtr; + uint32_t uhs_max_dtr; +#define HIGH_SPEED_MAX_DTR 50000000 +#define UHS_SDR104_MAX_DTR 208000000 +#define UHS_SDR50_MAX_DTR 100000000 +#define UHS_DDR50_MAX_DTR 50000000 +#define UHS_SDR25_MAX_DTR UHS_DDR50_MAX_DTR +#define UHS_SDR12_MAX_DTR 25000000 + uint32_t sd3_bus_mode; +#define UHS_SDR12_BUS_SPEED 0 +#define HIGH_SPEED_BUS_SPEED 1 +#define UHS_SDR25_BUS_SPEED 1 +#define UHS_SDR50_BUS_SPEED 2 +#define UHS_SDR104_BUS_SPEED 3 +#define UHS_DDR50_BUS_SPEED 4 + +#define SD_MODE_HIGH_SPEED (1 << HIGH_SPEED_BUS_SPEED) +#define SD_MODE_UHS_SDR12 (1 << UHS_SDR12_BUS_SPEED) +#define SD_MODE_UHS_SDR25 (1 << UHS_SDR25_BUS_SPEED) +#define SD_MODE_UHS_SDR50 (1 << UHS_SDR50_BUS_SPEED) +#define SD_MODE_UHS_SDR104 (1 << UHS_SDR104_BUS_SPEED) +#define SD_MODE_UHS_DDR50 (1 << UHS_DDR50_BUS_SPEED) + uint32_t sd3_drv_type; +#define SD_DRIVER_TYPE_B 0x01 +#define SD_DRIVER_TYPE_A 0x02 +#define SD_DRIVER_TYPE_C 0x04 +#define SD_DRIVER_TYPE_D 0x08 + uint32_t sd3_curr_limit; +#define SD_SET_CURRENT_LIMIT_200 0 +#define SD_SET_CURRENT_LIMIT_400 1 +#define SD_SET_CURRENT_LIMIT_600 2 +#define SD_SET_CURRENT_LIMIT_800 3 + +#define SD_MAX_CURRENT_200 (1 << SD_SET_CURRENT_LIMIT_200) +#define SD_MAX_CURRENT_400 (1 << SD_SET_CURRENT_LIMIT_400) +#define SD_MAX_CURRENT_600 (1 << SD_SET_CURRENT_LIMIT_600) +#define SD_MAX_CURRENT_800 (1 << SD_SET_CURRENT_LIMIT_800) +}; + +struct mmc_ext_csd { + uint8_t version; + uint8_t card_type; + uint8_t csd_struc; + uint8_t hs_timing; + uint8_t bus_width; + uint8_t part_config; + uint8_t boot_bus_cond; +}; + +struct mmc_host; + +#define SDIO_MAX_FUNCS 7 + +struct mmc_card { + /* register info. */ + struct mmc_cid cid; + struct mmc_csd csd; + struct sd_scr scr; + struct mmc_ext_csd extcsd; + struct sd_switch_caps sw_caps; /* switch (CMD6) caps */ + /* card information */ + uint32_t type; /* card type */ +#define MMC_TYPE_MMC 0 /* MMC card */ +#define MMC_TYPE_SD 1 /* SD card */ +#define MMC_TYPE_SDIO 2 /* SDIO card */ +#define MMC_TYPE_SD_COMBO 3 /* SD combo (IO+mem) card */ + + uint32_t sd_bus_speed; /* Bus Speed Mode set for the card */ + uint32_t state; /* (our) card state */ +#define MMC_STATE_PRESENT (1 << 0) /* present */ +#define MMC_STATE_READONLY (1 << 1) /* card is read-only */ +#define MMC_STATE_HIGHSPEED (1 << 2) /* card is in high speed mode */ +#define MMC_STATE_BLOCKADDR (1 << 3) /* card uses block-addressing */ +#define MMC_STATE_HIGHSPEED_DDR (1 << 4) /* card is in high speed mode */ + +#define MMC_STATE_ULTRAHIGHSPEED (1<<5) /* card is in ultra high speed mode */ +#define MMC_CARD_SDXC (1<<6) /* card is SDXC */ +#define MMC_CARD_REMOVED (1<<7) /* card has been removed */ +#define MMC_STATE_HIGHSPEED_200 (1<<8) /* card is in HS200 mode */ +#define MMC_STATE_SLEEP (1<<9) /* card is in sleep state */ + + uint8_t bus_width; + uint8_t speed_class; + uint32_t cidno[4]; + + uint32_t rca; /* relative card address of device */ + struct mmc_ocr ocr; + struct mmc_host *host; /* the host this device belongs to */ +}; + +#define mmc_card_mmc(c) ((c)->type == MMC_TYPE_MMC) +#define mmc_card_sd(c) ((c)->type == MMC_TYPE_SD) +#define mmc_card_sdio(c) ((c)->type == MMC_TYPE_SDIO) + +#define mmc_card_present(c) ((c)->state & MMC_STATE_PRESENT) +#define mmc_card_readonly(c) ((c)->state & MMC_STATE_READONLY) +#define mmc_card_highspeed(c) ((c)->state & MMC_STATE_HIGHSPEED) +#define mmc_card_hs200(c) ((c)->state & MMC_STATE_HIGHSPEED_200) +#define mmc_card_blockaddr(c) ((c)->state & MMC_STATE_BLOCKADDR) +#define mmc_card_ddr_mode(c) ((c)->state & MMC_STATE_HIGHSPEED_DDR) +#define mmc_card_uhs(c) ((c)->state & MMC_STATE_ULTRAHIGHSPEED) +#define mmc_sd_card_uhs(c) ((c)->state & MMC_STATE_ULTRAHIGHSPEED) +#define mmc_card_ext_capacity(c) ((c)->state & MMC_CARD_SDXC) +#define mmc_card_removed(c) ((c) && ((c)->state & MMC_CARD_REMOVED)) +#define mmc_card_is_sleep(c) ((c)->state & MMC_STATE_SLEEP) + +#define mmc_card_set_present(c) ((c)->state |= MMC_STATE_PRESENT) +#define mmc_card_set_readonly(c) ((c)->state |= MMC_STATE_READONLY) +#define mmc_card_set_highspeed(c) ((c)->state |= MMC_STATE_HIGHSPEED) +#define mmc_card_set_hs200(c) ((c)->state |= MMC_STATE_HIGHSPEED_200) +#define mmc_card_set_blockaddr(c) ((c)->state |= MMC_STATE_BLOCKADDR) +#define mmc_card_set_ddr_mode(c) ((c)->state |= MMC_STATE_HIGHSPEED_DDR) +#define mmc_card_set_uhs(c) ((c)->state |= MMC_STATE_ULTRAHIGHSPEED) +#define mmc_sd_card_set_uhs(c) ((c)->state |= MMC_STATE_ULTRAHIGHSPEED) +#define mmc_card_set_ext_capacity(c) ((c)->state |= MMC_CARD_SDXC) +#define mmc_card_set_removed(c) ((c)->state |= MMC_CARD_REMOVED) +#define mmc_card_set_sleep(c) ((c)->state |= MMC_STATE_SLEEP) + +#define mmc_card_clr_sleep(c) ((c)->state &= ~MMC_STATE_SLEEP) + +#ifdef __cplusplus +} +#endif + +#endif /* _DRIVER_CHIP_HAL_SDMMC_CARD_H_ */ diff --git a/platform/mcu/xr871/include/driver/chip/sdmmc/hal_sdhost.h b/platform/mcu/xr871/include/driver/chip/sdmmc/hal_sdhost.h new file mode 100644 index 0000000000..48cda90700 --- /dev/null +++ b/platform/mcu/xr871/include/driver/chip/sdmmc/hal_sdhost.h @@ -0,0 +1,114 @@ +/** + * @file hal_sdhost.h + * @author XRADIO IOT WLAN Team + */ + +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _DRIVER_CHIP_HAL_SDMMC_SDHOST_H_ +#define _DRIVER_CHIP_HAL_SDMMC_SDHOST_H_ + +#include "driver/chip/hal_def.h" +#ifdef __CONFIG_ARCH_APP_CORE +#include "driver/chip/hal_gpio.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** @bried Detect card by gpio irq or D3. */ +//#define CONFIG_DETECT_CARD 1 + +typedef enum +{ + SDCGPIO_BAS = 0, + SDCGPIO_DET = 1, +} HAL_SDCGPIOType; + +typedef struct { + uint8_t data_bits; + int8_t has_detect_gpio; + GPIO_Port detect_port; + GPIO_Pin detect_pin; +} HAL_SDCGPIOCfg; + +/** @bried Detect card callback if used CONFIG_DETECT_CARD. */ +typedef void (*card_detect_cb)(uint32_t present); + +/** @bried SDC Init Structure definition. */ +typedef struct { +#ifdef CONFIG_DETECT_CARD + uint32_t cd_mode; +/* NOTE: The specification advise that CARD_DETECT_BY_D3 is not a preferred + * mechanism for card detection. Moreover it won't work with MMC cards. + * And, this won't work with external pull-up resistors on the card interface. + * The preferred card detection mechanism is a mechanical switch on the card connector. +*/ +#define CARD_DETECT_BY_GPIO_IRQ (2) /* mmc detected by gpio irq */ +#define CARD_ALWAYS_PRESENT (3) /* mmc always present, without detect pin */ +#define CARD_DETECT_BY_FS (4) /* mmc insert/remove by fs */ +#define CARD_DETECT_BY_D3 (5) /* mmc detected by data3 */ + + card_detect_cb cd_cb; /* NOTE: should delay 500ms before rescan card to wait Voltage stable */ +#endif +} SDC_InitTypeDef; + +/** + * @brief Initializes the SDC peripheral. + * @param sdc_id: + * @arg sdc_id->SDC ID. + * @param param: + * @arg param->[in] The configuration information. + * @retval SDC handler. + */ +extern struct mmc_host *HAL_SDC_Init(uint32_t sdc_id, SDC_InitTypeDef *param); +#else +/** + * @brief Initializes the SDC peripheral. + * @param sdc_id: + * @arg sdc_id->SDC ID. + * @retval SDC handler. + */ +extern struct mmc_host *HAL_SDC_Init(uint32_t sdc_id); +#endif + +/** + * @brief DeInitializes the SDC peripheral. + * @param sdc_id: + * @arg sdc_id-> SDC ID. + * @retval None. + */ +extern int32_t HAL_SDC_Deinit(uint32_t sdc_id); + +#ifdef __cplusplus +} +#endif + +#endif /* _DRIVER_CHIP_HAL_SDMMC_SDHOST_H_ */ diff --git a/platform/mcu/xr871/include/driver/chip/sdmmc/sdmmc.h b/platform/mcu/xr871/include/driver/chip/sdmmc/sdmmc.h new file mode 100644 index 0000000000..7bd78d4ef2 --- /dev/null +++ b/platform/mcu/xr871/include/driver/chip/sdmmc/sdmmc.h @@ -0,0 +1,486 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _DRIVER_CHIP_HAL_SDMMC_SDMMC_H_ +#define _DRIVER_CHIP_HAL_SDMMC_SDMMC_H_ + +#include "driver/chip/sdmmc/card.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define SD_EXCLUSIVE_HOST + +/* SD commands type argument response */ + /* class 0 */ +/* This is basically the same command as for MMC with some quirks. */ +#define SD_SEND_RELATIVE_ADDR 3 /* bcr R6, ask the card to publish a new relative address */ +#define SD_SEND_IF_COND 8 /* bcr [11:0] See below R7, sends Sd memory card interface condition, for sd 2.0 */ +#define SD_SWITCH_VOLTAGE 11 /* ac R1, switch to 1.8V bus signaling level */ + + /* class 10 */ +#define SD_SWITCH 6 /* adtc [31:0] See below R1, checks swithcable function(mode 0),And swtich card function(mode 1), for sd 1.x */ + + /* class 5 */ +#define SD_ERASE_WR_BLK_START 32 /* ac [31:0] data addr R1 */ +#define SD_ERASE_WR_BLK_END 33 /* ac [31:0] data addr R1 */ + + /* Application commands */ +#define SD_APP_SET_BUS_WIDTH 6 /* ac [1:0] bus width R1 */ +#define SD_APP_SD_STATUS 13 /* adtc R1 */ +#define SD_APP_SEND_NUM_WR_BLKS 22 /* adtc R1 */ +#define SD_APP_OP_COND 41 /* bcr [31:0] OCR R3 */ +#define SD_APP_SEND_SCR 51 /* adtc R1 */ + +/* OCR bit definitions */ +#define SD_OCR_S18R (1 << 24) /* 1.8V switching request */ +#define SD_ROCR_S18A SD_OCR_S18R /* 1.8V switching accepted by card */ +#define SD_OCR_XPC (1 << 28) /* SDXC power control */ +#define SD_OCR_CCS (1 << 30) /* Card Capacity Status */ + +/* + * SD_SWITCH argument format: + * + * [31] Check (0) or switch (1) + * [30:24] Reserved (0) + * [23:20] Function group 6 + * [19:16] Function group 5 + * [15:12] Function group 4 + * [11:8] Function group 3 + * [7:4] Function group 2 + * [3:0] Function group 1 + */ + +/* + * SD_SEND_IF_COND argument format: + * + * [31:12] Reserved (0) + * [11:8] Host Voltage Supply Flags + * [7:0] Check Pattern (0xAA) + */ + +/* + * SCR field definitions + */ +#define SCR_SPEC_VER_0 0 /* Implements system specification 1.0 - 1.01 */ +#define SCR_SPEC_VER_1 1 /* Implements system specification 1.10 */ +#define SCR_SPEC_VER_2 2 /* Implements system specification 2.00-3.0X */ + +/* + * SD bus widths + */ +#define SD_BUS_WIDTH_1 0 +#define SD_BUS_WIDTH_4 2 + +/* + * SD_SWITCH mode + */ +#define SD_SWITCH_CHECK 0 +#define SD_SWITCH_SET 1 + +/* + * SD_SWITCH function groups + */ +#define SD_SWITCH_GRP_ACCESS 0 + +/* + * SD_SWITCH access modes + */ +#define SD_SWITCH_ACCESS_DEF 0 +#define SD_SWITCH_ACCESS_HS 1 + +/*command index*/ +/*basic commands*/ +#define MMC_GO_IDLE_STATE 0 /* bc,rest all cards to idle state */ +#define MMC_ALL_SEND_CID 2 /* bcr R2, asks any card to send CID numbers */ +#define MMC_SET_RELATIVE_ADDR 3 /* bcr,ask the card to publish a new relative address,R6 */ +#define MMC_SET_DSR 4 /* bc,program the DSR of all cards,for sd 2.0 */ +#define MMC_SELECT_CARD 7 /* ac [31:16] RCA R1, select or deselect one card */ +#define MMC_SEND_EXT_CSD 8 /* adtc,the card sends it's ex_csd as a block of data,R1 */ +#define MMC_SEND_CSD 9 /* ac [31:16] RCA R2, addressed card send CSD */ +#define MMC_SEND_CID 10 /* ac, addressed card send CID,R2 */ +#define MMC_READ_DATA_UNTIL_STOP 11 /* atdc,read data stream from catd until a STOP_TRANSMITISSION follows,R1 */ +#define MMC_STOP_TRANSMISSION 12 /* ac, termilate a multiple block read/write operation,R1b */ +#define MMC_SEND_STATUS 13 /* ac, addressed card send its status regesiter,R1 */ +#define MMC_GO_INACTIVE_STATE 15 /* ac, sets the card to inactive state */ +#define MMC_SEND_TUNING_PATTERN 19 /* adtc, send 64 bytes pattern for sdr50 and sdr104 mode */ + +/* erase commands*/ +#define MMC_ERASE_WR_BLK_START 32 /* ac,sets the address of the first write bloock to be erased,R1 */ +#define MMC_ERASE_WR_BLK_END 33 /* ac,sets the address of the last write bloock to be erased,R1 */ + +/*MMC private command*/ +#define MMC_SEND_OP_COND 1 /* bcr,Activates the card's initialization process,R3 */ +#define MMC_SWITCH 6 /* ac,switch the mode of operation of the selected card or modifies tge EXT_CSD,R1b */ +#define MMC_BUSTEST_R 14 /* adtc,a host reads the reversed testing data pattern from a card,R1 */ +#define MMC_BUSTEST_W 19 /* adtc,a host sends the bus teset data pattern to a card,R1 */ + +/*sd io command*/ +#define IO_SEND_OP_COND 5 /* for SD IO.similar to ACMD41 for sd mem */ +#define IO_RW_DIRECT 52 +#define IO_RW_EXTENDED 53 +#define SD_IO_CMD54 54 + + /* class 2 */ +#define MMC_SET_BLOCKLEN 16 /* ac [31:0] block len R1, select a block length for all read/write cmds */ +#define MMC_READ_SINGLE_BLOCK 17 /* adtc [31:0] data addr R1, reads a block of the size seclected by SET_BLOCKLEN */ +#define MMC_READ_MULTIPLE_BLOCK 18 /* adtc [31:0] data addr R1, continuously send blocks of data until interrupted by a stop transmission commmad */ +#define MMC_SEND_TUNING_BLOCK 19 /* adtc R1 */ +#define MMC_SEND_TUNING_BLOCK_HS200 21 /* adtc R1 */ + + /* class 3 */ +#define MMC_WRITE_DAT_UNTIL_STOP 20 /* adtc [31:0] data addr R1 */ + + /* class 4 */ +#define MMC_SET_BLOCK_COUNT 23 /* adtc [31:0] data addr R1 */ +#define MMC_WRITE_SINGLE_BLOCK 24 /* adtc [31:0] data addr R1, writes a block of the size seclected by SET_BLOCKLEN */ +#define MMC_WRITE_MULTIPLE_BLOCK 25 /* adtc R1, continuously writes blocks of data until interrupted by a stop transmission commmad */ +#define MMC_PROGRAM_CID 26 /* adtc R1 */ +#define MMC_PROGRAM_CSD 27 /* adtc R1, program the programmable bits of CSD */ + + /* class 6 */ +#define MMC_SET_WRITE_PROT 28 /* ac [31:0] data addr R1b, sets the write protect bit of the addressed group */ +#define MMC_CLR_WRITE_PROT 29 /* ac [31:0] data addr R1b, clears the write protect bit of the addressed group */ +#define MMC_SEND_WRITE_PROT 30 /* adtc [31:0] wpdata addr R1, ask the card to send status of the write protection bits */ + + /* class 5 */ +#define MMC_ERASE_GROUP_START 35 /* ac [31:0] data addr R1 */ +#define MMC_ERASE_GROUP_END 36 /* ac [31:0] data addr R1 */ +#define MMC_ERASE 38 /* ac R1b, erase all selected write blocks */ + + /* class 9 */ +#define MMC_FAST_IO 39 /* ac R4, used to read or write 8 bit registers */ +#define MMC_GO_IRQ_STATE 40 /* bcr R5, sets the system info interrupt mode */ + + /* class 7 */ +#define MMC_LOCK_UNLOCK 42 /* adtc R1b, lock or unlock sd card */ + + /* class 8 */ +#define MMC_APP_CMD 55 /* ac [31:16] RCA R1, indicates the next cmd is an specific cmd */ +#define MMC_GEN_CMD 56 /* adtc [0] RD/WR R1, send or get a block of data */ + +static inline uint32_t mmc_op_multi(uint32_t opcode) +{ + return opcode == MMC_WRITE_MULTIPLE_BLOCK || + opcode == MMC_READ_MULTIPLE_BLOCK; +} + +/* + * MMC_SWITCH argument format: + * + * [31:26] Always 0 + * [25:24] Access Mode + * [23:16] Location of target Byte in EXT_CSD + * [15:08] Value Byte + * [07:03] Always 0 + * [02:00] Command Set + */ + +/* + MMC status in R1, for native mode (SPI bits are different) + Type + e : error bit + s : status bit + r : detected and set for the actual command response + x : detected and set during command execution. the host must poll + the card by sending status command in order to read these bits. + Clear condition + a : according to the card state + b : always related to the previous command. Reception of + a valid command will clear it (with a delay of one command) + c : clear by read + */ + +#define R1_OUT_OF_RANGE (1 << 31) /* er, c */ +#define R1_ADDRESS_ERROR (1 << 30) /* erx, c */ +#define R1_BLOCK_LEN_ERROR (1 << 29) /* er, c */ +#define R1_ERASE_SEQ_ERROR (1 << 28) /* er, c */ +#define R1_ERASE_PARAM (1 << 27) /* ex, c */ +#define R1_WP_VIOLATION (1 << 26) /* erx, c */ +#define R1_CARD_IS_LOCKED (1 << 25) /* sx, a */ +#define R1_LOCK_UNLOCK_FAILED (1 << 24) /* erx, c */ +#define R1_COM_CRC_ERROR (1 << 23) /* er, b */ +#define R1_ILLEGAL_COMMAND (1 << 22) /* er, b */ +#define R1_CARD_ECC_FAILED (1 << 21) /* ex, c */ +#define R1_CC_ERROR (1 << 20) /* erx, c */ +#define R1_ERROR (1 << 19) /* erx, c */ +#define R1_UNDERRUN (1 << 18) /* ex, c */ +#define R1_OVERRUN (1 << 17) /* ex, c */ +#define R1_CID_CSD_OVERWRITE (1 << 16) /* erx, c, CID/CSD overwrite */ +#define R1_WP_ERASE_SKIP (1 << 15) /* sx, c */ +#define R1_CARD_ECC_DISABLED (1 << 14) /* sx, a */ +#define R1_ERASE_RESET (1 << 13) /* sr, c */ +#define R1_STATUS(x) (x & 0xFFFFE000) +#define R1_CURRENT_STATE(x) ((x & 0x00001E00) >> 9) /* sx, b (4 bits) */ +#define R1_READY_FOR_DATA (1 << 8) /* sx, a */ +#define R1_SWITCH_ERROR (1 << 7) /* sx, c */ +#define R1_APP_CMD (1 << 5) /* sr, c */ + +#define R1_STATE_IDLE 0 +#define R1_STATE_READY 1 +#define R1_STATE_IDENT 2 +#define R1_STATE_STBY 3 +#define R1_STATE_TRAN 4 +#define R1_STATE_DATA 5 +#define R1_STATE_RCV 6 +#define R1_STATE_PRG 7 +#define R1_STATE_DIS 8 + +/* + * OCR bits are mostly in host.h + */ +#define MMC_CARD_BUSY 0x80000000 /* Card Power up status bit */ + +/*ce-ata command*/ +#define CEATA_RW_MULTIPLE_REGISTER 60 +#define CEATA_RW_MULTIPLE_BLOCK 61 + +/*application specific commands used by sd mem*/ +#define SET_BUS_WIDTH 6 /* ac,define the bus width(00 = 1bit, 10 = 4bit),R1 */ +#define SD_STATUS 13 /* adtc,send the sd card status,R1 */ +#define SEND_NUM_WR_BLOCKS 22 /* adtc,send the number of written write blocks,R1 */ +#define SET_WR_BLK_ERASE_CNT 23 /* ac,set the number of write blocks to be pre-erased before writing,R1 */ +#define SD_APP_OP_COND 41 /* bcr,asks the accessed card to send its OCR(operating conditon register) content,R3 */ +#define SET_CLR_CARD_DETECT 42 /* ac,connect or disconnect the pull up resistor of the card for card detect,R1 */ +#define SEND_SCR 51 /* adtc,reads the SCR(sd configure register),R1 */ + +/* MMC/SD in SPI mode reports R1 status always, and R2 for SEND_STATUS + * R1 is the low order byte; R2 is the next highest byte, when present. + */ +#define R1_SPI_IDLE (1 << 0) +#define R1_SPI_ERASE_RESET (1 << 1) +#define R1_SPI_ILLEGAL_COMMAND (1 << 2) +#define R1_SPI_COM_CRC (1 << 3) +#define R1_SPI_ERASE_SEQ (1 << 4) +#define R1_SPI_ADDRESS (1 << 5) +#define R1_SPI_PARAMETER (1 << 6) + +/* R1 bit 7 is always zero */ +#define R2_SPI_CARD_LOCKED (1 << 8) +#define R2_SPI_WP_ERASE_SKIP (1 << 9) /* or lock/unlock fail */ +#define R2_SPI_LOCK_UNLOCK_FAIL R2_SPI_WP_ERASE_SKIP +#define R2_SPI_ERROR (1 << 10) +#define R2_SPI_CC_ERROR (1 << 11) +#define R2_SPI_CARD_ECC_ERROR (1 << 12) +#define R2_SPI_WP_VIOLATION (1 << 13) +#define R2_SPI_ERASE_PARAM (1 << 14) +#define R2_SPI_OUT_OF_RANGE (1 << 15) /* or CSD overwrite */ +#define R2_SPI_CSD_OVERWRITE R2_SPI_OUT_OF_RANGE + +#define CEATA_INDENTIFY_DEVICE 0xec +#define CEATA_READ_DMA_EXT 0x25 +#define CEATA_WRITE_DMA_EXT 0x35 +#define CEATA_STANBY_IMMIDIATE 0xe0 +#define CEATA_FLUSH_CACHE_EXT 0Xea + +/* + * OCR bits are mostly in host.h + */ +#define MMC_CARD_BUSY 0x80000000 /* Card Power up status bit */ + +/* Card Command Classes (CCC) */ +#define CCC_BASIC (1<<0) /* (0) Basic protocol functions */ + /* (CMD0,1,2,3,4,7,9,10,12,13,15) */ + /* (and for SPI, CMD58,59) */ +#define CCC_STREAM_READ (1<<1) /* (1) Stream read commands */ + /* (CMD11) */ +#define CCC_BLOCK_READ (1<<2) /* (2) Block read commands */ + /* (CMD16,17,18) */ +#define CCC_STREAM_WRITE (1<<3) /* (3) Stream write commands */ + /* (CMD20) */ +#define CCC_BLOCK_WRITE (1<<4) /* (4) Block write commands */ + /* (CMD16,24,25,26,27) */ +#define CCC_ERASE (1<<5) /* (5) Ability to erase blocks */ + /* (CMD32,33,34,35,36,37,38,39) */ +#define CCC_WRITE_PROT (1<<6) /* (6) Able to write protect blocks */ + /* (CMD28,29,30) */ +#define CCC_LOCK_CARD (1<<7) /* (7) Able to lock down card */ + /* (CMD16,CMD42) */ +#define CCC_APP_SPEC (1<<8) /* (8) Application specific */ + /* (CMD55,56,57,ACMD*) */ +#define CCC_IO_MODE (1<<9) /* (9) I/O mode */ + /* (CMD5,39,40,52,53) */ +#define CCC_SWITCH (1<<10) /* (10) High speed switch */ + /* (CMD6,34,35,36,37,50) */ + /* (11) Reserved */ + /* (CMD?) */ + +/* CSD field definitions */ +#define MMC_CSD_STRUCT_VER_1_0 0 /* Valid for system specification 1.0 - 1.2 */ +#define MMC_CSD_STRUCT_VER_1_1 1 /* Valid for system specification 1.4 - 2.2 */ +#define MMC_CSD_STRUCT_VER_1_2 2 /* Valid for system specification 3.1 - 3.2 - 3.31 - 4.0 - 4.1 */ +#define MMC_CSD_STRUCT_EXT_CSD 3 /* Version is coded in CSD_STRUCTURE in EXT_CSD */ + +#define MMC_CSD_SPEC_VER_0 0 /* Implements system specification 1.0 - 1.2 */ +#define MMC_CSD_SPEC_VER_1 1 /* Implements system specification 1.4 */ +#define MMC_CSD_SPEC_VER_2 2 /* Implements system specification 2.0 - 2.2 */ +#define MMC_CSD_SPEC_VER_3 3 /* Implements system specification 3.1 - 3.2 - 3.31 */ +#define MMC_CSD_SPEC_VER_4 4 /* Implements system specification 4.0 - 4.1 */ + +/* EXT_CSD fields */ +#define MMC_EXT_CSD_BOOT_BUS_COND 177 /* R/W */ +#define MMC_EXT_CSD_PART_CONF 179 /* R/W */ +#define MMC_EXT_CSD_BUS_WIDTH 183 /* R/W */ +#define MMC_EXT_CSD_HS_TIMING 185 /* R/W */ +#define MMC_EXT_CSD_CARD_TYPE 196 /* RO */ +#define MMC_EXT_CSD_REV 192 /* RO */ +#define MMC_EXT_CSD_SEC_CNT 212 /* RO, 4 bytes */ + +/* EXT_CSD field definitions */ +#define MMC_EXT_CSD_CMD_SET_NORMAL (1<<0) +#define MMC_EXT_CSD_CMD_SET_SECURE (1<<1) +#define MMC_EXT_CSD_CMD_SET_CPSECURE (1<<2) + +#define MMC_EXT_CSD_CARD_TYPE_26 (1<<0) /* Card can run at 26MHz */ +#define MMC_EXT_CSD_CARD_TYPE_52 (1<<1) /* Card can run at 52MHz */ + +#define MMC_EXT_CSD_BUS_WIDTH_1 0 /* Card is in 1 bit mode */ +#define MMC_EXT_CSD_BUS_WIDTH_4 1 /* Card is in 4 bit mode */ +#define MMC_EXT_CSD_BUS_WIDTH_8 2 /* Card is in 8 bit mode */ +#define MMC_EXT_CSD_BUS_WIDTH_4_DDR 5 /* Card is in 4 bit mode in DDR mode */ +#define MMC_EXT_CSD_BUS_WIDTH_8_DDR 6 /* Card is in 8 bit mode in DDR mode */ + +/* MMC_SWITCH access modes */ +#define MMC_SWITCH_MODE_CMD_SET 0x00 /* Change the command set */ +#define MMC_SWITCH_MODE_SET_BITS 0x01 /* Set bits which are 1 in value */ +#define MMC_SWITCH_MODE_CLEAR_BITS 0x02 /* Clear bits which are 1 in value */ +#define MMC_SWITCH_MODE_WRITE_BYTE 0x03 /* Set target to value */ + +/* MMC_SWITCH boot modes */ +#define MMC_SWITCH_MMCPART_NOAVAILABLE (0xff) +#define MMC_SWITCH_PART_ACCESS_MASK (0x7) +#define MMC_SWITCH_PART_SUPPORT (0x1) +#define MMC_SWITCH_PART_BOOT_PART_MASK (0x7 << 3) +#define MMC_SWITCH_PART_BOOT_PART_NONE (0x0) +#define MMC_SWITCH_PART_BOOT_PART_1 (0x1) +#define MMC_SWITCH_PART_BOOT_PART_2 (0x2) +#define MMC_SWITCH_PART_BOOT_USER (0x7) +#define MMC_SWITCH_PART_BOOT_ACK_MASK (0x1 << 6) +#define MMC_SWITCH_PART_BOOT_ACK_ENB (0x1) + +/* MMC_SWITCH boot condition */ +#define MMC_SWITCH_MMCBOOT_BUS_NOAVAILABLE (0xff) +#define MMC_SWITCH_BOOT_MODE_MASK (0x3 << 3) +#define MMC_SWITCH_BOOT_SDR_NORMAL (0x0) +#define MMC_SWITCH_BOOT_SDR_HS (0x1) +#define MMC_SWITCH_BOOT_DDR (0x2) +#define MMC_SWITCH_BOOT_RST_BUS_COND_MASK (0x1 << 2) +#define MMC_SWITCH_BOOT_RST_BUS_COND (0x0) +#define MMC_SWITCH_BOOT_RETAIN_BUS_COND (0x1) +#define MMC_SWITCH_BOOT_BUS_WIDTH_MASK (0x3 << 0) +#define MMC_SWITCH_BOOT_BUS_SDRx1_DDRx4 (0x0) +#define MMC_SWITCH_BOOT_BUS_SDRx4_DDRx4 (0x1) +#define MMC_SWITCH_BOOT_BUS_SDRx8_DDRx8 (0x2) + +/* SD_SWITCH function groups */ +#define SD_SWITCH_GRP_ACCESS_MODE 0 +#define SD_SWITCH_GRP_CMD_SYSTEM 1 +#define SD_SWITCH_GRP_DRV_STRENGTH 2 +#define SD_SWITCH_GRP_CUR_LIMIT 3 + +/* SD_SWITCH access modes */ +#define SD_SWITCH_ACCESS_DEF_SDR12 0 +#define SD_SWITCH_ACCESS_HS_SDR25 1 +#define SD_SWITCH_ACCESS_SDR50 2 +#define SD_SWITCH_ACCESS_SDR104 3 +#define SD_SWITCH_ACCESS_DDR50 4 + +/* SD_SWITCH cmd system */ +#define SD_SWITCH_CMDSYS_DEF 0 +#define SD_SWITCH_CMDSYS_EC 1 +#define SD_SWITCH_CMDSYS_OTP 3 +#define SD_SWITCH_CMDSYS_ASSD 4 +#define SD_SWITCH_CMDSYS_ESD 0xc + +/* SD_SWITCH driver strength */ +#define SD_SWITCH_DRVSTR_DEF_TB 0 +#define SD_SWITCH_DRVSTR_DEF_TA 1 +#define SD_SWITCH_DRVSTR_DEF_TC 2 +#define SD_SWITCH_DRVSTR_DEF_TD 3 + +/* SD_SWITCH current limit */ +#define SD_SWITCH_CURLMT_DEF_200MA 0 +#define SD_SWITCH_CURLMT_DEF_400MA 1 +#define SD_SWITCH_CURLMT_DEF_600MA 2 +#define SD_SWITCH_CURLMT_DEF_800MA 3 + +/** + * @brief read SD card. + * @param card: + * @arg card->card handler. + * @param buf: + * @arg buf->for store readed data. + * @param sblk: + * @arg sblk->start block num. + * @param nblk: + * @arg nblk->number of blocks. + * @retval 0 if success or other if failed. + */ +extern int32_t mmc_block_read(struct mmc_card *card, uint8_t *buf, uint64_t sblk, uint32_t nblk); + +/** + * @brief write SD card. + * @param card: + * @arg card->card handler. + * @param buf: + * @arg buf->data will be write. + * @param sblk: + * @arg sblk->start block num. + * @param nblk: + * @arg nblk->number of blocks. + * @retval 0 if success or other if failed. + */ +extern int32_t mmc_block_write(struct mmc_card *card, const uint8_t *buf, uint64_t sblk, uint32_t nblk); + +/** + * @brief scan or rescan SD card. + * @param card: + * @arg card->card handler. + * @param sdc_id: + * @arg sdc_id->SDC ID which card on. + * @retval 0 if success or other if failed. + */ +extern int32_t mmc_rescan(struct mmc_card *card, uint32_t sdc_id); + +/** + * @brief deinit SD card. + * @param card: + * @arg card->card handler. + * @retval 0 if success or other if failed. + */ +extern int32_t mmc_card_deinit(struct mmc_card *card); + +extern int32_t mmc_test(uint32_t cd_mode); + +#ifdef __cplusplus +} +#endif + +#endif /* _DRIVER_CHIP_HAL_SDMMC_SDMMC_H_ */ diff --git a/platform/mcu/xr871/include/driver/chip/system_chip.h b/platform/mcu/xr871/include/driver/chip/system_chip.h new file mode 100644 index 0000000000..0fb5d697fc --- /dev/null +++ b/platform/mcu/xr871/include/driver/chip/system_chip.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _DRIVER_CHIP_SYSTEM_CHIP_H_ +#define _DRIVER_CHIP_SYSTEM_CHIP_H_ + +#include "driver/chip/hal_def.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define SYSTEM_DEINIT_FLAG_RESET_CLK HAL_BIT(0) + +void SystemInit(void); +void SystemDeInit(uint32_t flag); +void SystemCoreClockUpdate(void); + +#ifdef __cplusplus +} +#endif + +#endif /* _DRIVER_CHIP_SYSTEM_CHIP_H_ */ diff --git a/platform/mcu/xr871/include/driver/cmsis/core_cm3.h b/platform/mcu/xr871/include/driver/cmsis/core_cm3.h new file mode 100644 index 0000000000..e1357c6735 --- /dev/null +++ b/platform/mcu/xr871/include/driver/cmsis/core_cm3.h @@ -0,0 +1,1650 @@ +/**************************************************************************//** + * @file core_cm3.h + * @brief CMSIS Cortex-M3 Core Peripheral Access Layer Header File + * @version V4.00 + * @date 22. August 2014 + * + * @note + * + ******************************************************************************/ +/* Copyright (c) 2009 - 2014 ARM LIMITED + + All rights reserved. + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + - Neither the name of ARM nor the names of its contributors may be used + to endorse or promote products derived from this software without + specific prior written permission. + * + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDERS AND CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + ---------------------------------------------------------------------------*/ + + +#if defined ( __ICCARM__ ) + #pragma system_include /* treat file as system include file for MISRA check */ +#endif + +#ifndef __CORE_CM3_H_GENERIC +#define __CORE_CM3_H_GENERIC + +#ifdef __cplusplus + extern "C" { +#endif + +/** \page CMSIS_MISRA_Exceptions MISRA-C:2004 Compliance Exceptions + CMSIS violates the following MISRA-C:2004 rules: + + \li Required Rule 8.5, object/function definition in header file.
+ Function definitions in header files are used to allow 'inlining'. + + \li Required Rule 18.4, declaration of union type or object of union type: '{...}'.
+ Unions are used for effective representation of core registers. + + \li Advisory Rule 19.7, Function-like macro defined.
+ Function-like macros are used to allow more efficient code. + */ + + +/******************************************************************************* + * CMSIS definitions + ******************************************************************************/ +/** \ingroup Cortex_M3 + @{ + */ + +/* CMSIS CM3 definitions */ +#define __CM3_CMSIS_VERSION_MAIN (0x04) /*!< [31:16] CMSIS HAL main version */ +#define __CM3_CMSIS_VERSION_SUB (0x00) /*!< [15:0] CMSIS HAL sub version */ +#define __CM3_CMSIS_VERSION ((__CM3_CMSIS_VERSION_MAIN << 16) | \ + __CM3_CMSIS_VERSION_SUB ) /*!< CMSIS HAL version number */ + +#define __CORTEX_M (0x03) /*!< Cortex-M Core */ + + +#if defined ( __CC_ARM ) + #define __ASM __asm /*!< asm keyword for ARM Compiler */ + #define __INLINE __inline /*!< inline keyword for ARM Compiler */ + #define __STATIC_INLINE static __inline + +#elif defined ( __GNUC__ ) + #define __ASM __asm /*!< asm keyword for GNU Compiler */ + #define __INLINE inline /*!< inline keyword for GNU Compiler */ + #define __STATIC_INLINE static inline + +#elif defined ( __ICCARM__ ) + #define __ASM __asm /*!< asm keyword for IAR Compiler */ + #define __INLINE inline /*!< inline keyword for IAR Compiler. Only available in High optimization mode! */ + #define __STATIC_INLINE static inline + +#elif defined ( __TMS470__ ) + #define __ASM __asm /*!< asm keyword for TI CCS Compiler */ + #define __STATIC_INLINE static inline + +#elif defined ( __TASKING__ ) + #define __ASM __asm /*!< asm keyword for TASKING Compiler */ + #define __INLINE inline /*!< inline keyword for TASKING Compiler */ + #define __STATIC_INLINE static inline + +#elif defined ( __CSMC__ ) + #define __packed + #define __ASM _asm /*!< asm keyword for COSMIC Compiler */ + #define __INLINE inline /*use -pc99 on compile line !< inline keyword for COSMIC Compiler */ + #define __STATIC_INLINE static inline + +#endif + +/** __FPU_USED indicates whether an FPU is used or not. + This core does not support an FPU at all +*/ +#define __FPU_USED 0 + +#if defined ( __CC_ARM ) + #if defined __TARGET_FPU_VFP + #warning "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #endif + +#elif defined ( __GNUC__ ) + #if defined (__VFP_FP__) && !defined(__SOFTFP__) + #warning "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #endif + +#elif defined ( __ICCARM__ ) + #if defined __ARMVFP__ + #warning "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #endif + +#elif defined ( __TMS470__ ) + #if defined __TI__VFP_SUPPORT____ + #warning "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #endif + +#elif defined ( __TASKING__ ) + #if defined __FPU_VFP__ + #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #endif + +#elif defined ( __CSMC__ ) /* Cosmic */ + #if ( __CSMC__ & 0x400) // FPU present for parser + #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #endif +#endif + +#include /* standard types definitions */ +#include /* Core Instruction Access */ +#include /* Core Function Access */ + +#ifdef __cplusplus +} +#endif + +#endif /* __CORE_CM3_H_GENERIC */ + +#ifndef __CMSIS_GENERIC + +#ifndef __CORE_CM3_H_DEPENDANT +#define __CORE_CM3_H_DEPENDANT + +#ifdef __cplusplus + extern "C" { +#endif + +/* check device defines and use defaults */ +#if defined __CHECK_DEVICE_DEFINES + #ifndef __CM3_REV + #define __CM3_REV 0x0200 + #warning "__CM3_REV not defined in device header file; using default!" + #endif + + #ifndef __MPU_PRESENT + #define __MPU_PRESENT 0 + #warning "__MPU_PRESENT not defined in device header file; using default!" + #endif + + #ifndef __NVIC_PRIO_BITS + #define __NVIC_PRIO_BITS 4 + #warning "__NVIC_PRIO_BITS not defined in device header file; using default!" + #endif + + #ifndef __Vendor_SysTickConfig + #define __Vendor_SysTickConfig 0 + #warning "__Vendor_SysTickConfig not defined in device header file; using default!" + #endif +#endif + +/* IO definitions (access restrictions to peripheral registers) */ +/** + \defgroup CMSIS_glob_defs CMSIS Global Defines + + IO Type Qualifiers are used + \li to specify the access to peripheral variables. + \li for automatic generation of peripheral register debug information. +*/ +#ifdef __cplusplus + #define __I volatile /*!< Defines 'read only' permissions */ +#else + #define __I volatile const /*!< Defines 'read only' permissions */ +#endif +#define __O volatile /*!< Defines 'write only' permissions */ +#define __IO volatile /*!< Defines 'read / write' permissions */ + +/*@} end of group Cortex_M3 */ + + + +/******************************************************************************* + * Register Abstraction + Core Register contain: + - Core Register + - Core NVIC Register + - Core SCB Register + - Core SysTick Register + - Core Debug Register + - Core MPU Register + ******************************************************************************/ +/** \defgroup CMSIS_core_register Defines and Type Definitions + \brief Type definitions and defines for Cortex-M processor based devices. +*/ + +/** \ingroup CMSIS_core_register + \defgroup CMSIS_CORE Status and Control Registers + \brief Core Register type definitions. + @{ + */ + +/** \brief Union type to access the Application Program Status Register (APSR). + */ +typedef union +{ + struct + { +#if (__CORTEX_M != 0x04) + uint32_t _reserved0:27; /*!< bit: 0..26 Reserved */ +#else + uint32_t _reserved0:16; /*!< bit: 0..15 Reserved */ + uint32_t GE:4; /*!< bit: 16..19 Greater than or Equal flags */ + uint32_t _reserved1:7; /*!< bit: 20..26 Reserved */ +#endif + uint32_t Q:1; /*!< bit: 27 Saturation condition flag */ + uint32_t V:1; /*!< bit: 28 Overflow condition code flag */ + uint32_t C:1; /*!< bit: 29 Carry condition code flag */ + uint32_t Z:1; /*!< bit: 30 Zero condition code flag */ + uint32_t N:1; /*!< bit: 31 Negative condition code flag */ + } b; /*!< Structure used for bit access */ + uint32_t w; /*!< Type used for word access */ +} APSR_Type; + + +/** \brief Union type to access the Interrupt Program Status Register (IPSR). + */ +typedef union +{ + struct + { + uint32_t ISR:9; /*!< bit: 0.. 8 Exception number */ + uint32_t _reserved0:23; /*!< bit: 9..31 Reserved */ + } b; /*!< Structure used for bit access */ + uint32_t w; /*!< Type used for word access */ +} IPSR_Type; + + +/** \brief Union type to access the Special-Purpose Program Status Registers (xPSR). + */ +typedef union +{ + struct + { + uint32_t ISR:9; /*!< bit: 0.. 8 Exception number */ +#if (__CORTEX_M != 0x04) + uint32_t _reserved0:15; /*!< bit: 9..23 Reserved */ +#else + uint32_t _reserved0:7; /*!< bit: 9..15 Reserved */ + uint32_t GE:4; /*!< bit: 16..19 Greater than or Equal flags */ + uint32_t _reserved1:4; /*!< bit: 20..23 Reserved */ +#endif + uint32_t T:1; /*!< bit: 24 Thumb bit (read 0) */ + uint32_t IT:2; /*!< bit: 25..26 saved IT state (read 0) */ + uint32_t Q:1; /*!< bit: 27 Saturation condition flag */ + uint32_t V:1; /*!< bit: 28 Overflow condition code flag */ + uint32_t C:1; /*!< bit: 29 Carry condition code flag */ + uint32_t Z:1; /*!< bit: 30 Zero condition code flag */ + uint32_t N:1; /*!< bit: 31 Negative condition code flag */ + } b; /*!< Structure used for bit access */ + uint32_t w; /*!< Type used for word access */ +} xPSR_Type; + + +/** \brief Union type to access the Control Registers (CONTROL). + */ +typedef union +{ + struct + { + uint32_t nPRIV:1; /*!< bit: 0 Execution privilege in Thread mode */ + uint32_t SPSEL:1; /*!< bit: 1 Stack to be used */ + uint32_t FPCA:1; /*!< bit: 2 FP extension active flag */ + uint32_t _reserved0:29; /*!< bit: 3..31 Reserved */ + } b; /*!< Structure used for bit access */ + uint32_t w; /*!< Type used for word access */ +} CONTROL_Type; + +/*@} end of group CMSIS_CORE */ + + +/** \ingroup CMSIS_core_register + \defgroup CMSIS_NVIC Nested Vectored Interrupt Controller (NVIC) + \brief Type definitions for the NVIC Registers + @{ + */ + +/** \brief Structure type to access the Nested Vectored Interrupt Controller (NVIC). + */ +typedef struct +{ + __IO uint32_t ISER[8]; /*!< Offset: 0x000 (R/W) Interrupt Set Enable Register */ + uint32_t RESERVED0[24]; + __IO uint32_t ICER[8]; /*!< Offset: 0x080 (R/W) Interrupt Clear Enable Register */ + uint32_t RSERVED1[24]; + __IO uint32_t ISPR[8]; /*!< Offset: 0x100 (R/W) Interrupt Set Pending Register */ + uint32_t RESERVED2[24]; + __IO uint32_t ICPR[8]; /*!< Offset: 0x180 (R/W) Interrupt Clear Pending Register */ + uint32_t RESERVED3[24]; + __IO uint32_t IABR[8]; /*!< Offset: 0x200 (R/W) Interrupt Active bit Register */ + uint32_t RESERVED4[56]; + __IO uint8_t IP[240]; /*!< Offset: 0x300 (R/W) Interrupt Priority Register (8Bit wide) */ + uint32_t RESERVED5[644]; + __O uint32_t STIR; /*!< Offset: 0xE00 ( /W) Software Trigger Interrupt Register */ +} NVIC_Type; + +/* Software Triggered Interrupt Register Definitions */ +#define NVIC_STIR_INTID_Pos 0 /*!< STIR: INTLINESNUM Position */ +#define NVIC_STIR_INTID_Msk (0x1FFUL << NVIC_STIR_INTID_Pos) /*!< STIR: INTLINESNUM Mask */ + +/*@} end of group CMSIS_NVIC */ + + +/** \ingroup CMSIS_core_register + \defgroup CMSIS_SCB System Control Block (SCB) + \brief Type definitions for the System Control Block Registers + @{ + */ + +/** \brief Structure type to access the System Control Block (SCB). + */ +typedef struct +{ + __I uint32_t CPUID; /*!< Offset: 0x000 (R/ ) CPUID Base Register */ + __IO uint32_t ICSR; /*!< Offset: 0x004 (R/W) Interrupt Control and State Register */ + __IO uint32_t VTOR; /*!< Offset: 0x008 (R/W) Vector Table Offset Register */ + __IO uint32_t AIRCR; /*!< Offset: 0x00C (R/W) Application Interrupt and Reset Control Register */ + __IO uint32_t SCR; /*!< Offset: 0x010 (R/W) System Control Register */ + __IO uint32_t CCR; /*!< Offset: 0x014 (R/W) Configuration Control Register */ + __IO uint8_t SHP[12]; /*!< Offset: 0x018 (R/W) System Handlers Priority Registers (4-7, 8-11, 12-15) */ + __IO uint32_t SHCSR; /*!< Offset: 0x024 (R/W) System Handler Control and State Register */ + __IO uint32_t CFSR; /*!< Offset: 0x028 (R/W) Configurable Fault Status Register */ + __IO uint32_t HFSR; /*!< Offset: 0x02C (R/W) HardFault Status Register */ + __IO uint32_t DFSR; /*!< Offset: 0x030 (R/W) Debug Fault Status Register */ + __IO uint32_t MMFAR; /*!< Offset: 0x034 (R/W) MemManage Fault Address Register */ + __IO uint32_t BFAR; /*!< Offset: 0x038 (R/W) BusFault Address Register */ + __IO uint32_t AFSR; /*!< Offset: 0x03C (R/W) Auxiliary Fault Status Register */ + __I uint32_t PFR[2]; /*!< Offset: 0x040 (R/ ) Processor Feature Register */ + __I uint32_t DFR; /*!< Offset: 0x048 (R/ ) Debug Feature Register */ + __I uint32_t ADR; /*!< Offset: 0x04C (R/ ) Auxiliary Feature Register */ + __I uint32_t MMFR[4]; /*!< Offset: 0x050 (R/ ) Memory Model Feature Register */ + __I uint32_t ISAR[5]; /*!< Offset: 0x060 (R/ ) Instruction Set Attributes Register */ + uint32_t RESERVED0[5]; + __IO uint32_t CPACR; /*!< Offset: 0x088 (R/W) Coprocessor Access Control Register */ +} SCB_Type; + +/* SCB CPUID Register Definitions */ +#define SCB_CPUID_IMPLEMENTER_Pos 24 /*!< SCB CPUID: IMPLEMENTER Position */ +#define SCB_CPUID_IMPLEMENTER_Msk (0xFFUL << SCB_CPUID_IMPLEMENTER_Pos) /*!< SCB CPUID: IMPLEMENTER Mask */ + +#define SCB_CPUID_VARIANT_Pos 20 /*!< SCB CPUID: VARIANT Position */ +#define SCB_CPUID_VARIANT_Msk (0xFUL << SCB_CPUID_VARIANT_Pos) /*!< SCB CPUID: VARIANT Mask */ + +#define SCB_CPUID_ARCHITECTURE_Pos 16 /*!< SCB CPUID: ARCHITECTURE Position */ +#define SCB_CPUID_ARCHITECTURE_Msk (0xFUL << SCB_CPUID_ARCHITECTURE_Pos) /*!< SCB CPUID: ARCHITECTURE Mask */ + +#define SCB_CPUID_PARTNO_Pos 4 /*!< SCB CPUID: PARTNO Position */ +#define SCB_CPUID_PARTNO_Msk (0xFFFUL << SCB_CPUID_PARTNO_Pos) /*!< SCB CPUID: PARTNO Mask */ + +#define SCB_CPUID_REVISION_Pos 0 /*!< SCB CPUID: REVISION Position */ +#define SCB_CPUID_REVISION_Msk (0xFUL << SCB_CPUID_REVISION_Pos) /*!< SCB CPUID: REVISION Mask */ + +/* SCB Interrupt Control State Register Definitions */ +#define SCB_ICSR_NMIPENDSET_Pos 31 /*!< SCB ICSR: NMIPENDSET Position */ +#define SCB_ICSR_NMIPENDSET_Msk (1UL << SCB_ICSR_NMIPENDSET_Pos) /*!< SCB ICSR: NMIPENDSET Mask */ + +#define SCB_ICSR_PENDSVSET_Pos 28 /*!< SCB ICSR: PENDSVSET Position */ +#define SCB_ICSR_PENDSVSET_Msk (1UL << SCB_ICSR_PENDSVSET_Pos) /*!< SCB ICSR: PENDSVSET Mask */ + +#define SCB_ICSR_PENDSVCLR_Pos 27 /*!< SCB ICSR: PENDSVCLR Position */ +#define SCB_ICSR_PENDSVCLR_Msk (1UL << SCB_ICSR_PENDSVCLR_Pos) /*!< SCB ICSR: PENDSVCLR Mask */ + +#define SCB_ICSR_PENDSTSET_Pos 26 /*!< SCB ICSR: PENDSTSET Position */ +#define SCB_ICSR_PENDSTSET_Msk (1UL << SCB_ICSR_PENDSTSET_Pos) /*!< SCB ICSR: PENDSTSET Mask */ + +#define SCB_ICSR_PENDSTCLR_Pos 25 /*!< SCB ICSR: PENDSTCLR Position */ +#define SCB_ICSR_PENDSTCLR_Msk (1UL << SCB_ICSR_PENDSTCLR_Pos) /*!< SCB ICSR: PENDSTCLR Mask */ + +#define SCB_ICSR_ISRPREEMPT_Pos 23 /*!< SCB ICSR: ISRPREEMPT Position */ +#define SCB_ICSR_ISRPREEMPT_Msk (1UL << SCB_ICSR_ISRPREEMPT_Pos) /*!< SCB ICSR: ISRPREEMPT Mask */ + +#define SCB_ICSR_ISRPENDING_Pos 22 /*!< SCB ICSR: ISRPENDING Position */ +#define SCB_ICSR_ISRPENDING_Msk (1UL << SCB_ICSR_ISRPENDING_Pos) /*!< SCB ICSR: ISRPENDING Mask */ + +#define SCB_ICSR_VECTPENDING_Pos 12 /*!< SCB ICSR: VECTPENDING Position */ +#define SCB_ICSR_VECTPENDING_Msk (0x1FFUL << SCB_ICSR_VECTPENDING_Pos) /*!< SCB ICSR: VECTPENDING Mask */ + +#define SCB_ICSR_RETTOBASE_Pos 11 /*!< SCB ICSR: RETTOBASE Position */ +#define SCB_ICSR_RETTOBASE_Msk (1UL << SCB_ICSR_RETTOBASE_Pos) /*!< SCB ICSR: RETTOBASE Mask */ + +#define SCB_ICSR_VECTACTIVE_Pos 0 /*!< SCB ICSR: VECTACTIVE Position */ +#define SCB_ICSR_VECTACTIVE_Msk (0x1FFUL << SCB_ICSR_VECTACTIVE_Pos) /*!< SCB ICSR: VECTACTIVE Mask */ + +/* SCB Vector Table Offset Register Definitions */ +#if (__CM3_REV < 0x0201) /* core r2p1 */ +#define SCB_VTOR_TBLBASE_Pos 29 /*!< SCB VTOR: TBLBASE Position */ +#define SCB_VTOR_TBLBASE_Msk (1UL << SCB_VTOR_TBLBASE_Pos) /*!< SCB VTOR: TBLBASE Mask */ + +#define SCB_VTOR_TBLOFF_Pos 7 /*!< SCB VTOR: TBLOFF Position */ +#define SCB_VTOR_TBLOFF_Msk (0x3FFFFFUL << SCB_VTOR_TBLOFF_Pos) /*!< SCB VTOR: TBLOFF Mask */ +#else +#define SCB_VTOR_TBLOFF_Pos 7 /*!< SCB VTOR: TBLOFF Position */ +#define SCB_VTOR_TBLOFF_Msk (0x1FFFFFFUL << SCB_VTOR_TBLOFF_Pos) /*!< SCB VTOR: TBLOFF Mask */ +#endif + +/* SCB Application Interrupt and Reset Control Register Definitions */ +#define SCB_AIRCR_VECTKEY_Pos 16 /*!< SCB AIRCR: VECTKEY Position */ +#define SCB_AIRCR_VECTKEY_Msk (0xFFFFUL << SCB_AIRCR_VECTKEY_Pos) /*!< SCB AIRCR: VECTKEY Mask */ + +#define SCB_AIRCR_VECTKEYSTAT_Pos 16 /*!< SCB AIRCR: VECTKEYSTAT Position */ +#define SCB_AIRCR_VECTKEYSTAT_Msk (0xFFFFUL << SCB_AIRCR_VECTKEYSTAT_Pos) /*!< SCB AIRCR: VECTKEYSTAT Mask */ + +#define SCB_AIRCR_ENDIANESS_Pos 15 /*!< SCB AIRCR: ENDIANESS Position */ +#define SCB_AIRCR_ENDIANESS_Msk (1UL << SCB_AIRCR_ENDIANESS_Pos) /*!< SCB AIRCR: ENDIANESS Mask */ + +#define SCB_AIRCR_PRIGROUP_Pos 8 /*!< SCB AIRCR: PRIGROUP Position */ +#define SCB_AIRCR_PRIGROUP_Msk (7UL << SCB_AIRCR_PRIGROUP_Pos) /*!< SCB AIRCR: PRIGROUP Mask */ + +#define SCB_AIRCR_SYSRESETREQ_Pos 2 /*!< SCB AIRCR: SYSRESETREQ Position */ +#define SCB_AIRCR_SYSRESETREQ_Msk (1UL << SCB_AIRCR_SYSRESETREQ_Pos) /*!< SCB AIRCR: SYSRESETREQ Mask */ + +#define SCB_AIRCR_VECTCLRACTIVE_Pos 1 /*!< SCB AIRCR: VECTCLRACTIVE Position */ +#define SCB_AIRCR_VECTCLRACTIVE_Msk (1UL << SCB_AIRCR_VECTCLRACTIVE_Pos) /*!< SCB AIRCR: VECTCLRACTIVE Mask */ + +#define SCB_AIRCR_VECTRESET_Pos 0 /*!< SCB AIRCR: VECTRESET Position */ +#define SCB_AIRCR_VECTRESET_Msk (1UL << SCB_AIRCR_VECTRESET_Pos) /*!< SCB AIRCR: VECTRESET Mask */ + +/* SCB System Control Register Definitions */ +#define SCB_SCR_SEVONPEND_Pos 4 /*!< SCB SCR: SEVONPEND Position */ +#define SCB_SCR_SEVONPEND_Msk (1UL << SCB_SCR_SEVONPEND_Pos) /*!< SCB SCR: SEVONPEND Mask */ + +#define SCB_SCR_SLEEPDEEP_Pos 2 /*!< SCB SCR: SLEEPDEEP Position */ +#define SCB_SCR_SLEEPDEEP_Msk (1UL << SCB_SCR_SLEEPDEEP_Pos) /*!< SCB SCR: SLEEPDEEP Mask */ + +#define SCB_SCR_SLEEPONEXIT_Pos 1 /*!< SCB SCR: SLEEPONEXIT Position */ +#define SCB_SCR_SLEEPONEXIT_Msk (1UL << SCB_SCR_SLEEPONEXIT_Pos) /*!< SCB SCR: SLEEPONEXIT Mask */ + +/* SCB Configuration Control Register Definitions */ +#define SCB_CCR_STKALIGN_Pos 9 /*!< SCB CCR: STKALIGN Position */ +#define SCB_CCR_STKALIGN_Msk (1UL << SCB_CCR_STKALIGN_Pos) /*!< SCB CCR: STKALIGN Mask */ + +#define SCB_CCR_BFHFNMIGN_Pos 8 /*!< SCB CCR: BFHFNMIGN Position */ +#define SCB_CCR_BFHFNMIGN_Msk (1UL << SCB_CCR_BFHFNMIGN_Pos) /*!< SCB CCR: BFHFNMIGN Mask */ + +#define SCB_CCR_DIV_0_TRP_Pos 4 /*!< SCB CCR: DIV_0_TRP Position */ +#define SCB_CCR_DIV_0_TRP_Msk (1UL << SCB_CCR_DIV_0_TRP_Pos) /*!< SCB CCR: DIV_0_TRP Mask */ + +#define SCB_CCR_UNALIGN_TRP_Pos 3 /*!< SCB CCR: UNALIGN_TRP Position */ +#define SCB_CCR_UNALIGN_TRP_Msk (1UL << SCB_CCR_UNALIGN_TRP_Pos) /*!< SCB CCR: UNALIGN_TRP Mask */ + +#define SCB_CCR_USERSETMPEND_Pos 1 /*!< SCB CCR: USERSETMPEND Position */ +#define SCB_CCR_USERSETMPEND_Msk (1UL << SCB_CCR_USERSETMPEND_Pos) /*!< SCB CCR: USERSETMPEND Mask */ + +#define SCB_CCR_NONBASETHRDENA_Pos 0 /*!< SCB CCR: NONBASETHRDENA Position */ +#define SCB_CCR_NONBASETHRDENA_Msk (1UL << SCB_CCR_NONBASETHRDENA_Pos) /*!< SCB CCR: NONBASETHRDENA Mask */ + +/* SCB System Handler Control and State Register Definitions */ +#define SCB_SHCSR_USGFAULTENA_Pos 18 /*!< SCB SHCSR: USGFAULTENA Position */ +#define SCB_SHCSR_USGFAULTENA_Msk (1UL << SCB_SHCSR_USGFAULTENA_Pos) /*!< SCB SHCSR: USGFAULTENA Mask */ + +#define SCB_SHCSR_BUSFAULTENA_Pos 17 /*!< SCB SHCSR: BUSFAULTENA Position */ +#define SCB_SHCSR_BUSFAULTENA_Msk (1UL << SCB_SHCSR_BUSFAULTENA_Pos) /*!< SCB SHCSR: BUSFAULTENA Mask */ + +#define SCB_SHCSR_MEMFAULTENA_Pos 16 /*!< SCB SHCSR: MEMFAULTENA Position */ +#define SCB_SHCSR_MEMFAULTENA_Msk (1UL << SCB_SHCSR_MEMFAULTENA_Pos) /*!< SCB SHCSR: MEMFAULTENA Mask */ + +#define SCB_SHCSR_SVCALLPENDED_Pos 15 /*!< SCB SHCSR: SVCALLPENDED Position */ +#define SCB_SHCSR_SVCALLPENDED_Msk (1UL << SCB_SHCSR_SVCALLPENDED_Pos) /*!< SCB SHCSR: SVCALLPENDED Mask */ + +#define SCB_SHCSR_BUSFAULTPENDED_Pos 14 /*!< SCB SHCSR: BUSFAULTPENDED Position */ +#define SCB_SHCSR_BUSFAULTPENDED_Msk (1UL << SCB_SHCSR_BUSFAULTPENDED_Pos) /*!< SCB SHCSR: BUSFAULTPENDED Mask */ + +#define SCB_SHCSR_MEMFAULTPENDED_Pos 13 /*!< SCB SHCSR: MEMFAULTPENDED Position */ +#define SCB_SHCSR_MEMFAULTPENDED_Msk (1UL << SCB_SHCSR_MEMFAULTPENDED_Pos) /*!< SCB SHCSR: MEMFAULTPENDED Mask */ + +#define SCB_SHCSR_USGFAULTPENDED_Pos 12 /*!< SCB SHCSR: USGFAULTPENDED Position */ +#define SCB_SHCSR_USGFAULTPENDED_Msk (1UL << SCB_SHCSR_USGFAULTPENDED_Pos) /*!< SCB SHCSR: USGFAULTPENDED Mask */ + +#define SCB_SHCSR_SYSTICKACT_Pos 11 /*!< SCB SHCSR: SYSTICKACT Position */ +#define SCB_SHCSR_SYSTICKACT_Msk (1UL << SCB_SHCSR_SYSTICKACT_Pos) /*!< SCB SHCSR: SYSTICKACT Mask */ + +#define SCB_SHCSR_PENDSVACT_Pos 10 /*!< SCB SHCSR: PENDSVACT Position */ +#define SCB_SHCSR_PENDSVACT_Msk (1UL << SCB_SHCSR_PENDSVACT_Pos) /*!< SCB SHCSR: PENDSVACT Mask */ + +#define SCB_SHCSR_MONITORACT_Pos 8 /*!< SCB SHCSR: MONITORACT Position */ +#define SCB_SHCSR_MONITORACT_Msk (1UL << SCB_SHCSR_MONITORACT_Pos) /*!< SCB SHCSR: MONITORACT Mask */ + +#define SCB_SHCSR_SVCALLACT_Pos 7 /*!< SCB SHCSR: SVCALLACT Position */ +#define SCB_SHCSR_SVCALLACT_Msk (1UL << SCB_SHCSR_SVCALLACT_Pos) /*!< SCB SHCSR: SVCALLACT Mask */ + +#define SCB_SHCSR_USGFAULTACT_Pos 3 /*!< SCB SHCSR: USGFAULTACT Position */ +#define SCB_SHCSR_USGFAULTACT_Msk (1UL << SCB_SHCSR_USGFAULTACT_Pos) /*!< SCB SHCSR: USGFAULTACT Mask */ + +#define SCB_SHCSR_BUSFAULTACT_Pos 1 /*!< SCB SHCSR: BUSFAULTACT Position */ +#define SCB_SHCSR_BUSFAULTACT_Msk (1UL << SCB_SHCSR_BUSFAULTACT_Pos) /*!< SCB SHCSR: BUSFAULTACT Mask */ + +#define SCB_SHCSR_MEMFAULTACT_Pos 0 /*!< SCB SHCSR: MEMFAULTACT Position */ +#define SCB_SHCSR_MEMFAULTACT_Msk (1UL << SCB_SHCSR_MEMFAULTACT_Pos) /*!< SCB SHCSR: MEMFAULTACT Mask */ + +/* SCB Configurable Fault Status Registers Definitions */ +#define SCB_CFSR_USGFAULTSR_Pos 16 /*!< SCB CFSR: Usage Fault Status Register Position */ +#define SCB_CFSR_USGFAULTSR_Msk (0xFFFFUL << SCB_CFSR_USGFAULTSR_Pos) /*!< SCB CFSR: Usage Fault Status Register Mask */ + +#define SCB_CFSR_BUSFAULTSR_Pos 8 /*!< SCB CFSR: Bus Fault Status Register Position */ +#define SCB_CFSR_BUSFAULTSR_Msk (0xFFUL << SCB_CFSR_BUSFAULTSR_Pos) /*!< SCB CFSR: Bus Fault Status Register Mask */ + +#define SCB_CFSR_MEMFAULTSR_Pos 0 /*!< SCB CFSR: Memory Manage Fault Status Register Position */ +#define SCB_CFSR_MEMFAULTSR_Msk (0xFFUL << SCB_CFSR_MEMFAULTSR_Pos) /*!< SCB CFSR: Memory Manage Fault Status Register Mask */ + +/* SCB Hard Fault Status Registers Definitions */ +#define SCB_HFSR_DEBUGEVT_Pos 31 /*!< SCB HFSR: DEBUGEVT Position */ +#define SCB_HFSR_DEBUGEVT_Msk (1UL << SCB_HFSR_DEBUGEVT_Pos) /*!< SCB HFSR: DEBUGEVT Mask */ + +#define SCB_HFSR_FORCED_Pos 30 /*!< SCB HFSR: FORCED Position */ +#define SCB_HFSR_FORCED_Msk (1UL << SCB_HFSR_FORCED_Pos) /*!< SCB HFSR: FORCED Mask */ + +#define SCB_HFSR_VECTTBL_Pos 1 /*!< SCB HFSR: VECTTBL Position */ +#define SCB_HFSR_VECTTBL_Msk (1UL << SCB_HFSR_VECTTBL_Pos) /*!< SCB HFSR: VECTTBL Mask */ + +/* SCB Debug Fault Status Register Definitions */ +#define SCB_DFSR_EXTERNAL_Pos 4 /*!< SCB DFSR: EXTERNAL Position */ +#define SCB_DFSR_EXTERNAL_Msk (1UL << SCB_DFSR_EXTERNAL_Pos) /*!< SCB DFSR: EXTERNAL Mask */ + +#define SCB_DFSR_VCATCH_Pos 3 /*!< SCB DFSR: VCATCH Position */ +#define SCB_DFSR_VCATCH_Msk (1UL << SCB_DFSR_VCATCH_Pos) /*!< SCB DFSR: VCATCH Mask */ + +#define SCB_DFSR_DWTTRAP_Pos 2 /*!< SCB DFSR: DWTTRAP Position */ +#define SCB_DFSR_DWTTRAP_Msk (1UL << SCB_DFSR_DWTTRAP_Pos) /*!< SCB DFSR: DWTTRAP Mask */ + +#define SCB_DFSR_BKPT_Pos 1 /*!< SCB DFSR: BKPT Position */ +#define SCB_DFSR_BKPT_Msk (1UL << SCB_DFSR_BKPT_Pos) /*!< SCB DFSR: BKPT Mask */ + +#define SCB_DFSR_HALTED_Pos 0 /*!< SCB DFSR: HALTED Position */ +#define SCB_DFSR_HALTED_Msk (1UL << SCB_DFSR_HALTED_Pos) /*!< SCB DFSR: HALTED Mask */ + +/*@} end of group CMSIS_SCB */ + + +/** \ingroup CMSIS_core_register + \defgroup CMSIS_SCnSCB System Controls not in SCB (SCnSCB) + \brief Type definitions for the System Control and ID Register not in the SCB + @{ + */ + +/** \brief Structure type to access the System Control and ID Register not in the SCB. + */ +typedef struct +{ + uint32_t RESERVED0[1]; + __I uint32_t ICTR; /*!< Offset: 0x004 (R/ ) Interrupt Controller Type Register */ +#if ((defined __CM3_REV) && (__CM3_REV >= 0x200)) + __IO uint32_t ACTLR; /*!< Offset: 0x008 (R/W) Auxiliary Control Register */ +#else + uint32_t RESERVED1[1]; +#endif +} SCnSCB_Type; + +/* Interrupt Controller Type Register Definitions */ +#define SCnSCB_ICTR_INTLINESNUM_Pos 0 /*!< ICTR: INTLINESNUM Position */ +#define SCnSCB_ICTR_INTLINESNUM_Msk (0xFUL << SCnSCB_ICTR_INTLINESNUM_Pos) /*!< ICTR: INTLINESNUM Mask */ + +/* Auxiliary Control Register Definitions */ + +#define SCnSCB_ACTLR_DISFOLD_Pos 2 /*!< ACTLR: DISFOLD Position */ +#define SCnSCB_ACTLR_DISFOLD_Msk (1UL << SCnSCB_ACTLR_DISFOLD_Pos) /*!< ACTLR: DISFOLD Mask */ + +#define SCnSCB_ACTLR_DISDEFWBUF_Pos 1 /*!< ACTLR: DISDEFWBUF Position */ +#define SCnSCB_ACTLR_DISDEFWBUF_Msk (1UL << SCnSCB_ACTLR_DISDEFWBUF_Pos) /*!< ACTLR: DISDEFWBUF Mask */ + +#define SCnSCB_ACTLR_DISMCYCINT_Pos 0 /*!< ACTLR: DISMCYCINT Position */ +#define SCnSCB_ACTLR_DISMCYCINT_Msk (1UL << SCnSCB_ACTLR_DISMCYCINT_Pos) /*!< ACTLR: DISMCYCINT Mask */ + +/*@} end of group CMSIS_SCnotSCB */ + + +/** \ingroup CMSIS_core_register + \defgroup CMSIS_SysTick System Tick Timer (SysTick) + \brief Type definitions for the System Timer Registers. + @{ + */ + +/** \brief Structure type to access the System Timer (SysTick). + */ +typedef struct +{ + __IO uint32_t CTRL; /*!< Offset: 0x000 (R/W) SysTick Control and Status Register */ + __IO uint32_t LOAD; /*!< Offset: 0x004 (R/W) SysTick Reload Value Register */ + __IO uint32_t VAL; /*!< Offset: 0x008 (R/W) SysTick Current Value Register */ + __I uint32_t CALIB; /*!< Offset: 0x00C (R/ ) SysTick Calibration Register */ +} SysTick_Type; + +/* SysTick Control / Status Register Definitions */ +#define SysTick_CTRL_COUNTFLAG_Pos 16 /*!< SysTick CTRL: COUNTFLAG Position */ +#define SysTick_CTRL_COUNTFLAG_Msk (1UL << SysTick_CTRL_COUNTFLAG_Pos) /*!< SysTick CTRL: COUNTFLAG Mask */ + +#define SysTick_CTRL_CLKSOURCE_Pos 2 /*!< SysTick CTRL: CLKSOURCE Position */ +#define SysTick_CTRL_CLKSOURCE_Msk (1UL << SysTick_CTRL_CLKSOURCE_Pos) /*!< SysTick CTRL: CLKSOURCE Mask */ + +#define SysTick_CTRL_TICKINT_Pos 1 /*!< SysTick CTRL: TICKINT Position */ +#define SysTick_CTRL_TICKINT_Msk (1UL << SysTick_CTRL_TICKINT_Pos) /*!< SysTick CTRL: TICKINT Mask */ + +#define SysTick_CTRL_ENABLE_Pos 0 /*!< SysTick CTRL: ENABLE Position */ +#define SysTick_CTRL_ENABLE_Msk (1UL << SysTick_CTRL_ENABLE_Pos) /*!< SysTick CTRL: ENABLE Mask */ + +/* SysTick Reload Register Definitions */ +#define SysTick_LOAD_RELOAD_Pos 0 /*!< SysTick LOAD: RELOAD Position */ +#define SysTick_LOAD_RELOAD_Msk (0xFFFFFFUL << SysTick_LOAD_RELOAD_Pos) /*!< SysTick LOAD: RELOAD Mask */ + +/* SysTick Current Register Definitions */ +#define SysTick_VAL_CURRENT_Pos 0 /*!< SysTick VAL: CURRENT Position */ +#define SysTick_VAL_CURRENT_Msk (0xFFFFFFUL << SysTick_VAL_CURRENT_Pos) /*!< SysTick VAL: CURRENT Mask */ + +/* SysTick Calibration Register Definitions */ +#define SysTick_CALIB_NOREF_Pos 31 /*!< SysTick CALIB: NOREF Position */ +#define SysTick_CALIB_NOREF_Msk (1UL << SysTick_CALIB_NOREF_Pos) /*!< SysTick CALIB: NOREF Mask */ + +#define SysTick_CALIB_SKEW_Pos 30 /*!< SysTick CALIB: SKEW Position */ +#define SysTick_CALIB_SKEW_Msk (1UL << SysTick_CALIB_SKEW_Pos) /*!< SysTick CALIB: SKEW Mask */ + +#define SysTick_CALIB_TENMS_Pos 0 /*!< SysTick CALIB: TENMS Position */ +#define SysTick_CALIB_TENMS_Msk (0xFFFFFFUL << SysTick_CALIB_TENMS_Pos) /*!< SysTick CALIB: TENMS Mask */ + +/*@} end of group CMSIS_SysTick */ + + +/** \ingroup CMSIS_core_register + \defgroup CMSIS_ITM Instrumentation Trace Macrocell (ITM) + \brief Type definitions for the Instrumentation Trace Macrocell (ITM) + @{ + */ + +/** \brief Structure type to access the Instrumentation Trace Macrocell Register (ITM). + */ +typedef struct +{ + __O union + { + __O uint8_t u8; /*!< Offset: 0x000 ( /W) ITM Stimulus Port 8-bit */ + __O uint16_t u16; /*!< Offset: 0x000 ( /W) ITM Stimulus Port 16-bit */ + __O uint32_t u32; /*!< Offset: 0x000 ( /W) ITM Stimulus Port 32-bit */ + } PORT [32]; /*!< Offset: 0x000 ( /W) ITM Stimulus Port Registers */ + uint32_t RESERVED0[864]; + __IO uint32_t TER; /*!< Offset: 0xE00 (R/W) ITM Trace Enable Register */ + uint32_t RESERVED1[15]; + __IO uint32_t TPR; /*!< Offset: 0xE40 (R/W) ITM Trace Privilege Register */ + uint32_t RESERVED2[15]; + __IO uint32_t TCR; /*!< Offset: 0xE80 (R/W) ITM Trace Control Register */ + uint32_t RESERVED3[29]; + __O uint32_t IWR; /*!< Offset: 0xEF8 ( /W) ITM Integration Write Register */ + __I uint32_t IRR; /*!< Offset: 0xEFC (R/ ) ITM Integration Read Register */ + __IO uint32_t IMCR; /*!< Offset: 0xF00 (R/W) ITM Integration Mode Control Register */ + uint32_t RESERVED4[43]; + __O uint32_t LAR; /*!< Offset: 0xFB0 ( /W) ITM Lock Access Register */ + __I uint32_t LSR; /*!< Offset: 0xFB4 (R/ ) ITM Lock Status Register */ + uint32_t RESERVED5[6]; + __I uint32_t PID4; /*!< Offset: 0xFD0 (R/ ) ITM Peripheral Identification Register #4 */ + __I uint32_t PID5; /*!< Offset: 0xFD4 (R/ ) ITM Peripheral Identification Register #5 */ + __I uint32_t PID6; /*!< Offset: 0xFD8 (R/ ) ITM Peripheral Identification Register #6 */ + __I uint32_t PID7; /*!< Offset: 0xFDC (R/ ) ITM Peripheral Identification Register #7 */ + __I uint32_t PID0; /*!< Offset: 0xFE0 (R/ ) ITM Peripheral Identification Register #0 */ + __I uint32_t PID1; /*!< Offset: 0xFE4 (R/ ) ITM Peripheral Identification Register #1 */ + __I uint32_t PID2; /*!< Offset: 0xFE8 (R/ ) ITM Peripheral Identification Register #2 */ + __I uint32_t PID3; /*!< Offset: 0xFEC (R/ ) ITM Peripheral Identification Register #3 */ + __I uint32_t CID0; /*!< Offset: 0xFF0 (R/ ) ITM Component Identification Register #0 */ + __I uint32_t CID1; /*!< Offset: 0xFF4 (R/ ) ITM Component Identification Register #1 */ + __I uint32_t CID2; /*!< Offset: 0xFF8 (R/ ) ITM Component Identification Register #2 */ + __I uint32_t CID3; /*!< Offset: 0xFFC (R/ ) ITM Component Identification Register #3 */ +} ITM_Type; + +/* ITM Trace Privilege Register Definitions */ +#define ITM_TPR_PRIVMASK_Pos 0 /*!< ITM TPR: PRIVMASK Position */ +#define ITM_TPR_PRIVMASK_Msk (0xFUL << ITM_TPR_PRIVMASK_Pos) /*!< ITM TPR: PRIVMASK Mask */ + +/* ITM Trace Control Register Definitions */ +#define ITM_TCR_BUSY_Pos 23 /*!< ITM TCR: BUSY Position */ +#define ITM_TCR_BUSY_Msk (1UL << ITM_TCR_BUSY_Pos) /*!< ITM TCR: BUSY Mask */ + +#define ITM_TCR_TraceBusID_Pos 16 /*!< ITM TCR: ATBID Position */ +#define ITM_TCR_TraceBusID_Msk (0x7FUL << ITM_TCR_TraceBusID_Pos) /*!< ITM TCR: ATBID Mask */ + +#define ITM_TCR_GTSFREQ_Pos 10 /*!< ITM TCR: Global timestamp frequency Position */ +#define ITM_TCR_GTSFREQ_Msk (3UL << ITM_TCR_GTSFREQ_Pos) /*!< ITM TCR: Global timestamp frequency Mask */ + +#define ITM_TCR_TSPrescale_Pos 8 /*!< ITM TCR: TSPrescale Position */ +#define ITM_TCR_TSPrescale_Msk (3UL << ITM_TCR_TSPrescale_Pos) /*!< ITM TCR: TSPrescale Mask */ + +#define ITM_TCR_SWOENA_Pos 4 /*!< ITM TCR: SWOENA Position */ +#define ITM_TCR_SWOENA_Msk (1UL << ITM_TCR_SWOENA_Pos) /*!< ITM TCR: SWOENA Mask */ + +#define ITM_TCR_DWTENA_Pos 3 /*!< ITM TCR: DWTENA Position */ +#define ITM_TCR_DWTENA_Msk (1UL << ITM_TCR_DWTENA_Pos) /*!< ITM TCR: DWTENA Mask */ + +#define ITM_TCR_SYNCENA_Pos 2 /*!< ITM TCR: SYNCENA Position */ +#define ITM_TCR_SYNCENA_Msk (1UL << ITM_TCR_SYNCENA_Pos) /*!< ITM TCR: SYNCENA Mask */ + +#define ITM_TCR_TSENA_Pos 1 /*!< ITM TCR: TSENA Position */ +#define ITM_TCR_TSENA_Msk (1UL << ITM_TCR_TSENA_Pos) /*!< ITM TCR: TSENA Mask */ + +#define ITM_TCR_ITMENA_Pos 0 /*!< ITM TCR: ITM Enable bit Position */ +#define ITM_TCR_ITMENA_Msk (1UL << ITM_TCR_ITMENA_Pos) /*!< ITM TCR: ITM Enable bit Mask */ + +/* ITM Integration Write Register Definitions */ +#define ITM_IWR_ATVALIDM_Pos 0 /*!< ITM IWR: ATVALIDM Position */ +#define ITM_IWR_ATVALIDM_Msk (1UL << ITM_IWR_ATVALIDM_Pos) /*!< ITM IWR: ATVALIDM Mask */ + +/* ITM Integration Read Register Definitions */ +#define ITM_IRR_ATREADYM_Pos 0 /*!< ITM IRR: ATREADYM Position */ +#define ITM_IRR_ATREADYM_Msk (1UL << ITM_IRR_ATREADYM_Pos) /*!< ITM IRR: ATREADYM Mask */ + +/* ITM Integration Mode Control Register Definitions */ +#define ITM_IMCR_INTEGRATION_Pos 0 /*!< ITM IMCR: INTEGRATION Position */ +#define ITM_IMCR_INTEGRATION_Msk (1UL << ITM_IMCR_INTEGRATION_Pos) /*!< ITM IMCR: INTEGRATION Mask */ + +/* ITM Lock Status Register Definitions */ +#define ITM_LSR_ByteAcc_Pos 2 /*!< ITM LSR: ByteAcc Position */ +#define ITM_LSR_ByteAcc_Msk (1UL << ITM_LSR_ByteAcc_Pos) /*!< ITM LSR: ByteAcc Mask */ + +#define ITM_LSR_Access_Pos 1 /*!< ITM LSR: Access Position */ +#define ITM_LSR_Access_Msk (1UL << ITM_LSR_Access_Pos) /*!< ITM LSR: Access Mask */ + +#define ITM_LSR_Present_Pos 0 /*!< ITM LSR: Present Position */ +#define ITM_LSR_Present_Msk (1UL << ITM_LSR_Present_Pos) /*!< ITM LSR: Present Mask */ + +/*@}*/ /* end of group CMSIS_ITM */ + + +/** \ingroup CMSIS_core_register + \defgroup CMSIS_DWT Data Watchpoint and Trace (DWT) + \brief Type definitions for the Data Watchpoint and Trace (DWT) + @{ + */ + +/** \brief Structure type to access the Data Watchpoint and Trace Register (DWT). + */ +typedef struct +{ + __IO uint32_t CTRL; /*!< Offset: 0x000 (R/W) Control Register */ + __IO uint32_t CYCCNT; /*!< Offset: 0x004 (R/W) Cycle Count Register */ + __IO uint32_t CPICNT; /*!< Offset: 0x008 (R/W) CPI Count Register */ + __IO uint32_t EXCCNT; /*!< Offset: 0x00C (R/W) Exception Overhead Count Register */ + __IO uint32_t SLEEPCNT; /*!< Offset: 0x010 (R/W) Sleep Count Register */ + __IO uint32_t LSUCNT; /*!< Offset: 0x014 (R/W) LSU Count Register */ + __IO uint32_t FOLDCNT; /*!< Offset: 0x018 (R/W) Folded-instruction Count Register */ + __I uint32_t PCSR; /*!< Offset: 0x01C (R/ ) Program Counter Sample Register */ + __IO uint32_t COMP0; /*!< Offset: 0x020 (R/W) Comparator Register 0 */ + __IO uint32_t MASK0; /*!< Offset: 0x024 (R/W) Mask Register 0 */ + __IO uint32_t FUNCTION0; /*!< Offset: 0x028 (R/W) Function Register 0 */ + uint32_t RESERVED0[1]; + __IO uint32_t COMP1; /*!< Offset: 0x030 (R/W) Comparator Register 1 */ + __IO uint32_t MASK1; /*!< Offset: 0x034 (R/W) Mask Register 1 */ + __IO uint32_t FUNCTION1; /*!< Offset: 0x038 (R/W) Function Register 1 */ + uint32_t RESERVED1[1]; + __IO uint32_t COMP2; /*!< Offset: 0x040 (R/W) Comparator Register 2 */ + __IO uint32_t MASK2; /*!< Offset: 0x044 (R/W) Mask Register 2 */ + __IO uint32_t FUNCTION2; /*!< Offset: 0x048 (R/W) Function Register 2 */ + uint32_t RESERVED2[1]; + __IO uint32_t COMP3; /*!< Offset: 0x050 (R/W) Comparator Register 3 */ + __IO uint32_t MASK3; /*!< Offset: 0x054 (R/W) Mask Register 3 */ + __IO uint32_t FUNCTION3; /*!< Offset: 0x058 (R/W) Function Register 3 */ +} DWT_Type; + +/* DWT Control Register Definitions */ +#define DWT_CTRL_NUMCOMP_Pos 28 /*!< DWT CTRL: NUMCOMP Position */ +#define DWT_CTRL_NUMCOMP_Msk (0xFUL << DWT_CTRL_NUMCOMP_Pos) /*!< DWT CTRL: NUMCOMP Mask */ + +#define DWT_CTRL_NOTRCPKT_Pos 27 /*!< DWT CTRL: NOTRCPKT Position */ +#define DWT_CTRL_NOTRCPKT_Msk (0x1UL << DWT_CTRL_NOTRCPKT_Pos) /*!< DWT CTRL: NOTRCPKT Mask */ + +#define DWT_CTRL_NOEXTTRIG_Pos 26 /*!< DWT CTRL: NOEXTTRIG Position */ +#define DWT_CTRL_NOEXTTRIG_Msk (0x1UL << DWT_CTRL_NOEXTTRIG_Pos) /*!< DWT CTRL: NOEXTTRIG Mask */ + +#define DWT_CTRL_NOCYCCNT_Pos 25 /*!< DWT CTRL: NOCYCCNT Position */ +#define DWT_CTRL_NOCYCCNT_Msk (0x1UL << DWT_CTRL_NOCYCCNT_Pos) /*!< DWT CTRL: NOCYCCNT Mask */ + +#define DWT_CTRL_NOPRFCNT_Pos 24 /*!< DWT CTRL: NOPRFCNT Position */ +#define DWT_CTRL_NOPRFCNT_Msk (0x1UL << DWT_CTRL_NOPRFCNT_Pos) /*!< DWT CTRL: NOPRFCNT Mask */ + +#define DWT_CTRL_CYCEVTENA_Pos 22 /*!< DWT CTRL: CYCEVTENA Position */ +#define DWT_CTRL_CYCEVTENA_Msk (0x1UL << DWT_CTRL_CYCEVTENA_Pos) /*!< DWT CTRL: CYCEVTENA Mask */ + +#define DWT_CTRL_FOLDEVTENA_Pos 21 /*!< DWT CTRL: FOLDEVTENA Position */ +#define DWT_CTRL_FOLDEVTENA_Msk (0x1UL << DWT_CTRL_FOLDEVTENA_Pos) /*!< DWT CTRL: FOLDEVTENA Mask */ + +#define DWT_CTRL_LSUEVTENA_Pos 20 /*!< DWT CTRL: LSUEVTENA Position */ +#define DWT_CTRL_LSUEVTENA_Msk (0x1UL << DWT_CTRL_LSUEVTENA_Pos) /*!< DWT CTRL: LSUEVTENA Mask */ + +#define DWT_CTRL_SLEEPEVTENA_Pos 19 /*!< DWT CTRL: SLEEPEVTENA Position */ +#define DWT_CTRL_SLEEPEVTENA_Msk (0x1UL << DWT_CTRL_SLEEPEVTENA_Pos) /*!< DWT CTRL: SLEEPEVTENA Mask */ + +#define DWT_CTRL_EXCEVTENA_Pos 18 /*!< DWT CTRL: EXCEVTENA Position */ +#define DWT_CTRL_EXCEVTENA_Msk (0x1UL << DWT_CTRL_EXCEVTENA_Pos) /*!< DWT CTRL: EXCEVTENA Mask */ + +#define DWT_CTRL_CPIEVTENA_Pos 17 /*!< DWT CTRL: CPIEVTENA Position */ +#define DWT_CTRL_CPIEVTENA_Msk (0x1UL << DWT_CTRL_CPIEVTENA_Pos) /*!< DWT CTRL: CPIEVTENA Mask */ + +#define DWT_CTRL_EXCTRCENA_Pos 16 /*!< DWT CTRL: EXCTRCENA Position */ +#define DWT_CTRL_EXCTRCENA_Msk (0x1UL << DWT_CTRL_EXCTRCENA_Pos) /*!< DWT CTRL: EXCTRCENA Mask */ + +#define DWT_CTRL_PCSAMPLENA_Pos 12 /*!< DWT CTRL: PCSAMPLENA Position */ +#define DWT_CTRL_PCSAMPLENA_Msk (0x1UL << DWT_CTRL_PCSAMPLENA_Pos) /*!< DWT CTRL: PCSAMPLENA Mask */ + +#define DWT_CTRL_SYNCTAP_Pos 10 /*!< DWT CTRL: SYNCTAP Position */ +#define DWT_CTRL_SYNCTAP_Msk (0x3UL << DWT_CTRL_SYNCTAP_Pos) /*!< DWT CTRL: SYNCTAP Mask */ + +#define DWT_CTRL_CYCTAP_Pos 9 /*!< DWT CTRL: CYCTAP Position */ +#define DWT_CTRL_CYCTAP_Msk (0x1UL << DWT_CTRL_CYCTAP_Pos) /*!< DWT CTRL: CYCTAP Mask */ + +#define DWT_CTRL_POSTINIT_Pos 5 /*!< DWT CTRL: POSTINIT Position */ +#define DWT_CTRL_POSTINIT_Msk (0xFUL << DWT_CTRL_POSTINIT_Pos) /*!< DWT CTRL: POSTINIT Mask */ + +#define DWT_CTRL_POSTPRESET_Pos 1 /*!< DWT CTRL: POSTPRESET Position */ +#define DWT_CTRL_POSTPRESET_Msk (0xFUL << DWT_CTRL_POSTPRESET_Pos) /*!< DWT CTRL: POSTPRESET Mask */ + +#define DWT_CTRL_CYCCNTENA_Pos 0 /*!< DWT CTRL: CYCCNTENA Position */ +#define DWT_CTRL_CYCCNTENA_Msk (0x1UL << DWT_CTRL_CYCCNTENA_Pos) /*!< DWT CTRL: CYCCNTENA Mask */ + +/* DWT CPI Count Register Definitions */ +#define DWT_CPICNT_CPICNT_Pos 0 /*!< DWT CPICNT: CPICNT Position */ +#define DWT_CPICNT_CPICNT_Msk (0xFFUL << DWT_CPICNT_CPICNT_Pos) /*!< DWT CPICNT: CPICNT Mask */ + +/* DWT Exception Overhead Count Register Definitions */ +#define DWT_EXCCNT_EXCCNT_Pos 0 /*!< DWT EXCCNT: EXCCNT Position */ +#define DWT_EXCCNT_EXCCNT_Msk (0xFFUL << DWT_EXCCNT_EXCCNT_Pos) /*!< DWT EXCCNT: EXCCNT Mask */ + +/* DWT Sleep Count Register Definitions */ +#define DWT_SLEEPCNT_SLEEPCNT_Pos 0 /*!< DWT SLEEPCNT: SLEEPCNT Position */ +#define DWT_SLEEPCNT_SLEEPCNT_Msk (0xFFUL << DWT_SLEEPCNT_SLEEPCNT_Pos) /*!< DWT SLEEPCNT: SLEEPCNT Mask */ + +/* DWT LSU Count Register Definitions */ +#define DWT_LSUCNT_LSUCNT_Pos 0 /*!< DWT LSUCNT: LSUCNT Position */ +#define DWT_LSUCNT_LSUCNT_Msk (0xFFUL << DWT_LSUCNT_LSUCNT_Pos) /*!< DWT LSUCNT: LSUCNT Mask */ + +/* DWT Folded-instruction Count Register Definitions */ +#define DWT_FOLDCNT_FOLDCNT_Pos 0 /*!< DWT FOLDCNT: FOLDCNT Position */ +#define DWT_FOLDCNT_FOLDCNT_Msk (0xFFUL << DWT_FOLDCNT_FOLDCNT_Pos) /*!< DWT FOLDCNT: FOLDCNT Mask */ + +/* DWT Comparator Mask Register Definitions */ +#define DWT_MASK_MASK_Pos 0 /*!< DWT MASK: MASK Position */ +#define DWT_MASK_MASK_Msk (0x1FUL << DWT_MASK_MASK_Pos) /*!< DWT MASK: MASK Mask */ + +/* DWT Comparator Function Register Definitions */ +#define DWT_FUNCTION_MATCHED_Pos 24 /*!< DWT FUNCTION: MATCHED Position */ +#define DWT_FUNCTION_MATCHED_Msk (0x1UL << DWT_FUNCTION_MATCHED_Pos) /*!< DWT FUNCTION: MATCHED Mask */ + +#define DWT_FUNCTION_DATAVADDR1_Pos 16 /*!< DWT FUNCTION: DATAVADDR1 Position */ +#define DWT_FUNCTION_DATAVADDR1_Msk (0xFUL << DWT_FUNCTION_DATAVADDR1_Pos) /*!< DWT FUNCTION: DATAVADDR1 Mask */ + +#define DWT_FUNCTION_DATAVADDR0_Pos 12 /*!< DWT FUNCTION: DATAVADDR0 Position */ +#define DWT_FUNCTION_DATAVADDR0_Msk (0xFUL << DWT_FUNCTION_DATAVADDR0_Pos) /*!< DWT FUNCTION: DATAVADDR0 Mask */ + +#define DWT_FUNCTION_DATAVSIZE_Pos 10 /*!< DWT FUNCTION: DATAVSIZE Position */ +#define DWT_FUNCTION_DATAVSIZE_Msk (0x3UL << DWT_FUNCTION_DATAVSIZE_Pos) /*!< DWT FUNCTION: DATAVSIZE Mask */ + +#define DWT_FUNCTION_LNK1ENA_Pos 9 /*!< DWT FUNCTION: LNK1ENA Position */ +#define DWT_FUNCTION_LNK1ENA_Msk (0x1UL << DWT_FUNCTION_LNK1ENA_Pos) /*!< DWT FUNCTION: LNK1ENA Mask */ + +#define DWT_FUNCTION_DATAVMATCH_Pos 8 /*!< DWT FUNCTION: DATAVMATCH Position */ +#define DWT_FUNCTION_DATAVMATCH_Msk (0x1UL << DWT_FUNCTION_DATAVMATCH_Pos) /*!< DWT FUNCTION: DATAVMATCH Mask */ + +#define DWT_FUNCTION_CYCMATCH_Pos 7 /*!< DWT FUNCTION: CYCMATCH Position */ +#define DWT_FUNCTION_CYCMATCH_Msk (0x1UL << DWT_FUNCTION_CYCMATCH_Pos) /*!< DWT FUNCTION: CYCMATCH Mask */ + +#define DWT_FUNCTION_EMITRANGE_Pos 5 /*!< DWT FUNCTION: EMITRANGE Position */ +#define DWT_FUNCTION_EMITRANGE_Msk (0x1UL << DWT_FUNCTION_EMITRANGE_Pos) /*!< DWT FUNCTION: EMITRANGE Mask */ + +#define DWT_FUNCTION_FUNCTION_Pos 0 /*!< DWT FUNCTION: FUNCTION Position */ +#define DWT_FUNCTION_FUNCTION_Msk (0xFUL << DWT_FUNCTION_FUNCTION_Pos) /*!< DWT FUNCTION: FUNCTION Mask */ + +/*@}*/ /* end of group CMSIS_DWT */ + + +/** \ingroup CMSIS_core_register + \defgroup CMSIS_TPI Trace Port Interface (TPI) + \brief Type definitions for the Trace Port Interface (TPI) + @{ + */ + +/** \brief Structure type to access the Trace Port Interface Register (TPI). + */ +typedef struct +{ + __IO uint32_t SSPSR; /*!< Offset: 0x000 (R/ ) Supported Parallel Port Size Register */ + __IO uint32_t CSPSR; /*!< Offset: 0x004 (R/W) Current Parallel Port Size Register */ + uint32_t RESERVED0[2]; + __IO uint32_t ACPR; /*!< Offset: 0x010 (R/W) Asynchronous Clock Prescaler Register */ + uint32_t RESERVED1[55]; + __IO uint32_t SPPR; /*!< Offset: 0x0F0 (R/W) Selected Pin Protocol Register */ + uint32_t RESERVED2[131]; + __I uint32_t FFSR; /*!< Offset: 0x300 (R/ ) Formatter and Flush Status Register */ + __IO uint32_t FFCR; /*!< Offset: 0x304 (R/W) Formatter and Flush Control Register */ + __I uint32_t FSCR; /*!< Offset: 0x308 (R/ ) Formatter Synchronization Counter Register */ + uint32_t RESERVED3[759]; + __I uint32_t TRIGGER; /*!< Offset: 0xEE8 (R/ ) TRIGGER */ + __I uint32_t FIFO0; /*!< Offset: 0xEEC (R/ ) Integration ETM Data */ + __I uint32_t ITATBCTR2; /*!< Offset: 0xEF0 (R/ ) ITATBCTR2 */ + uint32_t RESERVED4[1]; + __I uint32_t ITATBCTR0; /*!< Offset: 0xEF8 (R/ ) ITATBCTR0 */ + __I uint32_t FIFO1; /*!< Offset: 0xEFC (R/ ) Integration ITM Data */ + __IO uint32_t ITCTRL; /*!< Offset: 0xF00 (R/W) Integration Mode Control */ + uint32_t RESERVED5[39]; + __IO uint32_t CLAIMSET; /*!< Offset: 0xFA0 (R/W) Claim tag set */ + __IO uint32_t CLAIMCLR; /*!< Offset: 0xFA4 (R/W) Claim tag clear */ + uint32_t RESERVED7[8]; + __I uint32_t DEVID; /*!< Offset: 0xFC8 (R/ ) TPIU_DEVID */ + __I uint32_t DEVTYPE; /*!< Offset: 0xFCC (R/ ) TPIU_DEVTYPE */ +} TPI_Type; + +/* TPI Asynchronous Clock Prescaler Register Definitions */ +#define TPI_ACPR_PRESCALER_Pos 0 /*!< TPI ACPR: PRESCALER Position */ +#define TPI_ACPR_PRESCALER_Msk (0x1FFFUL << TPI_ACPR_PRESCALER_Pos) /*!< TPI ACPR: PRESCALER Mask */ + +/* TPI Selected Pin Protocol Register Definitions */ +#define TPI_SPPR_TXMODE_Pos 0 /*!< TPI SPPR: TXMODE Position */ +#define TPI_SPPR_TXMODE_Msk (0x3UL << TPI_SPPR_TXMODE_Pos) /*!< TPI SPPR: TXMODE Mask */ + +/* TPI Formatter and Flush Status Register Definitions */ +#define TPI_FFSR_FtNonStop_Pos 3 /*!< TPI FFSR: FtNonStop Position */ +#define TPI_FFSR_FtNonStop_Msk (0x1UL << TPI_FFSR_FtNonStop_Pos) /*!< TPI FFSR: FtNonStop Mask */ + +#define TPI_FFSR_TCPresent_Pos 2 /*!< TPI FFSR: TCPresent Position */ +#define TPI_FFSR_TCPresent_Msk (0x1UL << TPI_FFSR_TCPresent_Pos) /*!< TPI FFSR: TCPresent Mask */ + +#define TPI_FFSR_FtStopped_Pos 1 /*!< TPI FFSR: FtStopped Position */ +#define TPI_FFSR_FtStopped_Msk (0x1UL << TPI_FFSR_FtStopped_Pos) /*!< TPI FFSR: FtStopped Mask */ + +#define TPI_FFSR_FlInProg_Pos 0 /*!< TPI FFSR: FlInProg Position */ +#define TPI_FFSR_FlInProg_Msk (0x1UL << TPI_FFSR_FlInProg_Pos) /*!< TPI FFSR: FlInProg Mask */ + +/* TPI Formatter and Flush Control Register Definitions */ +#define TPI_FFCR_TrigIn_Pos 8 /*!< TPI FFCR: TrigIn Position */ +#define TPI_FFCR_TrigIn_Msk (0x1UL << TPI_FFCR_TrigIn_Pos) /*!< TPI FFCR: TrigIn Mask */ + +#define TPI_FFCR_EnFCont_Pos 1 /*!< TPI FFCR: EnFCont Position */ +#define TPI_FFCR_EnFCont_Msk (0x1UL << TPI_FFCR_EnFCont_Pos) /*!< TPI FFCR: EnFCont Mask */ + +/* TPI TRIGGER Register Definitions */ +#define TPI_TRIGGER_TRIGGER_Pos 0 /*!< TPI TRIGGER: TRIGGER Position */ +#define TPI_TRIGGER_TRIGGER_Msk (0x1UL << TPI_TRIGGER_TRIGGER_Pos) /*!< TPI TRIGGER: TRIGGER Mask */ + +/* TPI Integration ETM Data Register Definitions (FIFO0) */ +#define TPI_FIFO0_ITM_ATVALID_Pos 29 /*!< TPI FIFO0: ITM_ATVALID Position */ +#define TPI_FIFO0_ITM_ATVALID_Msk (0x3UL << TPI_FIFO0_ITM_ATVALID_Pos) /*!< TPI FIFO0: ITM_ATVALID Mask */ + +#define TPI_FIFO0_ITM_bytecount_Pos 27 /*!< TPI FIFO0: ITM_bytecount Position */ +#define TPI_FIFO0_ITM_bytecount_Msk (0x3UL << TPI_FIFO0_ITM_bytecount_Pos) /*!< TPI FIFO0: ITM_bytecount Mask */ + +#define TPI_FIFO0_ETM_ATVALID_Pos 26 /*!< TPI FIFO0: ETM_ATVALID Position */ +#define TPI_FIFO0_ETM_ATVALID_Msk (0x3UL << TPI_FIFO0_ETM_ATVALID_Pos) /*!< TPI FIFO0: ETM_ATVALID Mask */ + +#define TPI_FIFO0_ETM_bytecount_Pos 24 /*!< TPI FIFO0: ETM_bytecount Position */ +#define TPI_FIFO0_ETM_bytecount_Msk (0x3UL << TPI_FIFO0_ETM_bytecount_Pos) /*!< TPI FIFO0: ETM_bytecount Mask */ + +#define TPI_FIFO0_ETM2_Pos 16 /*!< TPI FIFO0: ETM2 Position */ +#define TPI_FIFO0_ETM2_Msk (0xFFUL << TPI_FIFO0_ETM2_Pos) /*!< TPI FIFO0: ETM2 Mask */ + +#define TPI_FIFO0_ETM1_Pos 8 /*!< TPI FIFO0: ETM1 Position */ +#define TPI_FIFO0_ETM1_Msk (0xFFUL << TPI_FIFO0_ETM1_Pos) /*!< TPI FIFO0: ETM1 Mask */ + +#define TPI_FIFO0_ETM0_Pos 0 /*!< TPI FIFO0: ETM0 Position */ +#define TPI_FIFO0_ETM0_Msk (0xFFUL << TPI_FIFO0_ETM0_Pos) /*!< TPI FIFO0: ETM0 Mask */ + +/* TPI ITATBCTR2 Register Definitions */ +#define TPI_ITATBCTR2_ATREADY_Pos 0 /*!< TPI ITATBCTR2: ATREADY Position */ +#define TPI_ITATBCTR2_ATREADY_Msk (0x1UL << TPI_ITATBCTR2_ATREADY_Pos) /*!< TPI ITATBCTR2: ATREADY Mask */ + +/* TPI Integration ITM Data Register Definitions (FIFO1) */ +#define TPI_FIFO1_ITM_ATVALID_Pos 29 /*!< TPI FIFO1: ITM_ATVALID Position */ +#define TPI_FIFO1_ITM_ATVALID_Msk (0x3UL << TPI_FIFO1_ITM_ATVALID_Pos) /*!< TPI FIFO1: ITM_ATVALID Mask */ + +#define TPI_FIFO1_ITM_bytecount_Pos 27 /*!< TPI FIFO1: ITM_bytecount Position */ +#define TPI_FIFO1_ITM_bytecount_Msk (0x3UL << TPI_FIFO1_ITM_bytecount_Pos) /*!< TPI FIFO1: ITM_bytecount Mask */ + +#define TPI_FIFO1_ETM_ATVALID_Pos 26 /*!< TPI FIFO1: ETM_ATVALID Position */ +#define TPI_FIFO1_ETM_ATVALID_Msk (0x3UL << TPI_FIFO1_ETM_ATVALID_Pos) /*!< TPI FIFO1: ETM_ATVALID Mask */ + +#define TPI_FIFO1_ETM_bytecount_Pos 24 /*!< TPI FIFO1: ETM_bytecount Position */ +#define TPI_FIFO1_ETM_bytecount_Msk (0x3UL << TPI_FIFO1_ETM_bytecount_Pos) /*!< TPI FIFO1: ETM_bytecount Mask */ + +#define TPI_FIFO1_ITM2_Pos 16 /*!< TPI FIFO1: ITM2 Position */ +#define TPI_FIFO1_ITM2_Msk (0xFFUL << TPI_FIFO1_ITM2_Pos) /*!< TPI FIFO1: ITM2 Mask */ + +#define TPI_FIFO1_ITM1_Pos 8 /*!< TPI FIFO1: ITM1 Position */ +#define TPI_FIFO1_ITM1_Msk (0xFFUL << TPI_FIFO1_ITM1_Pos) /*!< TPI FIFO1: ITM1 Mask */ + +#define TPI_FIFO1_ITM0_Pos 0 /*!< TPI FIFO1: ITM0 Position */ +#define TPI_FIFO1_ITM0_Msk (0xFFUL << TPI_FIFO1_ITM0_Pos) /*!< TPI FIFO1: ITM0 Mask */ + +/* TPI ITATBCTR0 Register Definitions */ +#define TPI_ITATBCTR0_ATREADY_Pos 0 /*!< TPI ITATBCTR0: ATREADY Position */ +#define TPI_ITATBCTR0_ATREADY_Msk (0x1UL << TPI_ITATBCTR0_ATREADY_Pos) /*!< TPI ITATBCTR0: ATREADY Mask */ + +/* TPI Integration Mode Control Register Definitions */ +#define TPI_ITCTRL_Mode_Pos 0 /*!< TPI ITCTRL: Mode Position */ +#define TPI_ITCTRL_Mode_Msk (0x1UL << TPI_ITCTRL_Mode_Pos) /*!< TPI ITCTRL: Mode Mask */ + +/* TPI DEVID Register Definitions */ +#define TPI_DEVID_NRZVALID_Pos 11 /*!< TPI DEVID: NRZVALID Position */ +#define TPI_DEVID_NRZVALID_Msk (0x1UL << TPI_DEVID_NRZVALID_Pos) /*!< TPI DEVID: NRZVALID Mask */ + +#define TPI_DEVID_MANCVALID_Pos 10 /*!< TPI DEVID: MANCVALID Position */ +#define TPI_DEVID_MANCVALID_Msk (0x1UL << TPI_DEVID_MANCVALID_Pos) /*!< TPI DEVID: MANCVALID Mask */ + +#define TPI_DEVID_PTINVALID_Pos 9 /*!< TPI DEVID: PTINVALID Position */ +#define TPI_DEVID_PTINVALID_Msk (0x1UL << TPI_DEVID_PTINVALID_Pos) /*!< TPI DEVID: PTINVALID Mask */ + +#define TPI_DEVID_MinBufSz_Pos 6 /*!< TPI DEVID: MinBufSz Position */ +#define TPI_DEVID_MinBufSz_Msk (0x7UL << TPI_DEVID_MinBufSz_Pos) /*!< TPI DEVID: MinBufSz Mask */ + +#define TPI_DEVID_AsynClkIn_Pos 5 /*!< TPI DEVID: AsynClkIn Position */ +#define TPI_DEVID_AsynClkIn_Msk (0x1UL << TPI_DEVID_AsynClkIn_Pos) /*!< TPI DEVID: AsynClkIn Mask */ + +#define TPI_DEVID_NrTraceInput_Pos 0 /*!< TPI DEVID: NrTraceInput Position */ +#define TPI_DEVID_NrTraceInput_Msk (0x1FUL << TPI_DEVID_NrTraceInput_Pos) /*!< TPI DEVID: NrTraceInput Mask */ + +/* TPI DEVTYPE Register Definitions */ +#define TPI_DEVTYPE_SubType_Pos 0 /*!< TPI DEVTYPE: SubType Position */ +#define TPI_DEVTYPE_SubType_Msk (0xFUL << TPI_DEVTYPE_SubType_Pos) /*!< TPI DEVTYPE: SubType Mask */ + +#define TPI_DEVTYPE_MajorType_Pos 4 /*!< TPI DEVTYPE: MajorType Position */ +#define TPI_DEVTYPE_MajorType_Msk (0xFUL << TPI_DEVTYPE_MajorType_Pos) /*!< TPI DEVTYPE: MajorType Mask */ + +/*@}*/ /* end of group CMSIS_TPI */ + + +#if (__MPU_PRESENT == 1) +/** \ingroup CMSIS_core_register + \defgroup CMSIS_MPU Memory Protection Unit (MPU) + \brief Type definitions for the Memory Protection Unit (MPU) + @{ + */ + +/** \brief Structure type to access the Memory Protection Unit (MPU). + */ +typedef struct +{ + __I uint32_t TYPE; /*!< Offset: 0x000 (R/ ) MPU Type Register */ + __IO uint32_t CTRL; /*!< Offset: 0x004 (R/W) MPU Control Register */ + __IO uint32_t RNR; /*!< Offset: 0x008 (R/W) MPU Region RNRber Register */ + __IO uint32_t RBAR; /*!< Offset: 0x00C (R/W) MPU Region Base Address Register */ + __IO uint32_t RASR; /*!< Offset: 0x010 (R/W) MPU Region Attribute and Size Register */ + __IO uint32_t RBAR_A1; /*!< Offset: 0x014 (R/W) MPU Alias 1 Region Base Address Register */ + __IO uint32_t RASR_A1; /*!< Offset: 0x018 (R/W) MPU Alias 1 Region Attribute and Size Register */ + __IO uint32_t RBAR_A2; /*!< Offset: 0x01C (R/W) MPU Alias 2 Region Base Address Register */ + __IO uint32_t RASR_A2; /*!< Offset: 0x020 (R/W) MPU Alias 2 Region Attribute and Size Register */ + __IO uint32_t RBAR_A3; /*!< Offset: 0x024 (R/W) MPU Alias 3 Region Base Address Register */ + __IO uint32_t RASR_A3; /*!< Offset: 0x028 (R/W) MPU Alias 3 Region Attribute and Size Register */ +} MPU_Type; + +/* MPU Type Register */ +#define MPU_TYPE_IREGION_Pos 16 /*!< MPU TYPE: IREGION Position */ +#define MPU_TYPE_IREGION_Msk (0xFFUL << MPU_TYPE_IREGION_Pos) /*!< MPU TYPE: IREGION Mask */ + +#define MPU_TYPE_DREGION_Pos 8 /*!< MPU TYPE: DREGION Position */ +#define MPU_TYPE_DREGION_Msk (0xFFUL << MPU_TYPE_DREGION_Pos) /*!< MPU TYPE: DREGION Mask */ + +#define MPU_TYPE_SEPARATE_Pos 0 /*!< MPU TYPE: SEPARATE Position */ +#define MPU_TYPE_SEPARATE_Msk (1UL << MPU_TYPE_SEPARATE_Pos) /*!< MPU TYPE: SEPARATE Mask */ + +/* MPU Control Register */ +#define MPU_CTRL_PRIVDEFENA_Pos 2 /*!< MPU CTRL: PRIVDEFENA Position */ +#define MPU_CTRL_PRIVDEFENA_Msk (1UL << MPU_CTRL_PRIVDEFENA_Pos) /*!< MPU CTRL: PRIVDEFENA Mask */ + +#define MPU_CTRL_HFNMIENA_Pos 1 /*!< MPU CTRL: HFNMIENA Position */ +#define MPU_CTRL_HFNMIENA_Msk (1UL << MPU_CTRL_HFNMIENA_Pos) /*!< MPU CTRL: HFNMIENA Mask */ + +#define MPU_CTRL_ENABLE_Pos 0 /*!< MPU CTRL: ENABLE Position */ +#define MPU_CTRL_ENABLE_Msk (1UL << MPU_CTRL_ENABLE_Pos) /*!< MPU CTRL: ENABLE Mask */ + +/* MPU Region Number Register */ +#define MPU_RNR_REGION_Pos 0 /*!< MPU RNR: REGION Position */ +#define MPU_RNR_REGION_Msk (0xFFUL << MPU_RNR_REGION_Pos) /*!< MPU RNR: REGION Mask */ + +/* MPU Region Base Address Register */ +#define MPU_RBAR_ADDR_Pos 5 /*!< MPU RBAR: ADDR Position */ +#define MPU_RBAR_ADDR_Msk (0x7FFFFFFUL << MPU_RBAR_ADDR_Pos) /*!< MPU RBAR: ADDR Mask */ + +#define MPU_RBAR_VALID_Pos 4 /*!< MPU RBAR: VALID Position */ +#define MPU_RBAR_VALID_Msk (1UL << MPU_RBAR_VALID_Pos) /*!< MPU RBAR: VALID Mask */ + +#define MPU_RBAR_REGION_Pos 0 /*!< MPU RBAR: REGION Position */ +#define MPU_RBAR_REGION_Msk (0xFUL << MPU_RBAR_REGION_Pos) /*!< MPU RBAR: REGION Mask */ + +/* MPU Region Attribute and Size Register */ +#define MPU_RASR_ATTRS_Pos 16 /*!< MPU RASR: MPU Region Attribute field Position */ +#define MPU_RASR_ATTRS_Msk (0xFFFFUL << MPU_RASR_ATTRS_Pos) /*!< MPU RASR: MPU Region Attribute field Mask */ + +#define MPU_RASR_XN_Pos 28 /*!< MPU RASR: ATTRS.XN Position */ +#define MPU_RASR_XN_Msk (1UL << MPU_RASR_XN_Pos) /*!< MPU RASR: ATTRS.XN Mask */ + +#define MPU_RASR_AP_Pos 24 /*!< MPU RASR: ATTRS.AP Position */ +#define MPU_RASR_AP_Msk (0x7UL << MPU_RASR_AP_Pos) /*!< MPU RASR: ATTRS.AP Mask */ + +#define MPU_RASR_TEX_Pos 19 /*!< MPU RASR: ATTRS.TEX Position */ +#define MPU_RASR_TEX_Msk (0x7UL << MPU_RASR_TEX_Pos) /*!< MPU RASR: ATTRS.TEX Mask */ + +#define MPU_RASR_S_Pos 18 /*!< MPU RASR: ATTRS.S Position */ +#define MPU_RASR_S_Msk (1UL << MPU_RASR_S_Pos) /*!< MPU RASR: ATTRS.S Mask */ + +#define MPU_RASR_C_Pos 17 /*!< MPU RASR: ATTRS.C Position */ +#define MPU_RASR_C_Msk (1UL << MPU_RASR_C_Pos) /*!< MPU RASR: ATTRS.C Mask */ + +#define MPU_RASR_B_Pos 16 /*!< MPU RASR: ATTRS.B Position */ +#define MPU_RASR_B_Msk (1UL << MPU_RASR_B_Pos) /*!< MPU RASR: ATTRS.B Mask */ + +#define MPU_RASR_SRD_Pos 8 /*!< MPU RASR: Sub-Region Disable Position */ +#define MPU_RASR_SRD_Msk (0xFFUL << MPU_RASR_SRD_Pos) /*!< MPU RASR: Sub-Region Disable Mask */ + +#define MPU_RASR_SIZE_Pos 1 /*!< MPU RASR: Region Size Field Position */ +#define MPU_RASR_SIZE_Msk (0x1FUL << MPU_RASR_SIZE_Pos) /*!< MPU RASR: Region Size Field Mask */ + +#define MPU_RASR_ENABLE_Pos 0 /*!< MPU RASR: Region enable bit Position */ +#define MPU_RASR_ENABLE_Msk (1UL << MPU_RASR_ENABLE_Pos) /*!< MPU RASR: Region enable bit Disable Mask */ + +/*@} end of group CMSIS_MPU */ +#endif + + +/** \ingroup CMSIS_core_register + \defgroup CMSIS_CoreDebug Core Debug Registers (CoreDebug) + \brief Type definitions for the Core Debug Registers + @{ + */ + +/** \brief Structure type to access the Core Debug Register (CoreDebug). + */ +typedef struct +{ + __IO uint32_t DHCSR; /*!< Offset: 0x000 (R/W) Debug Halting Control and Status Register */ + __O uint32_t DCRSR; /*!< Offset: 0x004 ( /W) Debug Core Register Selector Register */ + __IO uint32_t DCRDR; /*!< Offset: 0x008 (R/W) Debug Core Register Data Register */ + __IO uint32_t DEMCR; /*!< Offset: 0x00C (R/W) Debug Exception and Monitor Control Register */ +} CoreDebug_Type; + +/* Debug Halting Control and Status Register */ +#define CoreDebug_DHCSR_DBGKEY_Pos 16 /*!< CoreDebug DHCSR: DBGKEY Position */ +#define CoreDebug_DHCSR_DBGKEY_Msk (0xFFFFUL << CoreDebug_DHCSR_DBGKEY_Pos) /*!< CoreDebug DHCSR: DBGKEY Mask */ + +#define CoreDebug_DHCSR_S_RESET_ST_Pos 25 /*!< CoreDebug DHCSR: S_RESET_ST Position */ +#define CoreDebug_DHCSR_S_RESET_ST_Msk (1UL << CoreDebug_DHCSR_S_RESET_ST_Pos) /*!< CoreDebug DHCSR: S_RESET_ST Mask */ + +#define CoreDebug_DHCSR_S_RETIRE_ST_Pos 24 /*!< CoreDebug DHCSR: S_RETIRE_ST Position */ +#define CoreDebug_DHCSR_S_RETIRE_ST_Msk (1UL << CoreDebug_DHCSR_S_RETIRE_ST_Pos) /*!< CoreDebug DHCSR: S_RETIRE_ST Mask */ + +#define CoreDebug_DHCSR_S_LOCKUP_Pos 19 /*!< CoreDebug DHCSR: S_LOCKUP Position */ +#define CoreDebug_DHCSR_S_LOCKUP_Msk (1UL << CoreDebug_DHCSR_S_LOCKUP_Pos) /*!< CoreDebug DHCSR: S_LOCKUP Mask */ + +#define CoreDebug_DHCSR_S_SLEEP_Pos 18 /*!< CoreDebug DHCSR: S_SLEEP Position */ +#define CoreDebug_DHCSR_S_SLEEP_Msk (1UL << CoreDebug_DHCSR_S_SLEEP_Pos) /*!< CoreDebug DHCSR: S_SLEEP Mask */ + +#define CoreDebug_DHCSR_S_HALT_Pos 17 /*!< CoreDebug DHCSR: S_HALT Position */ +#define CoreDebug_DHCSR_S_HALT_Msk (1UL << CoreDebug_DHCSR_S_HALT_Pos) /*!< CoreDebug DHCSR: S_HALT Mask */ + +#define CoreDebug_DHCSR_S_REGRDY_Pos 16 /*!< CoreDebug DHCSR: S_REGRDY Position */ +#define CoreDebug_DHCSR_S_REGRDY_Msk (1UL << CoreDebug_DHCSR_S_REGRDY_Pos) /*!< CoreDebug DHCSR: S_REGRDY Mask */ + +#define CoreDebug_DHCSR_C_SNAPSTALL_Pos 5 /*!< CoreDebug DHCSR: C_SNAPSTALL Position */ +#define CoreDebug_DHCSR_C_SNAPSTALL_Msk (1UL << CoreDebug_DHCSR_C_SNAPSTALL_Pos) /*!< CoreDebug DHCSR: C_SNAPSTALL Mask */ + +#define CoreDebug_DHCSR_C_MASKINTS_Pos 3 /*!< CoreDebug DHCSR: C_MASKINTS Position */ +#define CoreDebug_DHCSR_C_MASKINTS_Msk (1UL << CoreDebug_DHCSR_C_MASKINTS_Pos) /*!< CoreDebug DHCSR: C_MASKINTS Mask */ + +#define CoreDebug_DHCSR_C_STEP_Pos 2 /*!< CoreDebug DHCSR: C_STEP Position */ +#define CoreDebug_DHCSR_C_STEP_Msk (1UL << CoreDebug_DHCSR_C_STEP_Pos) /*!< CoreDebug DHCSR: C_STEP Mask */ + +#define CoreDebug_DHCSR_C_HALT_Pos 1 /*!< CoreDebug DHCSR: C_HALT Position */ +#define CoreDebug_DHCSR_C_HALT_Msk (1UL << CoreDebug_DHCSR_C_HALT_Pos) /*!< CoreDebug DHCSR: C_HALT Mask */ + +#define CoreDebug_DHCSR_C_DEBUGEN_Pos 0 /*!< CoreDebug DHCSR: C_DEBUGEN Position */ +#define CoreDebug_DHCSR_C_DEBUGEN_Msk (1UL << CoreDebug_DHCSR_C_DEBUGEN_Pos) /*!< CoreDebug DHCSR: C_DEBUGEN Mask */ + +/* Debug Core Register Selector Register */ +#define CoreDebug_DCRSR_REGWnR_Pos 16 /*!< CoreDebug DCRSR: REGWnR Position */ +#define CoreDebug_DCRSR_REGWnR_Msk (1UL << CoreDebug_DCRSR_REGWnR_Pos) /*!< CoreDebug DCRSR: REGWnR Mask */ + +#define CoreDebug_DCRSR_REGSEL_Pos 0 /*!< CoreDebug DCRSR: REGSEL Position */ +#define CoreDebug_DCRSR_REGSEL_Msk (0x1FUL << CoreDebug_DCRSR_REGSEL_Pos) /*!< CoreDebug DCRSR: REGSEL Mask */ + +/* Debug Exception and Monitor Control Register */ +#define CoreDebug_DEMCR_TRCENA_Pos 24 /*!< CoreDebug DEMCR: TRCENA Position */ +#define CoreDebug_DEMCR_TRCENA_Msk (1UL << CoreDebug_DEMCR_TRCENA_Pos) /*!< CoreDebug DEMCR: TRCENA Mask */ + +#define CoreDebug_DEMCR_MON_REQ_Pos 19 /*!< CoreDebug DEMCR: MON_REQ Position */ +#define CoreDebug_DEMCR_MON_REQ_Msk (1UL << CoreDebug_DEMCR_MON_REQ_Pos) /*!< CoreDebug DEMCR: MON_REQ Mask */ + +#define CoreDebug_DEMCR_MON_STEP_Pos 18 /*!< CoreDebug DEMCR: MON_STEP Position */ +#define CoreDebug_DEMCR_MON_STEP_Msk (1UL << CoreDebug_DEMCR_MON_STEP_Pos) /*!< CoreDebug DEMCR: MON_STEP Mask */ + +#define CoreDebug_DEMCR_MON_PEND_Pos 17 /*!< CoreDebug DEMCR: MON_PEND Position */ +#define CoreDebug_DEMCR_MON_PEND_Msk (1UL << CoreDebug_DEMCR_MON_PEND_Pos) /*!< CoreDebug DEMCR: MON_PEND Mask */ + +#define CoreDebug_DEMCR_MON_EN_Pos 16 /*!< CoreDebug DEMCR: MON_EN Position */ +#define CoreDebug_DEMCR_MON_EN_Msk (1UL << CoreDebug_DEMCR_MON_EN_Pos) /*!< CoreDebug DEMCR: MON_EN Mask */ + +#define CoreDebug_DEMCR_VC_HARDERR_Pos 10 /*!< CoreDebug DEMCR: VC_HARDERR Position */ +#define CoreDebug_DEMCR_VC_HARDERR_Msk (1UL << CoreDebug_DEMCR_VC_HARDERR_Pos) /*!< CoreDebug DEMCR: VC_HARDERR Mask */ + +#define CoreDebug_DEMCR_VC_INTERR_Pos 9 /*!< CoreDebug DEMCR: VC_INTERR Position */ +#define CoreDebug_DEMCR_VC_INTERR_Msk (1UL << CoreDebug_DEMCR_VC_INTERR_Pos) /*!< CoreDebug DEMCR: VC_INTERR Mask */ + +#define CoreDebug_DEMCR_VC_BUSERR_Pos 8 /*!< CoreDebug DEMCR: VC_BUSERR Position */ +#define CoreDebug_DEMCR_VC_BUSERR_Msk (1UL << CoreDebug_DEMCR_VC_BUSERR_Pos) /*!< CoreDebug DEMCR: VC_BUSERR Mask */ + +#define CoreDebug_DEMCR_VC_STATERR_Pos 7 /*!< CoreDebug DEMCR: VC_STATERR Position */ +#define CoreDebug_DEMCR_VC_STATERR_Msk (1UL << CoreDebug_DEMCR_VC_STATERR_Pos) /*!< CoreDebug DEMCR: VC_STATERR Mask */ + +#define CoreDebug_DEMCR_VC_CHKERR_Pos 6 /*!< CoreDebug DEMCR: VC_CHKERR Position */ +#define CoreDebug_DEMCR_VC_CHKERR_Msk (1UL << CoreDebug_DEMCR_VC_CHKERR_Pos) /*!< CoreDebug DEMCR: VC_CHKERR Mask */ + +#define CoreDebug_DEMCR_VC_NOCPERR_Pos 5 /*!< CoreDebug DEMCR: VC_NOCPERR Position */ +#define CoreDebug_DEMCR_VC_NOCPERR_Msk (1UL << CoreDebug_DEMCR_VC_NOCPERR_Pos) /*!< CoreDebug DEMCR: VC_NOCPERR Mask */ + +#define CoreDebug_DEMCR_VC_MMERR_Pos 4 /*!< CoreDebug DEMCR: VC_MMERR Position */ +#define CoreDebug_DEMCR_VC_MMERR_Msk (1UL << CoreDebug_DEMCR_VC_MMERR_Pos) /*!< CoreDebug DEMCR: VC_MMERR Mask */ + +#define CoreDebug_DEMCR_VC_CORERESET_Pos 0 /*!< CoreDebug DEMCR: VC_CORERESET Position */ +#define CoreDebug_DEMCR_VC_CORERESET_Msk (1UL << CoreDebug_DEMCR_VC_CORERESET_Pos) /*!< CoreDebug DEMCR: VC_CORERESET Mask */ + +/*@} end of group CMSIS_CoreDebug */ + + +/** \ingroup CMSIS_core_register + \defgroup CMSIS_core_base Core Definitions + \brief Definitions for base addresses, unions, and structures. + @{ + */ + +/* Memory mapping of Cortex-M3 Hardware */ +#define SCS_BASE (0xE000E000UL) /*!< System Control Space Base Address */ +#define ITM_BASE (0xE0000000UL) /*!< ITM Base Address */ +#define DWT_BASE (0xE0001000UL) /*!< DWT Base Address */ +#define TPI_BASE (0xE0040000UL) /*!< TPI Base Address */ +#define CoreDebug_BASE (0xE000EDF0UL) /*!< Core Debug Base Address */ +#define SysTick_BASE (SCS_BASE + 0x0010UL) /*!< SysTick Base Address */ +#define NVIC_BASE (SCS_BASE + 0x0100UL) /*!< NVIC Base Address */ +#define SCB_BASE (SCS_BASE + 0x0D00UL) /*!< System Control Block Base Address */ + +#define SCnSCB ((SCnSCB_Type *) SCS_BASE ) /*!< System control Register not in SCB */ +#define SCB ((SCB_Type *) SCB_BASE ) /*!< SCB configuration struct */ +#define SysTick ((SysTick_Type *) SysTick_BASE ) /*!< SysTick configuration struct */ +#define NVIC ((NVIC_Type *) NVIC_BASE ) /*!< NVIC configuration struct */ +#define ITM ((ITM_Type *) ITM_BASE ) /*!< ITM configuration struct */ +#define DWT ((DWT_Type *) DWT_BASE ) /*!< DWT configuration struct */ +#define TPI ((TPI_Type *) TPI_BASE ) /*!< TPI configuration struct */ +#define CoreDebug ((CoreDebug_Type *) CoreDebug_BASE) /*!< Core Debug configuration struct */ + +#if (__MPU_PRESENT == 1) + #define MPU_BASE (SCS_BASE + 0x0D90UL) /*!< Memory Protection Unit */ + #define MPU ((MPU_Type *) MPU_BASE ) /*!< Memory Protection Unit */ +#endif + +/*@} */ + + + +/******************************************************************************* + * Hardware Abstraction Layer + Core Function Interface contains: + - Core NVIC Functions + - Core SysTick Functions + - Core Debug Functions + - Core Register Access Functions + ******************************************************************************/ +/** \defgroup CMSIS_Core_FunctionInterface Functions and Instructions Reference +*/ + + + +/* ########################## NVIC functions #################################### */ +/** \ingroup CMSIS_Core_FunctionInterface + \defgroup CMSIS_Core_NVICFunctions NVIC Functions + \brief Functions that manage interrupts and exceptions via the NVIC. + @{ + */ + +/** \brief Set Priority Grouping + + The function sets the priority grouping field using the required unlock sequence. + The parameter PriorityGroup is assigned to the field SCB->AIRCR [10:8] PRIGROUP field. + Only values from 0..7 are used. + In case of a conflict between priority grouping and available + priority bits (__NVIC_PRIO_BITS), the smallest possible priority group is set. + + \param [in] PriorityGroup Priority grouping field. + */ +__STATIC_INLINE void NVIC_SetPriorityGrouping(uint32_t PriorityGroup) +{ + uint32_t reg_value; + uint32_t PriorityGroupTmp = (PriorityGroup & (uint32_t)0x07); /* only values 0..7 are used */ + + reg_value = SCB->AIRCR; /* read old register configuration */ + reg_value &= ~(SCB_AIRCR_VECTKEY_Msk | SCB_AIRCR_PRIGROUP_Msk); /* clear bits to change */ + reg_value = (reg_value | + ((uint32_t)0x5FA << SCB_AIRCR_VECTKEY_Pos) | + (PriorityGroupTmp << 8)); /* Insert write key and priorty group */ + SCB->AIRCR = reg_value; +} + + +/** \brief Get Priority Grouping + + The function reads the priority grouping field from the NVIC Interrupt Controller. + + \return Priority grouping field (SCB->AIRCR [10:8] PRIGROUP field). + */ +__STATIC_INLINE uint32_t NVIC_GetPriorityGrouping(void) +{ + return ((SCB->AIRCR & SCB_AIRCR_PRIGROUP_Msk) >> SCB_AIRCR_PRIGROUP_Pos); /* read priority grouping field */ +} + + +/** \brief Enable External Interrupt + + The function enables a device-specific interrupt in the NVIC interrupt controller. + + \param [in] IRQn External interrupt number. Value cannot be negative. + */ +__STATIC_INLINE void NVIC_EnableIRQ(IRQn_Type IRQn) +{ + NVIC->ISER[((uint32_t)(IRQn) >> 5)] = (1 << ((uint32_t)(IRQn) & 0x1F)); /* enable interrupt */ +} + + +/** \brief Disable External Interrupt + + The function disables a device-specific interrupt in the NVIC interrupt controller. + + \param [in] IRQn External interrupt number. Value cannot be negative. + */ +__STATIC_INLINE void NVIC_DisableIRQ(IRQn_Type IRQn) +{ + NVIC->ICER[((uint32_t)(IRQn) >> 5)] = (1 << ((uint32_t)(IRQn) & 0x1F)); /* disable interrupt */ +} + + +/** \brief Get Pending Interrupt + + The function reads the pending register in the NVIC and returns the pending bit + for the specified interrupt. + + \param [in] IRQn Interrupt number. + + \return 0 Interrupt status is not pending. + \return 1 Interrupt status is pending. + */ +__STATIC_INLINE uint32_t NVIC_GetPendingIRQ(IRQn_Type IRQn) +{ + return((uint32_t) ((NVIC->ISPR[(uint32_t)(IRQn) >> 5] & (1 << ((uint32_t)(IRQn) & 0x1F)))?1:0)); /* Return 1 if pending else 0 */ +} + + +/** \brief Set Pending Interrupt + + The function sets the pending bit of an external interrupt. + + \param [in] IRQn Interrupt number. Value cannot be negative. + */ +__STATIC_INLINE void NVIC_SetPendingIRQ(IRQn_Type IRQn) +{ + NVIC->ISPR[((uint32_t)(IRQn) >> 5)] = (1 << ((uint32_t)(IRQn) & 0x1F)); /* set interrupt pending */ +} + + +/** \brief Clear Pending Interrupt + + The function clears the pending bit of an external interrupt. + + \param [in] IRQn External interrupt number. Value cannot be negative. + */ +__STATIC_INLINE void NVIC_ClearPendingIRQ(IRQn_Type IRQn) +{ + NVIC->ICPR[((uint32_t)(IRQn) >> 5)] = (1 << ((uint32_t)(IRQn) & 0x1F)); /* Clear pending interrupt */ +} + + +/** \brief Get Active Interrupt + + The function reads the active register in NVIC and returns the active bit. + + \param [in] IRQn Interrupt number. + + \return 0 Interrupt status is not active. + \return 1 Interrupt status is active. + */ +__STATIC_INLINE uint32_t NVIC_GetActive(IRQn_Type IRQn) +{ + return((uint32_t)((NVIC->IABR[(uint32_t)(IRQn) >> 5] & (1 << ((uint32_t)(IRQn) & 0x1F)))?1:0)); /* Return 1 if active else 0 */ +} + + +/** \brief Set Interrupt Priority + + The function sets the priority of an interrupt. + + \note The priority cannot be set for every core interrupt. + + \param [in] IRQn Interrupt number. + \param [in] priority Priority to set. + */ +__STATIC_INLINE void NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority) +{ + if(IRQn < 0) { + SCB->SHP[((uint32_t)(IRQn) & 0xF)-4] = ((priority << (8 - __NVIC_PRIO_BITS)) & 0xff); } /* set Priority for Cortex-M System Interrupts */ + else { + NVIC->IP[(uint32_t)(IRQn)] = ((priority << (8 - __NVIC_PRIO_BITS)) & 0xff); } /* set Priority for device specific Interrupts */ +} + + +/** \brief Get Interrupt Priority + + The function reads the priority of an interrupt. The interrupt + number can be positive to specify an external (device specific) + interrupt, or negative to specify an internal (core) interrupt. + + + \param [in] IRQn Interrupt number. + \return Interrupt Priority. Value is aligned automatically to the implemented + priority bits of the microcontroller. + */ +__STATIC_INLINE uint32_t NVIC_GetPriority(IRQn_Type IRQn) +{ + + if(IRQn < 0) { + return((uint32_t)(SCB->SHP[((uint32_t)(IRQn) & 0xF)-4] >> (8 - __NVIC_PRIO_BITS))); } /* get priority for Cortex-M system interrupts */ + else { + return((uint32_t)(NVIC->IP[(uint32_t)(IRQn)] >> (8 - __NVIC_PRIO_BITS))); } /* get priority for device specific interrupts */ +} + + +/** \brief Encode Priority + + The function encodes the priority for an interrupt with the given priority group, + preemptive priority value, and subpriority value. + In case of a conflict between priority grouping and available + priority bits (__NVIC_PRIO_BITS), the smallest possible priority group is set. + + \param [in] PriorityGroup Used priority group. + \param [in] PreemptPriority Preemptive priority value (starting from 0). + \param [in] SubPriority Subpriority value (starting from 0). + \return Encoded priority. Value can be used in the function \ref NVIC_SetPriority(). + */ +__STATIC_INLINE uint32_t NVIC_EncodePriority (uint32_t PriorityGroup, uint32_t PreemptPriority, uint32_t SubPriority) +{ + uint32_t PriorityGroupTmp = (PriorityGroup & 0x07); /* only values 0..7 are used */ + uint32_t PreemptPriorityBits; + uint32_t SubPriorityBits; + + PreemptPriorityBits = ((7 - PriorityGroupTmp) > __NVIC_PRIO_BITS) ? __NVIC_PRIO_BITS : 7 - PriorityGroupTmp; + SubPriorityBits = ((PriorityGroupTmp + __NVIC_PRIO_BITS) < 7) ? 0 : PriorityGroupTmp - 7 + __NVIC_PRIO_BITS; + + return ( + ((PreemptPriority & ((1 << (PreemptPriorityBits)) - 1)) << SubPriorityBits) | + ((SubPriority & ((1 << (SubPriorityBits )) - 1))) + ); +} + + +/** \brief Decode Priority + + The function decodes an interrupt priority value with a given priority group to + preemptive priority value and subpriority value. + In case of a conflict between priority grouping and available + priority bits (__NVIC_PRIO_BITS) the smallest possible priority group is set. + + \param [in] Priority Priority value, which can be retrieved with the function \ref NVIC_GetPriority(). + \param [in] PriorityGroup Used priority group. + \param [out] pPreemptPriority Preemptive priority value (starting from 0). + \param [out] pSubPriority Subpriority value (starting from 0). + */ +__STATIC_INLINE void NVIC_DecodePriority (uint32_t Priority, uint32_t PriorityGroup, uint32_t* pPreemptPriority, uint32_t* pSubPriority) +{ + uint32_t PriorityGroupTmp = (PriorityGroup & 0x07); /* only values 0..7 are used */ + uint32_t PreemptPriorityBits; + uint32_t SubPriorityBits; + + PreemptPriorityBits = ((7 - PriorityGroupTmp) > __NVIC_PRIO_BITS) ? __NVIC_PRIO_BITS : 7 - PriorityGroupTmp; + SubPriorityBits = ((PriorityGroupTmp + __NVIC_PRIO_BITS) < 7) ? 0 : PriorityGroupTmp - 7 + __NVIC_PRIO_BITS; + + *pPreemptPriority = (Priority >> SubPriorityBits) & ((1 << (PreemptPriorityBits)) - 1); + *pSubPriority = (Priority ) & ((1 << (SubPriorityBits )) - 1); +} + + +/** \brief System Reset + + The function initiates a system reset request to reset the MCU. + */ +__STATIC_INLINE void NVIC_SystemReset(void) +{ + __DSB(); /* Ensure all outstanding memory accesses included + buffered write are completed before reset */ + SCB->AIRCR = ((0x5FA << SCB_AIRCR_VECTKEY_Pos) | + (SCB->AIRCR & SCB_AIRCR_PRIGROUP_Msk) | + SCB_AIRCR_SYSRESETREQ_Msk); /* Keep priority group unchanged */ + __DSB(); /* Ensure completion of memory access */ + while(1); /* wait until reset */ +} + +/*@} end of CMSIS_Core_NVICFunctions */ + + + +/* ################################## SysTick function ############################################ */ +/** \ingroup CMSIS_Core_FunctionInterface + \defgroup CMSIS_Core_SysTickFunctions SysTick Functions + \brief Functions that configure the System. + @{ + */ + +#if (__Vendor_SysTickConfig == 0) + +/** \brief System Tick Configuration + + The function initializes the System Timer and its interrupt, and starts the System Tick Timer. + Counter is in free running mode to generate periodic interrupts. + + \param [in] ticks Number of ticks between two interrupts. + + \return 0 Function succeeded. + \return 1 Function failed. + + \note When the variable __Vendor_SysTickConfig is set to 1, then the + function SysTick_Config is not included. In this case, the file device.h + must contain a vendor-specific implementation of this function. + + */ +__STATIC_INLINE uint32_t SysTick_Config(uint32_t ticks) +{ + if ((ticks - 1) > SysTick_LOAD_RELOAD_Msk) return (1); /* Reload value impossible */ + + SysTick->LOAD = ticks - 1; /* set reload register */ + NVIC_SetPriority (SysTick_IRQn, (1<<__NVIC_PRIO_BITS) - 1); /* set Priority for Systick Interrupt */ + SysTick->VAL = 0; /* Load the SysTick Counter Value */ + SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk | + SysTick_CTRL_TICKINT_Msk | + SysTick_CTRL_ENABLE_Msk; /* Enable SysTick IRQ and SysTick Timer */ + return (0); /* Function successful */ +} + +#endif + +/*@} end of CMSIS_Core_SysTickFunctions */ + + + +/* ##################################### Debug In/Output function ########################################### */ +/** \ingroup CMSIS_Core_FunctionInterface + \defgroup CMSIS_core_DebugFunctions ITM Functions + \brief Functions that access the ITM debug interface. + @{ + */ + +extern volatile int32_t ITM_RxBuffer; /*!< External variable to receive characters. */ +#define ITM_RXBUFFER_EMPTY 0x5AA55AA5 /*!< Value identifying \ref ITM_RxBuffer is ready for next character. */ + + +/** \brief ITM Send Character + + The function transmits a character via the ITM channel 0, and + \li Just returns when no debugger is connected that has booked the output. + \li Is blocking when a debugger is connected, but the previous character sent has not been transmitted. + + \param [in] ch Character to transmit. + + \returns Character to transmit. + */ +__STATIC_INLINE uint32_t ITM_SendChar (uint32_t ch) +{ + if ((ITM->TCR & ITM_TCR_ITMENA_Msk) && /* ITM enabled */ + (ITM->TER & (1UL << 0) ) ) /* ITM Port #0 enabled */ + { + while (ITM->PORT[0].u32 == 0); + ITM->PORT[0].u8 = (uint8_t) ch; + } + return (ch); +} + + +/** \brief ITM Receive Character + + The function inputs a character via the external variable \ref ITM_RxBuffer. + + \return Received character. + \return -1 No character pending. + */ +__STATIC_INLINE int32_t ITM_ReceiveChar (void) { + int32_t ch = -1; /* no character available */ + + if (ITM_RxBuffer != ITM_RXBUFFER_EMPTY) { + ch = ITM_RxBuffer; + ITM_RxBuffer = ITM_RXBUFFER_EMPTY; /* ready for next character */ + } + + return (ch); +} + + +/** \brief ITM Check Character + + The function checks whether a character is pending for reading in the variable \ref ITM_RxBuffer. + + \return 0 No character available. + \return 1 Character available. + */ +__STATIC_INLINE int32_t ITM_CheckChar (void) { + + if (ITM_RxBuffer == ITM_RXBUFFER_EMPTY) { + return (0); /* no character available */ + } else { + return (1); /* character available */ + } +} + +/*@} end of CMSIS_core_DebugFunctions */ + + + + +#ifdef __cplusplus +} +#endif + +#endif /* __CORE_CM3_H_DEPENDANT */ + +#endif /* __CMSIS_GENERIC */ diff --git a/platform/mcu/xr871/include/driver/cmsis/core_cm4.h b/platform/mcu/xr871/include/driver/cmsis/core_cm4.h new file mode 100644 index 0000000000..bb6be1305d --- /dev/null +++ b/platform/mcu/xr871/include/driver/cmsis/core_cm4.h @@ -0,0 +1,1802 @@ +/**************************************************************************//** + * @file core_cm4.h + * @brief CMSIS Cortex-M4 Core Peripheral Access Layer Header File + * @version V4.00 + * @date 22. August 2014 + * + * @note + * + ******************************************************************************/ +/* Copyright (c) 2009 - 2014 ARM LIMITED + + All rights reserved. + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + - Neither the name of ARM nor the names of its contributors may be used + to endorse or promote products derived from this software without + specific prior written permission. + * + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDERS AND CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + ---------------------------------------------------------------------------*/ + + +#if defined ( __ICCARM__ ) + #pragma system_include /* treat file as system include file for MISRA check */ +#endif + +#ifndef __CORE_CM4_H_GENERIC +#define __CORE_CM4_H_GENERIC + +#ifdef __cplusplus + extern "C" { +#endif + +/** \page CMSIS_MISRA_Exceptions MISRA-C:2004 Compliance Exceptions + CMSIS violates the following MISRA-C:2004 rules: + + \li Required Rule 8.5, object/function definition in header file.
+ Function definitions in header files are used to allow 'inlining'. + + \li Required Rule 18.4, declaration of union type or object of union type: '{...}'.
+ Unions are used for effective representation of core registers. + + \li Advisory Rule 19.7, Function-like macro defined.
+ Function-like macros are used to allow more efficient code. + */ + + +/******************************************************************************* + * CMSIS definitions + ******************************************************************************/ +/** \ingroup Cortex_M4 + @{ + */ + +/* CMSIS CM4 definitions */ +#define __CM4_CMSIS_VERSION_MAIN (0x04) /*!< [31:16] CMSIS HAL main version */ +#define __CM4_CMSIS_VERSION_SUB (0x00) /*!< [15:0] CMSIS HAL sub version */ +#define __CM4_CMSIS_VERSION ((__CM4_CMSIS_VERSION_MAIN << 16) | \ + __CM4_CMSIS_VERSION_SUB ) /*!< CMSIS HAL version number */ + +#define __CORTEX_M (0x04) /*!< Cortex-M Core */ + + +#if defined ( __CC_ARM ) + #define __ASM __asm /*!< asm keyword for ARM Compiler */ + #define __INLINE __inline /*!< inline keyword for ARM Compiler */ + #define __STATIC_INLINE static __inline + +#elif defined ( __GNUC__ ) + #define __ASM __asm /*!< asm keyword for GNU Compiler */ + #define __INLINE inline /*!< inline keyword for GNU Compiler */ + #define __STATIC_INLINE static inline + +#elif defined ( __ICCARM__ ) + #define __ASM __asm /*!< asm keyword for IAR Compiler */ + #define __INLINE inline /*!< inline keyword for IAR Compiler. Only available in High optimization mode! */ + #define __STATIC_INLINE static inline + +#elif defined ( __TMS470__ ) + #define __ASM __asm /*!< asm keyword for TI CCS Compiler */ + #define __STATIC_INLINE static inline + +#elif defined ( __TASKING__ ) + #define __ASM __asm /*!< asm keyword for TASKING Compiler */ + #define __INLINE inline /*!< inline keyword for TASKING Compiler */ + #define __STATIC_INLINE static inline + +#elif defined ( __CSMC__ ) + #define __packed + #define __ASM _asm /*!< asm keyword for COSMIC Compiler */ + #define __INLINE inline /*use -pc99 on compile line !< inline keyword for COSMIC Compiler */ + #define __STATIC_INLINE static inline + +#endif + +/** __FPU_USED indicates whether an FPU is used or not. + For this, __FPU_PRESENT has to be checked prior to making use of FPU specific registers and functions. +*/ +#if defined ( __CC_ARM ) + #if defined __TARGET_FPU_VFP + #if (__FPU_PRESENT == 1) + #define __FPU_USED 1 + #else + #warning "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #define __FPU_USED 0 + #endif + #else + #define __FPU_USED 0 + #endif + +#elif defined ( __GNUC__ ) + #if defined (__VFP_FP__) && !defined(__SOFTFP__) + #if (__FPU_PRESENT == 1) + #define __FPU_USED 1 + #else + #warning "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #define __FPU_USED 0 + #endif + #else + #define __FPU_USED 0 + #endif + +#elif defined ( __ICCARM__ ) + #if defined __ARMVFP__ + #if (__FPU_PRESENT == 1) + #define __FPU_USED 1 + #else + #warning "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #define __FPU_USED 0 + #endif + #else + #define __FPU_USED 0 + #endif + +#elif defined ( __TMS470__ ) + #if defined __TI_VFP_SUPPORT__ + #if (__FPU_PRESENT == 1) + #define __FPU_USED 1 + #else + #warning "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #define __FPU_USED 0 + #endif + #else + #define __FPU_USED 0 + #endif + +#elif defined ( __TASKING__ ) + #if defined __FPU_VFP__ + #if (__FPU_PRESENT == 1) + #define __FPU_USED 1 + #else + #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #define __FPU_USED 0 + #endif + #else + #define __FPU_USED 0 + #endif + +#elif defined ( __CSMC__ ) /* Cosmic */ + #if ( __CSMC__ & 0x400) // FPU present for parser + #if (__FPU_PRESENT == 1) + #define __FPU_USED 1 + #else + #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #define __FPU_USED 0 + #endif + #else + #define __FPU_USED 0 + #endif +#endif + +#include /* standard types definitions */ +#include /* Core Instruction Access */ +#include /* Core Function Access */ +#include /* Compiler specific SIMD Intrinsics */ + +#ifdef __cplusplus +} +#endif + +#endif /* __CORE_CM4_H_GENERIC */ + +#ifndef __CMSIS_GENERIC + +#ifndef __CORE_CM4_H_DEPENDANT +#define __CORE_CM4_H_DEPENDANT + +#ifdef __cplusplus + extern "C" { +#endif + +/* check device defines and use defaults */ +#if defined __CHECK_DEVICE_DEFINES + #ifndef __CM4_REV + #define __CM4_REV 0x0000 + #warning "__CM4_REV not defined in device header file; using default!" + #endif + + #ifndef __FPU_PRESENT + #define __FPU_PRESENT 0 + #warning "__FPU_PRESENT not defined in device header file; using default!" + #endif + + #ifndef __MPU_PRESENT + #define __MPU_PRESENT 0 + #warning "__MPU_PRESENT not defined in device header file; using default!" + #endif + + #ifndef __NVIC_PRIO_BITS + #define __NVIC_PRIO_BITS 4 + #warning "__NVIC_PRIO_BITS not defined in device header file; using default!" + #endif + + #ifndef __Vendor_SysTickConfig + #define __Vendor_SysTickConfig 0 + #warning "__Vendor_SysTickConfig not defined in device header file; using default!" + #endif +#endif + +/* IO definitions (access restrictions to peripheral registers) */ +/** + \defgroup CMSIS_glob_defs CMSIS Global Defines + + IO Type Qualifiers are used + \li to specify the access to peripheral variables. + \li for automatic generation of peripheral register debug information. +*/ +#ifdef __cplusplus + #define __I volatile /*!< Defines 'read only' permissions */ +#else + #define __I volatile const /*!< Defines 'read only' permissions */ +#endif +#define __O volatile /*!< Defines 'write only' permissions */ +#define __IO volatile /*!< Defines 'read / write' permissions */ + +/*@} end of group Cortex_M4 */ + + + +/******************************************************************************* + * Register Abstraction + Core Register contain: + - Core Register + - Core NVIC Register + - Core SCB Register + - Core SysTick Register + - Core Debug Register + - Core MPU Register + - Core FPU Register + ******************************************************************************/ +/** \defgroup CMSIS_core_register Defines and Type Definitions + \brief Type definitions and defines for Cortex-M processor based devices. +*/ + +/** \ingroup CMSIS_core_register + \defgroup CMSIS_CORE Status and Control Registers + \brief Core Register type definitions. + @{ + */ + +/** \brief Union type to access the Application Program Status Register (APSR). + */ +typedef union +{ + struct + { +#if (__CORTEX_M != 0x04) + uint32_t _reserved0:27; /*!< bit: 0..26 Reserved */ +#else + uint32_t _reserved0:16; /*!< bit: 0..15 Reserved */ + uint32_t GE:4; /*!< bit: 16..19 Greater than or Equal flags */ + uint32_t _reserved1:7; /*!< bit: 20..26 Reserved */ +#endif + uint32_t Q:1; /*!< bit: 27 Saturation condition flag */ + uint32_t V:1; /*!< bit: 28 Overflow condition code flag */ + uint32_t C:1; /*!< bit: 29 Carry condition code flag */ + uint32_t Z:1; /*!< bit: 30 Zero condition code flag */ + uint32_t N:1; /*!< bit: 31 Negative condition code flag */ + } b; /*!< Structure used for bit access */ + uint32_t w; /*!< Type used for word access */ +} APSR_Type; + + +/** \brief Union type to access the Interrupt Program Status Register (IPSR). + */ +typedef union +{ + struct + { + uint32_t ISR:9; /*!< bit: 0.. 8 Exception number */ + uint32_t _reserved0:23; /*!< bit: 9..31 Reserved */ + } b; /*!< Structure used for bit access */ + uint32_t w; /*!< Type used for word access */ +} IPSR_Type; + + +/** \brief Union type to access the Special-Purpose Program Status Registers (xPSR). + */ +typedef union +{ + struct + { + uint32_t ISR:9; /*!< bit: 0.. 8 Exception number */ +#if (__CORTEX_M != 0x04) + uint32_t _reserved0:15; /*!< bit: 9..23 Reserved */ +#else + uint32_t _reserved0:7; /*!< bit: 9..15 Reserved */ + uint32_t GE:4; /*!< bit: 16..19 Greater than or Equal flags */ + uint32_t _reserved1:4; /*!< bit: 20..23 Reserved */ +#endif + uint32_t T:1; /*!< bit: 24 Thumb bit (read 0) */ + uint32_t IT:2; /*!< bit: 25..26 saved IT state (read 0) */ + uint32_t Q:1; /*!< bit: 27 Saturation condition flag */ + uint32_t V:1; /*!< bit: 28 Overflow condition code flag */ + uint32_t C:1; /*!< bit: 29 Carry condition code flag */ + uint32_t Z:1; /*!< bit: 30 Zero condition code flag */ + uint32_t N:1; /*!< bit: 31 Negative condition code flag */ + } b; /*!< Structure used for bit access */ + uint32_t w; /*!< Type used for word access */ +} xPSR_Type; + + +/** \brief Union type to access the Control Registers (CONTROL). + */ +typedef union +{ + struct + { + uint32_t nPRIV:1; /*!< bit: 0 Execution privilege in Thread mode */ + uint32_t SPSEL:1; /*!< bit: 1 Stack to be used */ + uint32_t FPCA:1; /*!< bit: 2 FP extension active flag */ + uint32_t _reserved0:29; /*!< bit: 3..31 Reserved */ + } b; /*!< Structure used for bit access */ + uint32_t w; /*!< Type used for word access */ +} CONTROL_Type; + +/*@} end of group CMSIS_CORE */ + + +/** \ingroup CMSIS_core_register + \defgroup CMSIS_NVIC Nested Vectored Interrupt Controller (NVIC) + \brief Type definitions for the NVIC Registers + @{ + */ + +/** \brief Structure type to access the Nested Vectored Interrupt Controller (NVIC). + */ +typedef struct +{ + __IO uint32_t ISER[8]; /*!< Offset: 0x000 (R/W) Interrupt Set Enable Register */ + uint32_t RESERVED0[24]; + __IO uint32_t ICER[8]; /*!< Offset: 0x080 (R/W) Interrupt Clear Enable Register */ + uint32_t RSERVED1[24]; + __IO uint32_t ISPR[8]; /*!< Offset: 0x100 (R/W) Interrupt Set Pending Register */ + uint32_t RESERVED2[24]; + __IO uint32_t ICPR[8]; /*!< Offset: 0x180 (R/W) Interrupt Clear Pending Register */ + uint32_t RESERVED3[24]; + __IO uint32_t IABR[8]; /*!< Offset: 0x200 (R/W) Interrupt Active bit Register */ + uint32_t RESERVED4[56]; + __IO uint8_t IP[240]; /*!< Offset: 0x300 (R/W) Interrupt Priority Register (8Bit wide) */ + uint32_t RESERVED5[644]; + __O uint32_t STIR; /*!< Offset: 0xE00 ( /W) Software Trigger Interrupt Register */ +} NVIC_Type; + +/* Software Triggered Interrupt Register Definitions */ +#define NVIC_STIR_INTID_Pos 0 /*!< STIR: INTLINESNUM Position */ +#define NVIC_STIR_INTID_Msk (0x1FFUL << NVIC_STIR_INTID_Pos) /*!< STIR: INTLINESNUM Mask */ + +/*@} end of group CMSIS_NVIC */ + + +/** \ingroup CMSIS_core_register + \defgroup CMSIS_SCB System Control Block (SCB) + \brief Type definitions for the System Control Block Registers + @{ + */ + +/** \brief Structure type to access the System Control Block (SCB). + */ +typedef struct +{ + __I uint32_t CPUID; /*!< Offset: 0x000 (R/ ) CPUID Base Register */ + __IO uint32_t ICSR; /*!< Offset: 0x004 (R/W) Interrupt Control and State Register */ + __IO uint32_t VTOR; /*!< Offset: 0x008 (R/W) Vector Table Offset Register */ + __IO uint32_t AIRCR; /*!< Offset: 0x00C (R/W) Application Interrupt and Reset Control Register */ + __IO uint32_t SCR; /*!< Offset: 0x010 (R/W) System Control Register */ + __IO uint32_t CCR; /*!< Offset: 0x014 (R/W) Configuration Control Register */ + __IO uint8_t SHP[12]; /*!< Offset: 0x018 (R/W) System Handlers Priority Registers (4-7, 8-11, 12-15) */ + __IO uint32_t SHCSR; /*!< Offset: 0x024 (R/W) System Handler Control and State Register */ + __IO uint32_t CFSR; /*!< Offset: 0x028 (R/W) Configurable Fault Status Register */ + __IO uint32_t HFSR; /*!< Offset: 0x02C (R/W) HardFault Status Register */ + __IO uint32_t DFSR; /*!< Offset: 0x030 (R/W) Debug Fault Status Register */ + __IO uint32_t MMFAR; /*!< Offset: 0x034 (R/W) MemManage Fault Address Register */ + __IO uint32_t BFAR; /*!< Offset: 0x038 (R/W) BusFault Address Register */ + __IO uint32_t AFSR; /*!< Offset: 0x03C (R/W) Auxiliary Fault Status Register */ + __I uint32_t PFR[2]; /*!< Offset: 0x040 (R/ ) Processor Feature Register */ + __I uint32_t DFR; /*!< Offset: 0x048 (R/ ) Debug Feature Register */ + __I uint32_t ADR; /*!< Offset: 0x04C (R/ ) Auxiliary Feature Register */ + __I uint32_t MMFR[4]; /*!< Offset: 0x050 (R/ ) Memory Model Feature Register */ + __I uint32_t ISAR[5]; /*!< Offset: 0x060 (R/ ) Instruction Set Attributes Register */ + uint32_t RESERVED0[5]; + __IO uint32_t CPACR; /*!< Offset: 0x088 (R/W) Coprocessor Access Control Register */ +} SCB_Type; + +/* SCB CPUID Register Definitions */ +#define SCB_CPUID_IMPLEMENTER_Pos 24 /*!< SCB CPUID: IMPLEMENTER Position */ +#define SCB_CPUID_IMPLEMENTER_Msk (0xFFUL << SCB_CPUID_IMPLEMENTER_Pos) /*!< SCB CPUID: IMPLEMENTER Mask */ + +#define SCB_CPUID_VARIANT_Pos 20 /*!< SCB CPUID: VARIANT Position */ +#define SCB_CPUID_VARIANT_Msk (0xFUL << SCB_CPUID_VARIANT_Pos) /*!< SCB CPUID: VARIANT Mask */ + +#define SCB_CPUID_ARCHITECTURE_Pos 16 /*!< SCB CPUID: ARCHITECTURE Position */ +#define SCB_CPUID_ARCHITECTURE_Msk (0xFUL << SCB_CPUID_ARCHITECTURE_Pos) /*!< SCB CPUID: ARCHITECTURE Mask */ + +#define SCB_CPUID_PARTNO_Pos 4 /*!< SCB CPUID: PARTNO Position */ +#define SCB_CPUID_PARTNO_Msk (0xFFFUL << SCB_CPUID_PARTNO_Pos) /*!< SCB CPUID: PARTNO Mask */ + +#define SCB_CPUID_REVISION_Pos 0 /*!< SCB CPUID: REVISION Position */ +#define SCB_CPUID_REVISION_Msk (0xFUL << SCB_CPUID_REVISION_Pos) /*!< SCB CPUID: REVISION Mask */ + +/* SCB Interrupt Control State Register Definitions */ +#define SCB_ICSR_NMIPENDSET_Pos 31 /*!< SCB ICSR: NMIPENDSET Position */ +#define SCB_ICSR_NMIPENDSET_Msk (1UL << SCB_ICSR_NMIPENDSET_Pos) /*!< SCB ICSR: NMIPENDSET Mask */ + +#define SCB_ICSR_PENDSVSET_Pos 28 /*!< SCB ICSR: PENDSVSET Position */ +#define SCB_ICSR_PENDSVSET_Msk (1UL << SCB_ICSR_PENDSVSET_Pos) /*!< SCB ICSR: PENDSVSET Mask */ + +#define SCB_ICSR_PENDSVCLR_Pos 27 /*!< SCB ICSR: PENDSVCLR Position */ +#define SCB_ICSR_PENDSVCLR_Msk (1UL << SCB_ICSR_PENDSVCLR_Pos) /*!< SCB ICSR: PENDSVCLR Mask */ + +#define SCB_ICSR_PENDSTSET_Pos 26 /*!< SCB ICSR: PENDSTSET Position */ +#define SCB_ICSR_PENDSTSET_Msk (1UL << SCB_ICSR_PENDSTSET_Pos) /*!< SCB ICSR: PENDSTSET Mask */ + +#define SCB_ICSR_PENDSTCLR_Pos 25 /*!< SCB ICSR: PENDSTCLR Position */ +#define SCB_ICSR_PENDSTCLR_Msk (1UL << SCB_ICSR_PENDSTCLR_Pos) /*!< SCB ICSR: PENDSTCLR Mask */ + +#define SCB_ICSR_ISRPREEMPT_Pos 23 /*!< SCB ICSR: ISRPREEMPT Position */ +#define SCB_ICSR_ISRPREEMPT_Msk (1UL << SCB_ICSR_ISRPREEMPT_Pos) /*!< SCB ICSR: ISRPREEMPT Mask */ + +#define SCB_ICSR_ISRPENDING_Pos 22 /*!< SCB ICSR: ISRPENDING Position */ +#define SCB_ICSR_ISRPENDING_Msk (1UL << SCB_ICSR_ISRPENDING_Pos) /*!< SCB ICSR: ISRPENDING Mask */ + +#define SCB_ICSR_VECTPENDING_Pos 12 /*!< SCB ICSR: VECTPENDING Position */ +#define SCB_ICSR_VECTPENDING_Msk (0x1FFUL << SCB_ICSR_VECTPENDING_Pos) /*!< SCB ICSR: VECTPENDING Mask */ + +#define SCB_ICSR_RETTOBASE_Pos 11 /*!< SCB ICSR: RETTOBASE Position */ +#define SCB_ICSR_RETTOBASE_Msk (1UL << SCB_ICSR_RETTOBASE_Pos) /*!< SCB ICSR: RETTOBASE Mask */ + +#define SCB_ICSR_VECTACTIVE_Pos 0 /*!< SCB ICSR: VECTACTIVE Position */ +#define SCB_ICSR_VECTACTIVE_Msk (0x1FFUL << SCB_ICSR_VECTACTIVE_Pos) /*!< SCB ICSR: VECTACTIVE Mask */ + +/* SCB Vector Table Offset Register Definitions */ +#define SCB_VTOR_TBLOFF_Pos 7 /*!< SCB VTOR: TBLOFF Position */ +#define SCB_VTOR_TBLOFF_Msk (0x1FFFFFFUL << SCB_VTOR_TBLOFF_Pos) /*!< SCB VTOR: TBLOFF Mask */ + +/* SCB Application Interrupt and Reset Control Register Definitions */ +#define SCB_AIRCR_VECTKEY_Pos 16 /*!< SCB AIRCR: VECTKEY Position */ +#define SCB_AIRCR_VECTKEY_Msk (0xFFFFUL << SCB_AIRCR_VECTKEY_Pos) /*!< SCB AIRCR: VECTKEY Mask */ + +#define SCB_AIRCR_VECTKEYSTAT_Pos 16 /*!< SCB AIRCR: VECTKEYSTAT Position */ +#define SCB_AIRCR_VECTKEYSTAT_Msk (0xFFFFUL << SCB_AIRCR_VECTKEYSTAT_Pos) /*!< SCB AIRCR: VECTKEYSTAT Mask */ + +#define SCB_AIRCR_ENDIANESS_Pos 15 /*!< SCB AIRCR: ENDIANESS Position */ +#define SCB_AIRCR_ENDIANESS_Msk (1UL << SCB_AIRCR_ENDIANESS_Pos) /*!< SCB AIRCR: ENDIANESS Mask */ + +#define SCB_AIRCR_PRIGROUP_Pos 8 /*!< SCB AIRCR: PRIGROUP Position */ +#define SCB_AIRCR_PRIGROUP_Msk (7UL << SCB_AIRCR_PRIGROUP_Pos) /*!< SCB AIRCR: PRIGROUP Mask */ + +#define SCB_AIRCR_SYSRESETREQ_Pos 2 /*!< SCB AIRCR: SYSRESETREQ Position */ +#define SCB_AIRCR_SYSRESETREQ_Msk (1UL << SCB_AIRCR_SYSRESETREQ_Pos) /*!< SCB AIRCR: SYSRESETREQ Mask */ + +#define SCB_AIRCR_VECTCLRACTIVE_Pos 1 /*!< SCB AIRCR: VECTCLRACTIVE Position */ +#define SCB_AIRCR_VECTCLRACTIVE_Msk (1UL << SCB_AIRCR_VECTCLRACTIVE_Pos) /*!< SCB AIRCR: VECTCLRACTIVE Mask */ + +#define SCB_AIRCR_VECTRESET_Pos 0 /*!< SCB AIRCR: VECTRESET Position */ +#define SCB_AIRCR_VECTRESET_Msk (1UL << SCB_AIRCR_VECTRESET_Pos) /*!< SCB AIRCR: VECTRESET Mask */ + +/* SCB System Control Register Definitions */ +#define SCB_SCR_SEVONPEND_Pos 4 /*!< SCB SCR: SEVONPEND Position */ +#define SCB_SCR_SEVONPEND_Msk (1UL << SCB_SCR_SEVONPEND_Pos) /*!< SCB SCR: SEVONPEND Mask */ + +#define SCB_SCR_SLEEPDEEP_Pos 2 /*!< SCB SCR: SLEEPDEEP Position */ +#define SCB_SCR_SLEEPDEEP_Msk (1UL << SCB_SCR_SLEEPDEEP_Pos) /*!< SCB SCR: SLEEPDEEP Mask */ + +#define SCB_SCR_SLEEPONEXIT_Pos 1 /*!< SCB SCR: SLEEPONEXIT Position */ +#define SCB_SCR_SLEEPONEXIT_Msk (1UL << SCB_SCR_SLEEPONEXIT_Pos) /*!< SCB SCR: SLEEPONEXIT Mask */ + +/* SCB Configuration Control Register Definitions */ +#define SCB_CCR_STKALIGN_Pos 9 /*!< SCB CCR: STKALIGN Position */ +#define SCB_CCR_STKALIGN_Msk (1UL << SCB_CCR_STKALIGN_Pos) /*!< SCB CCR: STKALIGN Mask */ + +#define SCB_CCR_BFHFNMIGN_Pos 8 /*!< SCB CCR: BFHFNMIGN Position */ +#define SCB_CCR_BFHFNMIGN_Msk (1UL << SCB_CCR_BFHFNMIGN_Pos) /*!< SCB CCR: BFHFNMIGN Mask */ + +#define SCB_CCR_DIV_0_TRP_Pos 4 /*!< SCB CCR: DIV_0_TRP Position */ +#define SCB_CCR_DIV_0_TRP_Msk (1UL << SCB_CCR_DIV_0_TRP_Pos) /*!< SCB CCR: DIV_0_TRP Mask */ + +#define SCB_CCR_UNALIGN_TRP_Pos 3 /*!< SCB CCR: UNALIGN_TRP Position */ +#define SCB_CCR_UNALIGN_TRP_Msk (1UL << SCB_CCR_UNALIGN_TRP_Pos) /*!< SCB CCR: UNALIGN_TRP Mask */ + +#define SCB_CCR_USERSETMPEND_Pos 1 /*!< SCB CCR: USERSETMPEND Position */ +#define SCB_CCR_USERSETMPEND_Msk (1UL << SCB_CCR_USERSETMPEND_Pos) /*!< SCB CCR: USERSETMPEND Mask */ + +#define SCB_CCR_NONBASETHRDENA_Pos 0 /*!< SCB CCR: NONBASETHRDENA Position */ +#define SCB_CCR_NONBASETHRDENA_Msk (1UL << SCB_CCR_NONBASETHRDENA_Pos) /*!< SCB CCR: NONBASETHRDENA Mask */ + +/* SCB System Handler Control and State Register Definitions */ +#define SCB_SHCSR_USGFAULTENA_Pos 18 /*!< SCB SHCSR: USGFAULTENA Position */ +#define SCB_SHCSR_USGFAULTENA_Msk (1UL << SCB_SHCSR_USGFAULTENA_Pos) /*!< SCB SHCSR: USGFAULTENA Mask */ + +#define SCB_SHCSR_BUSFAULTENA_Pos 17 /*!< SCB SHCSR: BUSFAULTENA Position */ +#define SCB_SHCSR_BUSFAULTENA_Msk (1UL << SCB_SHCSR_BUSFAULTENA_Pos) /*!< SCB SHCSR: BUSFAULTENA Mask */ + +#define SCB_SHCSR_MEMFAULTENA_Pos 16 /*!< SCB SHCSR: MEMFAULTENA Position */ +#define SCB_SHCSR_MEMFAULTENA_Msk (1UL << SCB_SHCSR_MEMFAULTENA_Pos) /*!< SCB SHCSR: MEMFAULTENA Mask */ + +#define SCB_SHCSR_SVCALLPENDED_Pos 15 /*!< SCB SHCSR: SVCALLPENDED Position */ +#define SCB_SHCSR_SVCALLPENDED_Msk (1UL << SCB_SHCSR_SVCALLPENDED_Pos) /*!< SCB SHCSR: SVCALLPENDED Mask */ + +#define SCB_SHCSR_BUSFAULTPENDED_Pos 14 /*!< SCB SHCSR: BUSFAULTPENDED Position */ +#define SCB_SHCSR_BUSFAULTPENDED_Msk (1UL << SCB_SHCSR_BUSFAULTPENDED_Pos) /*!< SCB SHCSR: BUSFAULTPENDED Mask */ + +#define SCB_SHCSR_MEMFAULTPENDED_Pos 13 /*!< SCB SHCSR: MEMFAULTPENDED Position */ +#define SCB_SHCSR_MEMFAULTPENDED_Msk (1UL << SCB_SHCSR_MEMFAULTPENDED_Pos) /*!< SCB SHCSR: MEMFAULTPENDED Mask */ + +#define SCB_SHCSR_USGFAULTPENDED_Pos 12 /*!< SCB SHCSR: USGFAULTPENDED Position */ +#define SCB_SHCSR_USGFAULTPENDED_Msk (1UL << SCB_SHCSR_USGFAULTPENDED_Pos) /*!< SCB SHCSR: USGFAULTPENDED Mask */ + +#define SCB_SHCSR_SYSTICKACT_Pos 11 /*!< SCB SHCSR: SYSTICKACT Position */ +#define SCB_SHCSR_SYSTICKACT_Msk (1UL << SCB_SHCSR_SYSTICKACT_Pos) /*!< SCB SHCSR: SYSTICKACT Mask */ + +#define SCB_SHCSR_PENDSVACT_Pos 10 /*!< SCB SHCSR: PENDSVACT Position */ +#define SCB_SHCSR_PENDSVACT_Msk (1UL << SCB_SHCSR_PENDSVACT_Pos) /*!< SCB SHCSR: PENDSVACT Mask */ + +#define SCB_SHCSR_MONITORACT_Pos 8 /*!< SCB SHCSR: MONITORACT Position */ +#define SCB_SHCSR_MONITORACT_Msk (1UL << SCB_SHCSR_MONITORACT_Pos) /*!< SCB SHCSR: MONITORACT Mask */ + +#define SCB_SHCSR_SVCALLACT_Pos 7 /*!< SCB SHCSR: SVCALLACT Position */ +#define SCB_SHCSR_SVCALLACT_Msk (1UL << SCB_SHCSR_SVCALLACT_Pos) /*!< SCB SHCSR: SVCALLACT Mask */ + +#define SCB_SHCSR_USGFAULTACT_Pos 3 /*!< SCB SHCSR: USGFAULTACT Position */ +#define SCB_SHCSR_USGFAULTACT_Msk (1UL << SCB_SHCSR_USGFAULTACT_Pos) /*!< SCB SHCSR: USGFAULTACT Mask */ + +#define SCB_SHCSR_BUSFAULTACT_Pos 1 /*!< SCB SHCSR: BUSFAULTACT Position */ +#define SCB_SHCSR_BUSFAULTACT_Msk (1UL << SCB_SHCSR_BUSFAULTACT_Pos) /*!< SCB SHCSR: BUSFAULTACT Mask */ + +#define SCB_SHCSR_MEMFAULTACT_Pos 0 /*!< SCB SHCSR: MEMFAULTACT Position */ +#define SCB_SHCSR_MEMFAULTACT_Msk (1UL << SCB_SHCSR_MEMFAULTACT_Pos) /*!< SCB SHCSR: MEMFAULTACT Mask */ + +/* SCB Configurable Fault Status Registers Definitions */ +#define SCB_CFSR_USGFAULTSR_Pos 16 /*!< SCB CFSR: Usage Fault Status Register Position */ +#define SCB_CFSR_USGFAULTSR_Msk (0xFFFFUL << SCB_CFSR_USGFAULTSR_Pos) /*!< SCB CFSR: Usage Fault Status Register Mask */ + +#define SCB_CFSR_BUSFAULTSR_Pos 8 /*!< SCB CFSR: Bus Fault Status Register Position */ +#define SCB_CFSR_BUSFAULTSR_Msk (0xFFUL << SCB_CFSR_BUSFAULTSR_Pos) /*!< SCB CFSR: Bus Fault Status Register Mask */ + +#define SCB_CFSR_MEMFAULTSR_Pos 0 /*!< SCB CFSR: Memory Manage Fault Status Register Position */ +#define SCB_CFSR_MEMFAULTSR_Msk (0xFFUL << SCB_CFSR_MEMFAULTSR_Pos) /*!< SCB CFSR: Memory Manage Fault Status Register Mask */ + +/* SCB Hard Fault Status Registers Definitions */ +#define SCB_HFSR_DEBUGEVT_Pos 31 /*!< SCB HFSR: DEBUGEVT Position */ +#define SCB_HFSR_DEBUGEVT_Msk (1UL << SCB_HFSR_DEBUGEVT_Pos) /*!< SCB HFSR: DEBUGEVT Mask */ + +#define SCB_HFSR_FORCED_Pos 30 /*!< SCB HFSR: FORCED Position */ +#define SCB_HFSR_FORCED_Msk (1UL << SCB_HFSR_FORCED_Pos) /*!< SCB HFSR: FORCED Mask */ + +#define SCB_HFSR_VECTTBL_Pos 1 /*!< SCB HFSR: VECTTBL Position */ +#define SCB_HFSR_VECTTBL_Msk (1UL << SCB_HFSR_VECTTBL_Pos) /*!< SCB HFSR: VECTTBL Mask */ + +/* SCB Debug Fault Status Register Definitions */ +#define SCB_DFSR_EXTERNAL_Pos 4 /*!< SCB DFSR: EXTERNAL Position */ +#define SCB_DFSR_EXTERNAL_Msk (1UL << SCB_DFSR_EXTERNAL_Pos) /*!< SCB DFSR: EXTERNAL Mask */ + +#define SCB_DFSR_VCATCH_Pos 3 /*!< SCB DFSR: VCATCH Position */ +#define SCB_DFSR_VCATCH_Msk (1UL << SCB_DFSR_VCATCH_Pos) /*!< SCB DFSR: VCATCH Mask */ + +#define SCB_DFSR_DWTTRAP_Pos 2 /*!< SCB DFSR: DWTTRAP Position */ +#define SCB_DFSR_DWTTRAP_Msk (1UL << SCB_DFSR_DWTTRAP_Pos) /*!< SCB DFSR: DWTTRAP Mask */ + +#define SCB_DFSR_BKPT_Pos 1 /*!< SCB DFSR: BKPT Position */ +#define SCB_DFSR_BKPT_Msk (1UL << SCB_DFSR_BKPT_Pos) /*!< SCB DFSR: BKPT Mask */ + +#define SCB_DFSR_HALTED_Pos 0 /*!< SCB DFSR: HALTED Position */ +#define SCB_DFSR_HALTED_Msk (1UL << SCB_DFSR_HALTED_Pos) /*!< SCB DFSR: HALTED Mask */ + +/*@} end of group CMSIS_SCB */ + + +/** \ingroup CMSIS_core_register + \defgroup CMSIS_SCnSCB System Controls not in SCB (SCnSCB) + \brief Type definitions for the System Control and ID Register not in the SCB + @{ + */ + +/** \brief Structure type to access the System Control and ID Register not in the SCB. + */ +typedef struct +{ + uint32_t RESERVED0[1]; + __I uint32_t ICTR; /*!< Offset: 0x004 (R/ ) Interrupt Controller Type Register */ + __IO uint32_t ACTLR; /*!< Offset: 0x008 (R/W) Auxiliary Control Register */ +} SCnSCB_Type; + +/* Interrupt Controller Type Register Definitions */ +#define SCnSCB_ICTR_INTLINESNUM_Pos 0 /*!< ICTR: INTLINESNUM Position */ +#define SCnSCB_ICTR_INTLINESNUM_Msk (0xFUL << SCnSCB_ICTR_INTLINESNUM_Pos) /*!< ICTR: INTLINESNUM Mask */ + +/* Auxiliary Control Register Definitions */ +#define SCnSCB_ACTLR_DISOOFP_Pos 9 /*!< ACTLR: DISOOFP Position */ +#define SCnSCB_ACTLR_DISOOFP_Msk (1UL << SCnSCB_ACTLR_DISOOFP_Pos) /*!< ACTLR: DISOOFP Mask */ + +#define SCnSCB_ACTLR_DISFPCA_Pos 8 /*!< ACTLR: DISFPCA Position */ +#define SCnSCB_ACTLR_DISFPCA_Msk (1UL << SCnSCB_ACTLR_DISFPCA_Pos) /*!< ACTLR: DISFPCA Mask */ + +#define SCnSCB_ACTLR_DISFOLD_Pos 2 /*!< ACTLR: DISFOLD Position */ +#define SCnSCB_ACTLR_DISFOLD_Msk (1UL << SCnSCB_ACTLR_DISFOLD_Pos) /*!< ACTLR: DISFOLD Mask */ + +#define SCnSCB_ACTLR_DISDEFWBUF_Pos 1 /*!< ACTLR: DISDEFWBUF Position */ +#define SCnSCB_ACTLR_DISDEFWBUF_Msk (1UL << SCnSCB_ACTLR_DISDEFWBUF_Pos) /*!< ACTLR: DISDEFWBUF Mask */ + +#define SCnSCB_ACTLR_DISMCYCINT_Pos 0 /*!< ACTLR: DISMCYCINT Position */ +#define SCnSCB_ACTLR_DISMCYCINT_Msk (1UL << SCnSCB_ACTLR_DISMCYCINT_Pos) /*!< ACTLR: DISMCYCINT Mask */ + +/*@} end of group CMSIS_SCnotSCB */ + + +/** \ingroup CMSIS_core_register + \defgroup CMSIS_SysTick System Tick Timer (SysTick) + \brief Type definitions for the System Timer Registers. + @{ + */ + +/** \brief Structure type to access the System Timer (SysTick). + */ +typedef struct +{ + __IO uint32_t CTRL; /*!< Offset: 0x000 (R/W) SysTick Control and Status Register */ + __IO uint32_t LOAD; /*!< Offset: 0x004 (R/W) SysTick Reload Value Register */ + __IO uint32_t VAL; /*!< Offset: 0x008 (R/W) SysTick Current Value Register */ + __I uint32_t CALIB; /*!< Offset: 0x00C (R/ ) SysTick Calibration Register */ +} SysTick_Type; + +/* SysTick Control / Status Register Definitions */ +#define SysTick_CTRL_COUNTFLAG_Pos 16 /*!< SysTick CTRL: COUNTFLAG Position */ +#define SysTick_CTRL_COUNTFLAG_Msk (1UL << SysTick_CTRL_COUNTFLAG_Pos) /*!< SysTick CTRL: COUNTFLAG Mask */ + +#define SysTick_CTRL_CLKSOURCE_Pos 2 /*!< SysTick CTRL: CLKSOURCE Position */ +#define SysTick_CTRL_CLKSOURCE_Msk (1UL << SysTick_CTRL_CLKSOURCE_Pos) /*!< SysTick CTRL: CLKSOURCE Mask */ + +#define SysTick_CTRL_TICKINT_Pos 1 /*!< SysTick CTRL: TICKINT Position */ +#define SysTick_CTRL_TICKINT_Msk (1UL << SysTick_CTRL_TICKINT_Pos) /*!< SysTick CTRL: TICKINT Mask */ + +#define SysTick_CTRL_ENABLE_Pos 0 /*!< SysTick CTRL: ENABLE Position */ +#define SysTick_CTRL_ENABLE_Msk (1UL << SysTick_CTRL_ENABLE_Pos) /*!< SysTick CTRL: ENABLE Mask */ + +/* SysTick Reload Register Definitions */ +#define SysTick_LOAD_RELOAD_Pos 0 /*!< SysTick LOAD: RELOAD Position */ +#define SysTick_LOAD_RELOAD_Msk (0xFFFFFFUL << SysTick_LOAD_RELOAD_Pos) /*!< SysTick LOAD: RELOAD Mask */ + +/* SysTick Current Register Definitions */ +#define SysTick_VAL_CURRENT_Pos 0 /*!< SysTick VAL: CURRENT Position */ +#define SysTick_VAL_CURRENT_Msk (0xFFFFFFUL << SysTick_VAL_CURRENT_Pos) /*!< SysTick VAL: CURRENT Mask */ + +/* SysTick Calibration Register Definitions */ +#define SysTick_CALIB_NOREF_Pos 31 /*!< SysTick CALIB: NOREF Position */ +#define SysTick_CALIB_NOREF_Msk (1UL << SysTick_CALIB_NOREF_Pos) /*!< SysTick CALIB: NOREF Mask */ + +#define SysTick_CALIB_SKEW_Pos 30 /*!< SysTick CALIB: SKEW Position */ +#define SysTick_CALIB_SKEW_Msk (1UL << SysTick_CALIB_SKEW_Pos) /*!< SysTick CALIB: SKEW Mask */ + +#define SysTick_CALIB_TENMS_Pos 0 /*!< SysTick CALIB: TENMS Position */ +#define SysTick_CALIB_TENMS_Msk (0xFFFFFFUL << SysTick_CALIB_TENMS_Pos) /*!< SysTick CALIB: TENMS Mask */ + +/*@} end of group CMSIS_SysTick */ + + +/** \ingroup CMSIS_core_register + \defgroup CMSIS_ITM Instrumentation Trace Macrocell (ITM) + \brief Type definitions for the Instrumentation Trace Macrocell (ITM) + @{ + */ + +/** \brief Structure type to access the Instrumentation Trace Macrocell Register (ITM). + */ +typedef struct +{ + __O union + { + __O uint8_t u8; /*!< Offset: 0x000 ( /W) ITM Stimulus Port 8-bit */ + __O uint16_t u16; /*!< Offset: 0x000 ( /W) ITM Stimulus Port 16-bit */ + __O uint32_t u32; /*!< Offset: 0x000 ( /W) ITM Stimulus Port 32-bit */ + } PORT [32]; /*!< Offset: 0x000 ( /W) ITM Stimulus Port Registers */ + uint32_t RESERVED0[864]; + __IO uint32_t TER; /*!< Offset: 0xE00 (R/W) ITM Trace Enable Register */ + uint32_t RESERVED1[15]; + __IO uint32_t TPR; /*!< Offset: 0xE40 (R/W) ITM Trace Privilege Register */ + uint32_t RESERVED2[15]; + __IO uint32_t TCR; /*!< Offset: 0xE80 (R/W) ITM Trace Control Register */ + uint32_t RESERVED3[29]; + __O uint32_t IWR; /*!< Offset: 0xEF8 ( /W) ITM Integration Write Register */ + __I uint32_t IRR; /*!< Offset: 0xEFC (R/ ) ITM Integration Read Register */ + __IO uint32_t IMCR; /*!< Offset: 0xF00 (R/W) ITM Integration Mode Control Register */ + uint32_t RESERVED4[43]; + __O uint32_t LAR; /*!< Offset: 0xFB0 ( /W) ITM Lock Access Register */ + __I uint32_t LSR; /*!< Offset: 0xFB4 (R/ ) ITM Lock Status Register */ + uint32_t RESERVED5[6]; + __I uint32_t PID4; /*!< Offset: 0xFD0 (R/ ) ITM Peripheral Identification Register #4 */ + __I uint32_t PID5; /*!< Offset: 0xFD4 (R/ ) ITM Peripheral Identification Register #5 */ + __I uint32_t PID6; /*!< Offset: 0xFD8 (R/ ) ITM Peripheral Identification Register #6 */ + __I uint32_t PID7; /*!< Offset: 0xFDC (R/ ) ITM Peripheral Identification Register #7 */ + __I uint32_t PID0; /*!< Offset: 0xFE0 (R/ ) ITM Peripheral Identification Register #0 */ + __I uint32_t PID1; /*!< Offset: 0xFE4 (R/ ) ITM Peripheral Identification Register #1 */ + __I uint32_t PID2; /*!< Offset: 0xFE8 (R/ ) ITM Peripheral Identification Register #2 */ + __I uint32_t PID3; /*!< Offset: 0xFEC (R/ ) ITM Peripheral Identification Register #3 */ + __I uint32_t CID0; /*!< Offset: 0xFF0 (R/ ) ITM Component Identification Register #0 */ + __I uint32_t CID1; /*!< Offset: 0xFF4 (R/ ) ITM Component Identification Register #1 */ + __I uint32_t CID2; /*!< Offset: 0xFF8 (R/ ) ITM Component Identification Register #2 */ + __I uint32_t CID3; /*!< Offset: 0xFFC (R/ ) ITM Component Identification Register #3 */ +} ITM_Type; + +/* ITM Trace Privilege Register Definitions */ +#define ITM_TPR_PRIVMASK_Pos 0 /*!< ITM TPR: PRIVMASK Position */ +#define ITM_TPR_PRIVMASK_Msk (0xFUL << ITM_TPR_PRIVMASK_Pos) /*!< ITM TPR: PRIVMASK Mask */ + +/* ITM Trace Control Register Definitions */ +#define ITM_TCR_BUSY_Pos 23 /*!< ITM TCR: BUSY Position */ +#define ITM_TCR_BUSY_Msk (1UL << ITM_TCR_BUSY_Pos) /*!< ITM TCR: BUSY Mask */ + +#define ITM_TCR_TraceBusID_Pos 16 /*!< ITM TCR: ATBID Position */ +#define ITM_TCR_TraceBusID_Msk (0x7FUL << ITM_TCR_TraceBusID_Pos) /*!< ITM TCR: ATBID Mask */ + +#define ITM_TCR_GTSFREQ_Pos 10 /*!< ITM TCR: Global timestamp frequency Position */ +#define ITM_TCR_GTSFREQ_Msk (3UL << ITM_TCR_GTSFREQ_Pos) /*!< ITM TCR: Global timestamp frequency Mask */ + +#define ITM_TCR_TSPrescale_Pos 8 /*!< ITM TCR: TSPrescale Position */ +#define ITM_TCR_TSPrescale_Msk (3UL << ITM_TCR_TSPrescale_Pos) /*!< ITM TCR: TSPrescale Mask */ + +#define ITM_TCR_SWOENA_Pos 4 /*!< ITM TCR: SWOENA Position */ +#define ITM_TCR_SWOENA_Msk (1UL << ITM_TCR_SWOENA_Pos) /*!< ITM TCR: SWOENA Mask */ + +#define ITM_TCR_DWTENA_Pos 3 /*!< ITM TCR: DWTENA Position */ +#define ITM_TCR_DWTENA_Msk (1UL << ITM_TCR_DWTENA_Pos) /*!< ITM TCR: DWTENA Mask */ + +#define ITM_TCR_SYNCENA_Pos 2 /*!< ITM TCR: SYNCENA Position */ +#define ITM_TCR_SYNCENA_Msk (1UL << ITM_TCR_SYNCENA_Pos) /*!< ITM TCR: SYNCENA Mask */ + +#define ITM_TCR_TSENA_Pos 1 /*!< ITM TCR: TSENA Position */ +#define ITM_TCR_TSENA_Msk (1UL << ITM_TCR_TSENA_Pos) /*!< ITM TCR: TSENA Mask */ + +#define ITM_TCR_ITMENA_Pos 0 /*!< ITM TCR: ITM Enable bit Position */ +#define ITM_TCR_ITMENA_Msk (1UL << ITM_TCR_ITMENA_Pos) /*!< ITM TCR: ITM Enable bit Mask */ + +/* ITM Integration Write Register Definitions */ +#define ITM_IWR_ATVALIDM_Pos 0 /*!< ITM IWR: ATVALIDM Position */ +#define ITM_IWR_ATVALIDM_Msk (1UL << ITM_IWR_ATVALIDM_Pos) /*!< ITM IWR: ATVALIDM Mask */ + +/* ITM Integration Read Register Definitions */ +#define ITM_IRR_ATREADYM_Pos 0 /*!< ITM IRR: ATREADYM Position */ +#define ITM_IRR_ATREADYM_Msk (1UL << ITM_IRR_ATREADYM_Pos) /*!< ITM IRR: ATREADYM Mask */ + +/* ITM Integration Mode Control Register Definitions */ +#define ITM_IMCR_INTEGRATION_Pos 0 /*!< ITM IMCR: INTEGRATION Position */ +#define ITM_IMCR_INTEGRATION_Msk (1UL << ITM_IMCR_INTEGRATION_Pos) /*!< ITM IMCR: INTEGRATION Mask */ + +/* ITM Lock Status Register Definitions */ +#define ITM_LSR_ByteAcc_Pos 2 /*!< ITM LSR: ByteAcc Position */ +#define ITM_LSR_ByteAcc_Msk (1UL << ITM_LSR_ByteAcc_Pos) /*!< ITM LSR: ByteAcc Mask */ + +#define ITM_LSR_Access_Pos 1 /*!< ITM LSR: Access Position */ +#define ITM_LSR_Access_Msk (1UL << ITM_LSR_Access_Pos) /*!< ITM LSR: Access Mask */ + +#define ITM_LSR_Present_Pos 0 /*!< ITM LSR: Present Position */ +#define ITM_LSR_Present_Msk (1UL << ITM_LSR_Present_Pos) /*!< ITM LSR: Present Mask */ + +/*@}*/ /* end of group CMSIS_ITM */ + + +/** \ingroup CMSIS_core_register + \defgroup CMSIS_DWT Data Watchpoint and Trace (DWT) + \brief Type definitions for the Data Watchpoint and Trace (DWT) + @{ + */ + +/** \brief Structure type to access the Data Watchpoint and Trace Register (DWT). + */ +typedef struct +{ + __IO uint32_t CTRL; /*!< Offset: 0x000 (R/W) Control Register */ + __IO uint32_t CYCCNT; /*!< Offset: 0x004 (R/W) Cycle Count Register */ + __IO uint32_t CPICNT; /*!< Offset: 0x008 (R/W) CPI Count Register */ + __IO uint32_t EXCCNT; /*!< Offset: 0x00C (R/W) Exception Overhead Count Register */ + __IO uint32_t SLEEPCNT; /*!< Offset: 0x010 (R/W) Sleep Count Register */ + __IO uint32_t LSUCNT; /*!< Offset: 0x014 (R/W) LSU Count Register */ + __IO uint32_t FOLDCNT; /*!< Offset: 0x018 (R/W) Folded-instruction Count Register */ + __I uint32_t PCSR; /*!< Offset: 0x01C (R/ ) Program Counter Sample Register */ + __IO uint32_t COMP0; /*!< Offset: 0x020 (R/W) Comparator Register 0 */ + __IO uint32_t MASK0; /*!< Offset: 0x024 (R/W) Mask Register 0 */ + __IO uint32_t FUNCTION0; /*!< Offset: 0x028 (R/W) Function Register 0 */ + uint32_t RESERVED0[1]; + __IO uint32_t COMP1; /*!< Offset: 0x030 (R/W) Comparator Register 1 */ + __IO uint32_t MASK1; /*!< Offset: 0x034 (R/W) Mask Register 1 */ + __IO uint32_t FUNCTION1; /*!< Offset: 0x038 (R/W) Function Register 1 */ + uint32_t RESERVED1[1]; + __IO uint32_t COMP2; /*!< Offset: 0x040 (R/W) Comparator Register 2 */ + __IO uint32_t MASK2; /*!< Offset: 0x044 (R/W) Mask Register 2 */ + __IO uint32_t FUNCTION2; /*!< Offset: 0x048 (R/W) Function Register 2 */ + uint32_t RESERVED2[1]; + __IO uint32_t COMP3; /*!< Offset: 0x050 (R/W) Comparator Register 3 */ + __IO uint32_t MASK3; /*!< Offset: 0x054 (R/W) Mask Register 3 */ + __IO uint32_t FUNCTION3; /*!< Offset: 0x058 (R/W) Function Register 3 */ +} DWT_Type; + +/* DWT Control Register Definitions */ +#define DWT_CTRL_NUMCOMP_Pos 28 /*!< DWT CTRL: NUMCOMP Position */ +#define DWT_CTRL_NUMCOMP_Msk (0xFUL << DWT_CTRL_NUMCOMP_Pos) /*!< DWT CTRL: NUMCOMP Mask */ + +#define DWT_CTRL_NOTRCPKT_Pos 27 /*!< DWT CTRL: NOTRCPKT Position */ +#define DWT_CTRL_NOTRCPKT_Msk (0x1UL << DWT_CTRL_NOTRCPKT_Pos) /*!< DWT CTRL: NOTRCPKT Mask */ + +#define DWT_CTRL_NOEXTTRIG_Pos 26 /*!< DWT CTRL: NOEXTTRIG Position */ +#define DWT_CTRL_NOEXTTRIG_Msk (0x1UL << DWT_CTRL_NOEXTTRIG_Pos) /*!< DWT CTRL: NOEXTTRIG Mask */ + +#define DWT_CTRL_NOCYCCNT_Pos 25 /*!< DWT CTRL: NOCYCCNT Position */ +#define DWT_CTRL_NOCYCCNT_Msk (0x1UL << DWT_CTRL_NOCYCCNT_Pos) /*!< DWT CTRL: NOCYCCNT Mask */ + +#define DWT_CTRL_NOPRFCNT_Pos 24 /*!< DWT CTRL: NOPRFCNT Position */ +#define DWT_CTRL_NOPRFCNT_Msk (0x1UL << DWT_CTRL_NOPRFCNT_Pos) /*!< DWT CTRL: NOPRFCNT Mask */ + +#define DWT_CTRL_CYCEVTENA_Pos 22 /*!< DWT CTRL: CYCEVTENA Position */ +#define DWT_CTRL_CYCEVTENA_Msk (0x1UL << DWT_CTRL_CYCEVTENA_Pos) /*!< DWT CTRL: CYCEVTENA Mask */ + +#define DWT_CTRL_FOLDEVTENA_Pos 21 /*!< DWT CTRL: FOLDEVTENA Position */ +#define DWT_CTRL_FOLDEVTENA_Msk (0x1UL << DWT_CTRL_FOLDEVTENA_Pos) /*!< DWT CTRL: FOLDEVTENA Mask */ + +#define DWT_CTRL_LSUEVTENA_Pos 20 /*!< DWT CTRL: LSUEVTENA Position */ +#define DWT_CTRL_LSUEVTENA_Msk (0x1UL << DWT_CTRL_LSUEVTENA_Pos) /*!< DWT CTRL: LSUEVTENA Mask */ + +#define DWT_CTRL_SLEEPEVTENA_Pos 19 /*!< DWT CTRL: SLEEPEVTENA Position */ +#define DWT_CTRL_SLEEPEVTENA_Msk (0x1UL << DWT_CTRL_SLEEPEVTENA_Pos) /*!< DWT CTRL: SLEEPEVTENA Mask */ + +#define DWT_CTRL_EXCEVTENA_Pos 18 /*!< DWT CTRL: EXCEVTENA Position */ +#define DWT_CTRL_EXCEVTENA_Msk (0x1UL << DWT_CTRL_EXCEVTENA_Pos) /*!< DWT CTRL: EXCEVTENA Mask */ + +#define DWT_CTRL_CPIEVTENA_Pos 17 /*!< DWT CTRL: CPIEVTENA Position */ +#define DWT_CTRL_CPIEVTENA_Msk (0x1UL << DWT_CTRL_CPIEVTENA_Pos) /*!< DWT CTRL: CPIEVTENA Mask */ + +#define DWT_CTRL_EXCTRCENA_Pos 16 /*!< DWT CTRL: EXCTRCENA Position */ +#define DWT_CTRL_EXCTRCENA_Msk (0x1UL << DWT_CTRL_EXCTRCENA_Pos) /*!< DWT CTRL: EXCTRCENA Mask */ + +#define DWT_CTRL_PCSAMPLENA_Pos 12 /*!< DWT CTRL: PCSAMPLENA Position */ +#define DWT_CTRL_PCSAMPLENA_Msk (0x1UL << DWT_CTRL_PCSAMPLENA_Pos) /*!< DWT CTRL: PCSAMPLENA Mask */ + +#define DWT_CTRL_SYNCTAP_Pos 10 /*!< DWT CTRL: SYNCTAP Position */ +#define DWT_CTRL_SYNCTAP_Msk (0x3UL << DWT_CTRL_SYNCTAP_Pos) /*!< DWT CTRL: SYNCTAP Mask */ + +#define DWT_CTRL_CYCTAP_Pos 9 /*!< DWT CTRL: CYCTAP Position */ +#define DWT_CTRL_CYCTAP_Msk (0x1UL << DWT_CTRL_CYCTAP_Pos) /*!< DWT CTRL: CYCTAP Mask */ + +#define DWT_CTRL_POSTINIT_Pos 5 /*!< DWT CTRL: POSTINIT Position */ +#define DWT_CTRL_POSTINIT_Msk (0xFUL << DWT_CTRL_POSTINIT_Pos) /*!< DWT CTRL: POSTINIT Mask */ + +#define DWT_CTRL_POSTPRESET_Pos 1 /*!< DWT CTRL: POSTPRESET Position */ +#define DWT_CTRL_POSTPRESET_Msk (0xFUL << DWT_CTRL_POSTPRESET_Pos) /*!< DWT CTRL: POSTPRESET Mask */ + +#define DWT_CTRL_CYCCNTENA_Pos 0 /*!< DWT CTRL: CYCCNTENA Position */ +#define DWT_CTRL_CYCCNTENA_Msk (0x1UL << DWT_CTRL_CYCCNTENA_Pos) /*!< DWT CTRL: CYCCNTENA Mask */ + +/* DWT CPI Count Register Definitions */ +#define DWT_CPICNT_CPICNT_Pos 0 /*!< DWT CPICNT: CPICNT Position */ +#define DWT_CPICNT_CPICNT_Msk (0xFFUL << DWT_CPICNT_CPICNT_Pos) /*!< DWT CPICNT: CPICNT Mask */ + +/* DWT Exception Overhead Count Register Definitions */ +#define DWT_EXCCNT_EXCCNT_Pos 0 /*!< DWT EXCCNT: EXCCNT Position */ +#define DWT_EXCCNT_EXCCNT_Msk (0xFFUL << DWT_EXCCNT_EXCCNT_Pos) /*!< DWT EXCCNT: EXCCNT Mask */ + +/* DWT Sleep Count Register Definitions */ +#define DWT_SLEEPCNT_SLEEPCNT_Pos 0 /*!< DWT SLEEPCNT: SLEEPCNT Position */ +#define DWT_SLEEPCNT_SLEEPCNT_Msk (0xFFUL << DWT_SLEEPCNT_SLEEPCNT_Pos) /*!< DWT SLEEPCNT: SLEEPCNT Mask */ + +/* DWT LSU Count Register Definitions */ +#define DWT_LSUCNT_LSUCNT_Pos 0 /*!< DWT LSUCNT: LSUCNT Position */ +#define DWT_LSUCNT_LSUCNT_Msk (0xFFUL << DWT_LSUCNT_LSUCNT_Pos) /*!< DWT LSUCNT: LSUCNT Mask */ + +/* DWT Folded-instruction Count Register Definitions */ +#define DWT_FOLDCNT_FOLDCNT_Pos 0 /*!< DWT FOLDCNT: FOLDCNT Position */ +#define DWT_FOLDCNT_FOLDCNT_Msk (0xFFUL << DWT_FOLDCNT_FOLDCNT_Pos) /*!< DWT FOLDCNT: FOLDCNT Mask */ + +/* DWT Comparator Mask Register Definitions */ +#define DWT_MASK_MASK_Pos 0 /*!< DWT MASK: MASK Position */ +#define DWT_MASK_MASK_Msk (0x1FUL << DWT_MASK_MASK_Pos) /*!< DWT MASK: MASK Mask */ + +/* DWT Comparator Function Register Definitions */ +#define DWT_FUNCTION_MATCHED_Pos 24 /*!< DWT FUNCTION: MATCHED Position */ +#define DWT_FUNCTION_MATCHED_Msk (0x1UL << DWT_FUNCTION_MATCHED_Pos) /*!< DWT FUNCTION: MATCHED Mask */ + +#define DWT_FUNCTION_DATAVADDR1_Pos 16 /*!< DWT FUNCTION: DATAVADDR1 Position */ +#define DWT_FUNCTION_DATAVADDR1_Msk (0xFUL << DWT_FUNCTION_DATAVADDR1_Pos) /*!< DWT FUNCTION: DATAVADDR1 Mask */ + +#define DWT_FUNCTION_DATAVADDR0_Pos 12 /*!< DWT FUNCTION: DATAVADDR0 Position */ +#define DWT_FUNCTION_DATAVADDR0_Msk (0xFUL << DWT_FUNCTION_DATAVADDR0_Pos) /*!< DWT FUNCTION: DATAVADDR0 Mask */ + +#define DWT_FUNCTION_DATAVSIZE_Pos 10 /*!< DWT FUNCTION: DATAVSIZE Position */ +#define DWT_FUNCTION_DATAVSIZE_Msk (0x3UL << DWT_FUNCTION_DATAVSIZE_Pos) /*!< DWT FUNCTION: DATAVSIZE Mask */ + +#define DWT_FUNCTION_LNK1ENA_Pos 9 /*!< DWT FUNCTION: LNK1ENA Position */ +#define DWT_FUNCTION_LNK1ENA_Msk (0x1UL << DWT_FUNCTION_LNK1ENA_Pos) /*!< DWT FUNCTION: LNK1ENA Mask */ + +#define DWT_FUNCTION_DATAVMATCH_Pos 8 /*!< DWT FUNCTION: DATAVMATCH Position */ +#define DWT_FUNCTION_DATAVMATCH_Msk (0x1UL << DWT_FUNCTION_DATAVMATCH_Pos) /*!< DWT FUNCTION: DATAVMATCH Mask */ + +#define DWT_FUNCTION_CYCMATCH_Pos 7 /*!< DWT FUNCTION: CYCMATCH Position */ +#define DWT_FUNCTION_CYCMATCH_Msk (0x1UL << DWT_FUNCTION_CYCMATCH_Pos) /*!< DWT FUNCTION: CYCMATCH Mask */ + +#define DWT_FUNCTION_EMITRANGE_Pos 5 /*!< DWT FUNCTION: EMITRANGE Position */ +#define DWT_FUNCTION_EMITRANGE_Msk (0x1UL << DWT_FUNCTION_EMITRANGE_Pos) /*!< DWT FUNCTION: EMITRANGE Mask */ + +#define DWT_FUNCTION_FUNCTION_Pos 0 /*!< DWT FUNCTION: FUNCTION Position */ +#define DWT_FUNCTION_FUNCTION_Msk (0xFUL << DWT_FUNCTION_FUNCTION_Pos) /*!< DWT FUNCTION: FUNCTION Mask */ + +/*@}*/ /* end of group CMSIS_DWT */ + + +/** \ingroup CMSIS_core_register + \defgroup CMSIS_TPI Trace Port Interface (TPI) + \brief Type definitions for the Trace Port Interface (TPI) + @{ + */ + +/** \brief Structure type to access the Trace Port Interface Register (TPI). + */ +typedef struct +{ + __IO uint32_t SSPSR; /*!< Offset: 0x000 (R/ ) Supported Parallel Port Size Register */ + __IO uint32_t CSPSR; /*!< Offset: 0x004 (R/W) Current Parallel Port Size Register */ + uint32_t RESERVED0[2]; + __IO uint32_t ACPR; /*!< Offset: 0x010 (R/W) Asynchronous Clock Prescaler Register */ + uint32_t RESERVED1[55]; + __IO uint32_t SPPR; /*!< Offset: 0x0F0 (R/W) Selected Pin Protocol Register */ + uint32_t RESERVED2[131]; + __I uint32_t FFSR; /*!< Offset: 0x300 (R/ ) Formatter and Flush Status Register */ + __IO uint32_t FFCR; /*!< Offset: 0x304 (R/W) Formatter and Flush Control Register */ + __I uint32_t FSCR; /*!< Offset: 0x308 (R/ ) Formatter Synchronization Counter Register */ + uint32_t RESERVED3[759]; + __I uint32_t TRIGGER; /*!< Offset: 0xEE8 (R/ ) TRIGGER */ + __I uint32_t FIFO0; /*!< Offset: 0xEEC (R/ ) Integration ETM Data */ + __I uint32_t ITATBCTR2; /*!< Offset: 0xEF0 (R/ ) ITATBCTR2 */ + uint32_t RESERVED4[1]; + __I uint32_t ITATBCTR0; /*!< Offset: 0xEF8 (R/ ) ITATBCTR0 */ + __I uint32_t FIFO1; /*!< Offset: 0xEFC (R/ ) Integration ITM Data */ + __IO uint32_t ITCTRL; /*!< Offset: 0xF00 (R/W) Integration Mode Control */ + uint32_t RESERVED5[39]; + __IO uint32_t CLAIMSET; /*!< Offset: 0xFA0 (R/W) Claim tag set */ + __IO uint32_t CLAIMCLR; /*!< Offset: 0xFA4 (R/W) Claim tag clear */ + uint32_t RESERVED7[8]; + __I uint32_t DEVID; /*!< Offset: 0xFC8 (R/ ) TPIU_DEVID */ + __I uint32_t DEVTYPE; /*!< Offset: 0xFCC (R/ ) TPIU_DEVTYPE */ +} TPI_Type; + +/* TPI Asynchronous Clock Prescaler Register Definitions */ +#define TPI_ACPR_PRESCALER_Pos 0 /*!< TPI ACPR: PRESCALER Position */ +#define TPI_ACPR_PRESCALER_Msk (0x1FFFUL << TPI_ACPR_PRESCALER_Pos) /*!< TPI ACPR: PRESCALER Mask */ + +/* TPI Selected Pin Protocol Register Definitions */ +#define TPI_SPPR_TXMODE_Pos 0 /*!< TPI SPPR: TXMODE Position */ +#define TPI_SPPR_TXMODE_Msk (0x3UL << TPI_SPPR_TXMODE_Pos) /*!< TPI SPPR: TXMODE Mask */ + +/* TPI Formatter and Flush Status Register Definitions */ +#define TPI_FFSR_FtNonStop_Pos 3 /*!< TPI FFSR: FtNonStop Position */ +#define TPI_FFSR_FtNonStop_Msk (0x1UL << TPI_FFSR_FtNonStop_Pos) /*!< TPI FFSR: FtNonStop Mask */ + +#define TPI_FFSR_TCPresent_Pos 2 /*!< TPI FFSR: TCPresent Position */ +#define TPI_FFSR_TCPresent_Msk (0x1UL << TPI_FFSR_TCPresent_Pos) /*!< TPI FFSR: TCPresent Mask */ + +#define TPI_FFSR_FtStopped_Pos 1 /*!< TPI FFSR: FtStopped Position */ +#define TPI_FFSR_FtStopped_Msk (0x1UL << TPI_FFSR_FtStopped_Pos) /*!< TPI FFSR: FtStopped Mask */ + +#define TPI_FFSR_FlInProg_Pos 0 /*!< TPI FFSR: FlInProg Position */ +#define TPI_FFSR_FlInProg_Msk (0x1UL << TPI_FFSR_FlInProg_Pos) /*!< TPI FFSR: FlInProg Mask */ + +/* TPI Formatter and Flush Control Register Definitions */ +#define TPI_FFCR_TrigIn_Pos 8 /*!< TPI FFCR: TrigIn Position */ +#define TPI_FFCR_TrigIn_Msk (0x1UL << TPI_FFCR_TrigIn_Pos) /*!< TPI FFCR: TrigIn Mask */ + +#define TPI_FFCR_EnFCont_Pos 1 /*!< TPI FFCR: EnFCont Position */ +#define TPI_FFCR_EnFCont_Msk (0x1UL << TPI_FFCR_EnFCont_Pos) /*!< TPI FFCR: EnFCont Mask */ + +/* TPI TRIGGER Register Definitions */ +#define TPI_TRIGGER_TRIGGER_Pos 0 /*!< TPI TRIGGER: TRIGGER Position */ +#define TPI_TRIGGER_TRIGGER_Msk (0x1UL << TPI_TRIGGER_TRIGGER_Pos) /*!< TPI TRIGGER: TRIGGER Mask */ + +/* TPI Integration ETM Data Register Definitions (FIFO0) */ +#define TPI_FIFO0_ITM_ATVALID_Pos 29 /*!< TPI FIFO0: ITM_ATVALID Position */ +#define TPI_FIFO0_ITM_ATVALID_Msk (0x3UL << TPI_FIFO0_ITM_ATVALID_Pos) /*!< TPI FIFO0: ITM_ATVALID Mask */ + +#define TPI_FIFO0_ITM_bytecount_Pos 27 /*!< TPI FIFO0: ITM_bytecount Position */ +#define TPI_FIFO0_ITM_bytecount_Msk (0x3UL << TPI_FIFO0_ITM_bytecount_Pos) /*!< TPI FIFO0: ITM_bytecount Mask */ + +#define TPI_FIFO0_ETM_ATVALID_Pos 26 /*!< TPI FIFO0: ETM_ATVALID Position */ +#define TPI_FIFO0_ETM_ATVALID_Msk (0x3UL << TPI_FIFO0_ETM_ATVALID_Pos) /*!< TPI FIFO0: ETM_ATVALID Mask */ + +#define TPI_FIFO0_ETM_bytecount_Pos 24 /*!< TPI FIFO0: ETM_bytecount Position */ +#define TPI_FIFO0_ETM_bytecount_Msk (0x3UL << TPI_FIFO0_ETM_bytecount_Pos) /*!< TPI FIFO0: ETM_bytecount Mask */ + +#define TPI_FIFO0_ETM2_Pos 16 /*!< TPI FIFO0: ETM2 Position */ +#define TPI_FIFO0_ETM2_Msk (0xFFUL << TPI_FIFO0_ETM2_Pos) /*!< TPI FIFO0: ETM2 Mask */ + +#define TPI_FIFO0_ETM1_Pos 8 /*!< TPI FIFO0: ETM1 Position */ +#define TPI_FIFO0_ETM1_Msk (0xFFUL << TPI_FIFO0_ETM1_Pos) /*!< TPI FIFO0: ETM1 Mask */ + +#define TPI_FIFO0_ETM0_Pos 0 /*!< TPI FIFO0: ETM0 Position */ +#define TPI_FIFO0_ETM0_Msk (0xFFUL << TPI_FIFO0_ETM0_Pos) /*!< TPI FIFO0: ETM0 Mask */ + +/* TPI ITATBCTR2 Register Definitions */ +#define TPI_ITATBCTR2_ATREADY_Pos 0 /*!< TPI ITATBCTR2: ATREADY Position */ +#define TPI_ITATBCTR2_ATREADY_Msk (0x1UL << TPI_ITATBCTR2_ATREADY_Pos) /*!< TPI ITATBCTR2: ATREADY Mask */ + +/* TPI Integration ITM Data Register Definitions (FIFO1) */ +#define TPI_FIFO1_ITM_ATVALID_Pos 29 /*!< TPI FIFO1: ITM_ATVALID Position */ +#define TPI_FIFO1_ITM_ATVALID_Msk (0x3UL << TPI_FIFO1_ITM_ATVALID_Pos) /*!< TPI FIFO1: ITM_ATVALID Mask */ + +#define TPI_FIFO1_ITM_bytecount_Pos 27 /*!< TPI FIFO1: ITM_bytecount Position */ +#define TPI_FIFO1_ITM_bytecount_Msk (0x3UL << TPI_FIFO1_ITM_bytecount_Pos) /*!< TPI FIFO1: ITM_bytecount Mask */ + +#define TPI_FIFO1_ETM_ATVALID_Pos 26 /*!< TPI FIFO1: ETM_ATVALID Position */ +#define TPI_FIFO1_ETM_ATVALID_Msk (0x3UL << TPI_FIFO1_ETM_ATVALID_Pos) /*!< TPI FIFO1: ETM_ATVALID Mask */ + +#define TPI_FIFO1_ETM_bytecount_Pos 24 /*!< TPI FIFO1: ETM_bytecount Position */ +#define TPI_FIFO1_ETM_bytecount_Msk (0x3UL << TPI_FIFO1_ETM_bytecount_Pos) /*!< TPI FIFO1: ETM_bytecount Mask */ + +#define TPI_FIFO1_ITM2_Pos 16 /*!< TPI FIFO1: ITM2 Position */ +#define TPI_FIFO1_ITM2_Msk (0xFFUL << TPI_FIFO1_ITM2_Pos) /*!< TPI FIFO1: ITM2 Mask */ + +#define TPI_FIFO1_ITM1_Pos 8 /*!< TPI FIFO1: ITM1 Position */ +#define TPI_FIFO1_ITM1_Msk (0xFFUL << TPI_FIFO1_ITM1_Pos) /*!< TPI FIFO1: ITM1 Mask */ + +#define TPI_FIFO1_ITM0_Pos 0 /*!< TPI FIFO1: ITM0 Position */ +#define TPI_FIFO1_ITM0_Msk (0xFFUL << TPI_FIFO1_ITM0_Pos) /*!< TPI FIFO1: ITM0 Mask */ + +/* TPI ITATBCTR0 Register Definitions */ +#define TPI_ITATBCTR0_ATREADY_Pos 0 /*!< TPI ITATBCTR0: ATREADY Position */ +#define TPI_ITATBCTR0_ATREADY_Msk (0x1UL << TPI_ITATBCTR0_ATREADY_Pos) /*!< TPI ITATBCTR0: ATREADY Mask */ + +/* TPI Integration Mode Control Register Definitions */ +#define TPI_ITCTRL_Mode_Pos 0 /*!< TPI ITCTRL: Mode Position */ +#define TPI_ITCTRL_Mode_Msk (0x1UL << TPI_ITCTRL_Mode_Pos) /*!< TPI ITCTRL: Mode Mask */ + +/* TPI DEVID Register Definitions */ +#define TPI_DEVID_NRZVALID_Pos 11 /*!< TPI DEVID: NRZVALID Position */ +#define TPI_DEVID_NRZVALID_Msk (0x1UL << TPI_DEVID_NRZVALID_Pos) /*!< TPI DEVID: NRZVALID Mask */ + +#define TPI_DEVID_MANCVALID_Pos 10 /*!< TPI DEVID: MANCVALID Position */ +#define TPI_DEVID_MANCVALID_Msk (0x1UL << TPI_DEVID_MANCVALID_Pos) /*!< TPI DEVID: MANCVALID Mask */ + +#define TPI_DEVID_PTINVALID_Pos 9 /*!< TPI DEVID: PTINVALID Position */ +#define TPI_DEVID_PTINVALID_Msk (0x1UL << TPI_DEVID_PTINVALID_Pos) /*!< TPI DEVID: PTINVALID Mask */ + +#define TPI_DEVID_MinBufSz_Pos 6 /*!< TPI DEVID: MinBufSz Position */ +#define TPI_DEVID_MinBufSz_Msk (0x7UL << TPI_DEVID_MinBufSz_Pos) /*!< TPI DEVID: MinBufSz Mask */ + +#define TPI_DEVID_AsynClkIn_Pos 5 /*!< TPI DEVID: AsynClkIn Position */ +#define TPI_DEVID_AsynClkIn_Msk (0x1UL << TPI_DEVID_AsynClkIn_Pos) /*!< TPI DEVID: AsynClkIn Mask */ + +#define TPI_DEVID_NrTraceInput_Pos 0 /*!< TPI DEVID: NrTraceInput Position */ +#define TPI_DEVID_NrTraceInput_Msk (0x1FUL << TPI_DEVID_NrTraceInput_Pos) /*!< TPI DEVID: NrTraceInput Mask */ + +/* TPI DEVTYPE Register Definitions */ +#define TPI_DEVTYPE_SubType_Pos 0 /*!< TPI DEVTYPE: SubType Position */ +#define TPI_DEVTYPE_SubType_Msk (0xFUL << TPI_DEVTYPE_SubType_Pos) /*!< TPI DEVTYPE: SubType Mask */ + +#define TPI_DEVTYPE_MajorType_Pos 4 /*!< TPI DEVTYPE: MajorType Position */ +#define TPI_DEVTYPE_MajorType_Msk (0xFUL << TPI_DEVTYPE_MajorType_Pos) /*!< TPI DEVTYPE: MajorType Mask */ + +/*@}*/ /* end of group CMSIS_TPI */ + + +#if (__MPU_PRESENT == 1) +/** \ingroup CMSIS_core_register + \defgroup CMSIS_MPU Memory Protection Unit (MPU) + \brief Type definitions for the Memory Protection Unit (MPU) + @{ + */ + +/** \brief Structure type to access the Memory Protection Unit (MPU). + */ +typedef struct +{ + __I uint32_t TYPE; /*!< Offset: 0x000 (R/ ) MPU Type Register */ + __IO uint32_t CTRL; /*!< Offset: 0x004 (R/W) MPU Control Register */ + __IO uint32_t RNR; /*!< Offset: 0x008 (R/W) MPU Region RNRber Register */ + __IO uint32_t RBAR; /*!< Offset: 0x00C (R/W) MPU Region Base Address Register */ + __IO uint32_t RASR; /*!< Offset: 0x010 (R/W) MPU Region Attribute and Size Register */ + __IO uint32_t RBAR_A1; /*!< Offset: 0x014 (R/W) MPU Alias 1 Region Base Address Register */ + __IO uint32_t RASR_A1; /*!< Offset: 0x018 (R/W) MPU Alias 1 Region Attribute and Size Register */ + __IO uint32_t RBAR_A2; /*!< Offset: 0x01C (R/W) MPU Alias 2 Region Base Address Register */ + __IO uint32_t RASR_A2; /*!< Offset: 0x020 (R/W) MPU Alias 2 Region Attribute and Size Register */ + __IO uint32_t RBAR_A3; /*!< Offset: 0x024 (R/W) MPU Alias 3 Region Base Address Register */ + __IO uint32_t RASR_A3; /*!< Offset: 0x028 (R/W) MPU Alias 3 Region Attribute and Size Register */ +} MPU_Type; + +/* MPU Type Register */ +#define MPU_TYPE_IREGION_Pos 16 /*!< MPU TYPE: IREGION Position */ +#define MPU_TYPE_IREGION_Msk (0xFFUL << MPU_TYPE_IREGION_Pos) /*!< MPU TYPE: IREGION Mask */ + +#define MPU_TYPE_DREGION_Pos 8 /*!< MPU TYPE: DREGION Position */ +#define MPU_TYPE_DREGION_Msk (0xFFUL << MPU_TYPE_DREGION_Pos) /*!< MPU TYPE: DREGION Mask */ + +#define MPU_TYPE_SEPARATE_Pos 0 /*!< MPU TYPE: SEPARATE Position */ +#define MPU_TYPE_SEPARATE_Msk (1UL << MPU_TYPE_SEPARATE_Pos) /*!< MPU TYPE: SEPARATE Mask */ + +/* MPU Control Register */ +#define MPU_CTRL_PRIVDEFENA_Pos 2 /*!< MPU CTRL: PRIVDEFENA Position */ +#define MPU_CTRL_PRIVDEFENA_Msk (1UL << MPU_CTRL_PRIVDEFENA_Pos) /*!< MPU CTRL: PRIVDEFENA Mask */ + +#define MPU_CTRL_HFNMIENA_Pos 1 /*!< MPU CTRL: HFNMIENA Position */ +#define MPU_CTRL_HFNMIENA_Msk (1UL << MPU_CTRL_HFNMIENA_Pos) /*!< MPU CTRL: HFNMIENA Mask */ + +#define MPU_CTRL_ENABLE_Pos 0 /*!< MPU CTRL: ENABLE Position */ +#define MPU_CTRL_ENABLE_Msk (1UL << MPU_CTRL_ENABLE_Pos) /*!< MPU CTRL: ENABLE Mask */ + +/* MPU Region Number Register */ +#define MPU_RNR_REGION_Pos 0 /*!< MPU RNR: REGION Position */ +#define MPU_RNR_REGION_Msk (0xFFUL << MPU_RNR_REGION_Pos) /*!< MPU RNR: REGION Mask */ + +/* MPU Region Base Address Register */ +#define MPU_RBAR_ADDR_Pos 5 /*!< MPU RBAR: ADDR Position */ +#define MPU_RBAR_ADDR_Msk (0x7FFFFFFUL << MPU_RBAR_ADDR_Pos) /*!< MPU RBAR: ADDR Mask */ + +#define MPU_RBAR_VALID_Pos 4 /*!< MPU RBAR: VALID Position */ +#define MPU_RBAR_VALID_Msk (1UL << MPU_RBAR_VALID_Pos) /*!< MPU RBAR: VALID Mask */ + +#define MPU_RBAR_REGION_Pos 0 /*!< MPU RBAR: REGION Position */ +#define MPU_RBAR_REGION_Msk (0xFUL << MPU_RBAR_REGION_Pos) /*!< MPU RBAR: REGION Mask */ + +/* MPU Region Attribute and Size Register */ +#define MPU_RASR_ATTRS_Pos 16 /*!< MPU RASR: MPU Region Attribute field Position */ +#define MPU_RASR_ATTRS_Msk (0xFFFFUL << MPU_RASR_ATTRS_Pos) /*!< MPU RASR: MPU Region Attribute field Mask */ + +#define MPU_RASR_XN_Pos 28 /*!< MPU RASR: ATTRS.XN Position */ +#define MPU_RASR_XN_Msk (1UL << MPU_RASR_XN_Pos) /*!< MPU RASR: ATTRS.XN Mask */ + +#define MPU_RASR_AP_Pos 24 /*!< MPU RASR: ATTRS.AP Position */ +#define MPU_RASR_AP_Msk (0x7UL << MPU_RASR_AP_Pos) /*!< MPU RASR: ATTRS.AP Mask */ + +#define MPU_RASR_TEX_Pos 19 /*!< MPU RASR: ATTRS.TEX Position */ +#define MPU_RASR_TEX_Msk (0x7UL << MPU_RASR_TEX_Pos) /*!< MPU RASR: ATTRS.TEX Mask */ + +#define MPU_RASR_S_Pos 18 /*!< MPU RASR: ATTRS.S Position */ +#define MPU_RASR_S_Msk (1UL << MPU_RASR_S_Pos) /*!< MPU RASR: ATTRS.S Mask */ + +#define MPU_RASR_C_Pos 17 /*!< MPU RASR: ATTRS.C Position */ +#define MPU_RASR_C_Msk (1UL << MPU_RASR_C_Pos) /*!< MPU RASR: ATTRS.C Mask */ + +#define MPU_RASR_B_Pos 16 /*!< MPU RASR: ATTRS.B Position */ +#define MPU_RASR_B_Msk (1UL << MPU_RASR_B_Pos) /*!< MPU RASR: ATTRS.B Mask */ + +#define MPU_RASR_SRD_Pos 8 /*!< MPU RASR: Sub-Region Disable Position */ +#define MPU_RASR_SRD_Msk (0xFFUL << MPU_RASR_SRD_Pos) /*!< MPU RASR: Sub-Region Disable Mask */ + +#define MPU_RASR_SIZE_Pos 1 /*!< MPU RASR: Region Size Field Position */ +#define MPU_RASR_SIZE_Msk (0x1FUL << MPU_RASR_SIZE_Pos) /*!< MPU RASR: Region Size Field Mask */ + +#define MPU_RASR_ENABLE_Pos 0 /*!< MPU RASR: Region enable bit Position */ +#define MPU_RASR_ENABLE_Msk (1UL << MPU_RASR_ENABLE_Pos) /*!< MPU RASR: Region enable bit Disable Mask */ + +/*@} end of group CMSIS_MPU */ +#endif + + +#if (__FPU_PRESENT == 1) +/** \ingroup CMSIS_core_register + \defgroup CMSIS_FPU Floating Point Unit (FPU) + \brief Type definitions for the Floating Point Unit (FPU) + @{ + */ + +/** \brief Structure type to access the Floating Point Unit (FPU). + */ +typedef struct +{ + uint32_t RESERVED0[1]; + __IO uint32_t FPCCR; /*!< Offset: 0x004 (R/W) Floating-Point Context Control Register */ + __IO uint32_t FPCAR; /*!< Offset: 0x008 (R/W) Floating-Point Context Address Register */ + __IO uint32_t FPDSCR; /*!< Offset: 0x00C (R/W) Floating-Point Default Status Control Register */ + __I uint32_t MVFR0; /*!< Offset: 0x010 (R/ ) Media and FP Feature Register 0 */ + __I uint32_t MVFR1; /*!< Offset: 0x014 (R/ ) Media and FP Feature Register 1 */ +} FPU_Type; + +/* Floating-Point Context Control Register */ +#define FPU_FPCCR_ASPEN_Pos 31 /*!< FPCCR: ASPEN bit Position */ +#define FPU_FPCCR_ASPEN_Msk (1UL << FPU_FPCCR_ASPEN_Pos) /*!< FPCCR: ASPEN bit Mask */ + +#define FPU_FPCCR_LSPEN_Pos 30 /*!< FPCCR: LSPEN Position */ +#define FPU_FPCCR_LSPEN_Msk (1UL << FPU_FPCCR_LSPEN_Pos) /*!< FPCCR: LSPEN bit Mask */ + +#define FPU_FPCCR_MONRDY_Pos 8 /*!< FPCCR: MONRDY Position */ +#define FPU_FPCCR_MONRDY_Msk (1UL << FPU_FPCCR_MONRDY_Pos) /*!< FPCCR: MONRDY bit Mask */ + +#define FPU_FPCCR_BFRDY_Pos 6 /*!< FPCCR: BFRDY Position */ +#define FPU_FPCCR_BFRDY_Msk (1UL << FPU_FPCCR_BFRDY_Pos) /*!< FPCCR: BFRDY bit Mask */ + +#define FPU_FPCCR_MMRDY_Pos 5 /*!< FPCCR: MMRDY Position */ +#define FPU_FPCCR_MMRDY_Msk (1UL << FPU_FPCCR_MMRDY_Pos) /*!< FPCCR: MMRDY bit Mask */ + +#define FPU_FPCCR_HFRDY_Pos 4 /*!< FPCCR: HFRDY Position */ +#define FPU_FPCCR_HFRDY_Msk (1UL << FPU_FPCCR_HFRDY_Pos) /*!< FPCCR: HFRDY bit Mask */ + +#define FPU_FPCCR_THREAD_Pos 3 /*!< FPCCR: processor mode bit Position */ +#define FPU_FPCCR_THREAD_Msk (1UL << FPU_FPCCR_THREAD_Pos) /*!< FPCCR: processor mode active bit Mask */ + +#define FPU_FPCCR_USER_Pos 1 /*!< FPCCR: privilege level bit Position */ +#define FPU_FPCCR_USER_Msk (1UL << FPU_FPCCR_USER_Pos) /*!< FPCCR: privilege level bit Mask */ + +#define FPU_FPCCR_LSPACT_Pos 0 /*!< FPCCR: Lazy state preservation active bit Position */ +#define FPU_FPCCR_LSPACT_Msk (1UL << FPU_FPCCR_LSPACT_Pos) /*!< FPCCR: Lazy state preservation active bit Mask */ + +/* Floating-Point Context Address Register */ +#define FPU_FPCAR_ADDRESS_Pos 3 /*!< FPCAR: ADDRESS bit Position */ +#define FPU_FPCAR_ADDRESS_Msk (0x1FFFFFFFUL << FPU_FPCAR_ADDRESS_Pos) /*!< FPCAR: ADDRESS bit Mask */ + +/* Floating-Point Default Status Control Register */ +#define FPU_FPDSCR_AHP_Pos 26 /*!< FPDSCR: AHP bit Position */ +#define FPU_FPDSCR_AHP_Msk (1UL << FPU_FPDSCR_AHP_Pos) /*!< FPDSCR: AHP bit Mask */ + +#define FPU_FPDSCR_DN_Pos 25 /*!< FPDSCR: DN bit Position */ +#define FPU_FPDSCR_DN_Msk (1UL << FPU_FPDSCR_DN_Pos) /*!< FPDSCR: DN bit Mask */ + +#define FPU_FPDSCR_FZ_Pos 24 /*!< FPDSCR: FZ bit Position */ +#define FPU_FPDSCR_FZ_Msk (1UL << FPU_FPDSCR_FZ_Pos) /*!< FPDSCR: FZ bit Mask */ + +#define FPU_FPDSCR_RMode_Pos 22 /*!< FPDSCR: RMode bit Position */ +#define FPU_FPDSCR_RMode_Msk (3UL << FPU_FPDSCR_RMode_Pos) /*!< FPDSCR: RMode bit Mask */ + +/* Media and FP Feature Register 0 */ +#define FPU_MVFR0_FP_rounding_modes_Pos 28 /*!< MVFR0: FP rounding modes bits Position */ +#define FPU_MVFR0_FP_rounding_modes_Msk (0xFUL << FPU_MVFR0_FP_rounding_modes_Pos) /*!< MVFR0: FP rounding modes bits Mask */ + +#define FPU_MVFR0_Short_vectors_Pos 24 /*!< MVFR0: Short vectors bits Position */ +#define FPU_MVFR0_Short_vectors_Msk (0xFUL << FPU_MVFR0_Short_vectors_Pos) /*!< MVFR0: Short vectors bits Mask */ + +#define FPU_MVFR0_Square_root_Pos 20 /*!< MVFR0: Square root bits Position */ +#define FPU_MVFR0_Square_root_Msk (0xFUL << FPU_MVFR0_Square_root_Pos) /*!< MVFR0: Square root bits Mask */ + +#define FPU_MVFR0_Divide_Pos 16 /*!< MVFR0: Divide bits Position */ +#define FPU_MVFR0_Divide_Msk (0xFUL << FPU_MVFR0_Divide_Pos) /*!< MVFR0: Divide bits Mask */ + +#define FPU_MVFR0_FP_excep_trapping_Pos 12 /*!< MVFR0: FP exception trapping bits Position */ +#define FPU_MVFR0_FP_excep_trapping_Msk (0xFUL << FPU_MVFR0_FP_excep_trapping_Pos) /*!< MVFR0: FP exception trapping bits Mask */ + +#define FPU_MVFR0_Double_precision_Pos 8 /*!< MVFR0: Double-precision bits Position */ +#define FPU_MVFR0_Double_precision_Msk (0xFUL << FPU_MVFR0_Double_precision_Pos) /*!< MVFR0: Double-precision bits Mask */ + +#define FPU_MVFR0_Single_precision_Pos 4 /*!< MVFR0: Single-precision bits Position */ +#define FPU_MVFR0_Single_precision_Msk (0xFUL << FPU_MVFR0_Single_precision_Pos) /*!< MVFR0: Single-precision bits Mask */ + +#define FPU_MVFR0_A_SIMD_registers_Pos 0 /*!< MVFR0: A_SIMD registers bits Position */ +#define FPU_MVFR0_A_SIMD_registers_Msk (0xFUL << FPU_MVFR0_A_SIMD_registers_Pos) /*!< MVFR0: A_SIMD registers bits Mask */ + +/* Media and FP Feature Register 1 */ +#define FPU_MVFR1_FP_fused_MAC_Pos 28 /*!< MVFR1: FP fused MAC bits Position */ +#define FPU_MVFR1_FP_fused_MAC_Msk (0xFUL << FPU_MVFR1_FP_fused_MAC_Pos) /*!< MVFR1: FP fused MAC bits Mask */ + +#define FPU_MVFR1_FP_HPFP_Pos 24 /*!< MVFR1: FP HPFP bits Position */ +#define FPU_MVFR1_FP_HPFP_Msk (0xFUL << FPU_MVFR1_FP_HPFP_Pos) /*!< MVFR1: FP HPFP bits Mask */ + +#define FPU_MVFR1_D_NaN_mode_Pos 4 /*!< MVFR1: D_NaN mode bits Position */ +#define FPU_MVFR1_D_NaN_mode_Msk (0xFUL << FPU_MVFR1_D_NaN_mode_Pos) /*!< MVFR1: D_NaN mode bits Mask */ + +#define FPU_MVFR1_FtZ_mode_Pos 0 /*!< MVFR1: FtZ mode bits Position */ +#define FPU_MVFR1_FtZ_mode_Msk (0xFUL << FPU_MVFR1_FtZ_mode_Pos) /*!< MVFR1: FtZ mode bits Mask */ + +/*@} end of group CMSIS_FPU */ +#endif + + +/** \ingroup CMSIS_core_register + \defgroup CMSIS_CoreDebug Core Debug Registers (CoreDebug) + \brief Type definitions for the Core Debug Registers + @{ + */ + +/** \brief Structure type to access the Core Debug Register (CoreDebug). + */ +typedef struct +{ + __IO uint32_t DHCSR; /*!< Offset: 0x000 (R/W) Debug Halting Control and Status Register */ + __O uint32_t DCRSR; /*!< Offset: 0x004 ( /W) Debug Core Register Selector Register */ + __IO uint32_t DCRDR; /*!< Offset: 0x008 (R/W) Debug Core Register Data Register */ + __IO uint32_t DEMCR; /*!< Offset: 0x00C (R/W) Debug Exception and Monitor Control Register */ +} CoreDebug_Type; + +/* Debug Halting Control and Status Register */ +#define CoreDebug_DHCSR_DBGKEY_Pos 16 /*!< CoreDebug DHCSR: DBGKEY Position */ +#define CoreDebug_DHCSR_DBGKEY_Msk (0xFFFFUL << CoreDebug_DHCSR_DBGKEY_Pos) /*!< CoreDebug DHCSR: DBGKEY Mask */ + +#define CoreDebug_DHCSR_S_RESET_ST_Pos 25 /*!< CoreDebug DHCSR: S_RESET_ST Position */ +#define CoreDebug_DHCSR_S_RESET_ST_Msk (1UL << CoreDebug_DHCSR_S_RESET_ST_Pos) /*!< CoreDebug DHCSR: S_RESET_ST Mask */ + +#define CoreDebug_DHCSR_S_RETIRE_ST_Pos 24 /*!< CoreDebug DHCSR: S_RETIRE_ST Position */ +#define CoreDebug_DHCSR_S_RETIRE_ST_Msk (1UL << CoreDebug_DHCSR_S_RETIRE_ST_Pos) /*!< CoreDebug DHCSR: S_RETIRE_ST Mask */ + +#define CoreDebug_DHCSR_S_LOCKUP_Pos 19 /*!< CoreDebug DHCSR: S_LOCKUP Position */ +#define CoreDebug_DHCSR_S_LOCKUP_Msk (1UL << CoreDebug_DHCSR_S_LOCKUP_Pos) /*!< CoreDebug DHCSR: S_LOCKUP Mask */ + +#define CoreDebug_DHCSR_S_SLEEP_Pos 18 /*!< CoreDebug DHCSR: S_SLEEP Position */ +#define CoreDebug_DHCSR_S_SLEEP_Msk (1UL << CoreDebug_DHCSR_S_SLEEP_Pos) /*!< CoreDebug DHCSR: S_SLEEP Mask */ + +#define CoreDebug_DHCSR_S_HALT_Pos 17 /*!< CoreDebug DHCSR: S_HALT Position */ +#define CoreDebug_DHCSR_S_HALT_Msk (1UL << CoreDebug_DHCSR_S_HALT_Pos) /*!< CoreDebug DHCSR: S_HALT Mask */ + +#define CoreDebug_DHCSR_S_REGRDY_Pos 16 /*!< CoreDebug DHCSR: S_REGRDY Position */ +#define CoreDebug_DHCSR_S_REGRDY_Msk (1UL << CoreDebug_DHCSR_S_REGRDY_Pos) /*!< CoreDebug DHCSR: S_REGRDY Mask */ + +#define CoreDebug_DHCSR_C_SNAPSTALL_Pos 5 /*!< CoreDebug DHCSR: C_SNAPSTALL Position */ +#define CoreDebug_DHCSR_C_SNAPSTALL_Msk (1UL << CoreDebug_DHCSR_C_SNAPSTALL_Pos) /*!< CoreDebug DHCSR: C_SNAPSTALL Mask */ + +#define CoreDebug_DHCSR_C_MASKINTS_Pos 3 /*!< CoreDebug DHCSR: C_MASKINTS Position */ +#define CoreDebug_DHCSR_C_MASKINTS_Msk (1UL << CoreDebug_DHCSR_C_MASKINTS_Pos) /*!< CoreDebug DHCSR: C_MASKINTS Mask */ + +#define CoreDebug_DHCSR_C_STEP_Pos 2 /*!< CoreDebug DHCSR: C_STEP Position */ +#define CoreDebug_DHCSR_C_STEP_Msk (1UL << CoreDebug_DHCSR_C_STEP_Pos) /*!< CoreDebug DHCSR: C_STEP Mask */ + +#define CoreDebug_DHCSR_C_HALT_Pos 1 /*!< CoreDebug DHCSR: C_HALT Position */ +#define CoreDebug_DHCSR_C_HALT_Msk (1UL << CoreDebug_DHCSR_C_HALT_Pos) /*!< CoreDebug DHCSR: C_HALT Mask */ + +#define CoreDebug_DHCSR_C_DEBUGEN_Pos 0 /*!< CoreDebug DHCSR: C_DEBUGEN Position */ +#define CoreDebug_DHCSR_C_DEBUGEN_Msk (1UL << CoreDebug_DHCSR_C_DEBUGEN_Pos) /*!< CoreDebug DHCSR: C_DEBUGEN Mask */ + +/* Debug Core Register Selector Register */ +#define CoreDebug_DCRSR_REGWnR_Pos 16 /*!< CoreDebug DCRSR: REGWnR Position */ +#define CoreDebug_DCRSR_REGWnR_Msk (1UL << CoreDebug_DCRSR_REGWnR_Pos) /*!< CoreDebug DCRSR: REGWnR Mask */ + +#define CoreDebug_DCRSR_REGSEL_Pos 0 /*!< CoreDebug DCRSR: REGSEL Position */ +#define CoreDebug_DCRSR_REGSEL_Msk (0x1FUL << CoreDebug_DCRSR_REGSEL_Pos) /*!< CoreDebug DCRSR: REGSEL Mask */ + +/* Debug Exception and Monitor Control Register */ +#define CoreDebug_DEMCR_TRCENA_Pos 24 /*!< CoreDebug DEMCR: TRCENA Position */ +#define CoreDebug_DEMCR_TRCENA_Msk (1UL << CoreDebug_DEMCR_TRCENA_Pos) /*!< CoreDebug DEMCR: TRCENA Mask */ + +#define CoreDebug_DEMCR_MON_REQ_Pos 19 /*!< CoreDebug DEMCR: MON_REQ Position */ +#define CoreDebug_DEMCR_MON_REQ_Msk (1UL << CoreDebug_DEMCR_MON_REQ_Pos) /*!< CoreDebug DEMCR: MON_REQ Mask */ + +#define CoreDebug_DEMCR_MON_STEP_Pos 18 /*!< CoreDebug DEMCR: MON_STEP Position */ +#define CoreDebug_DEMCR_MON_STEP_Msk (1UL << CoreDebug_DEMCR_MON_STEP_Pos) /*!< CoreDebug DEMCR: MON_STEP Mask */ + +#define CoreDebug_DEMCR_MON_PEND_Pos 17 /*!< CoreDebug DEMCR: MON_PEND Position */ +#define CoreDebug_DEMCR_MON_PEND_Msk (1UL << CoreDebug_DEMCR_MON_PEND_Pos) /*!< CoreDebug DEMCR: MON_PEND Mask */ + +#define CoreDebug_DEMCR_MON_EN_Pos 16 /*!< CoreDebug DEMCR: MON_EN Position */ +#define CoreDebug_DEMCR_MON_EN_Msk (1UL << CoreDebug_DEMCR_MON_EN_Pos) /*!< CoreDebug DEMCR: MON_EN Mask */ + +#define CoreDebug_DEMCR_VC_HARDERR_Pos 10 /*!< CoreDebug DEMCR: VC_HARDERR Position */ +#define CoreDebug_DEMCR_VC_HARDERR_Msk (1UL << CoreDebug_DEMCR_VC_HARDERR_Pos) /*!< CoreDebug DEMCR: VC_HARDERR Mask */ + +#define CoreDebug_DEMCR_VC_INTERR_Pos 9 /*!< CoreDebug DEMCR: VC_INTERR Position */ +#define CoreDebug_DEMCR_VC_INTERR_Msk (1UL << CoreDebug_DEMCR_VC_INTERR_Pos) /*!< CoreDebug DEMCR: VC_INTERR Mask */ + +#define CoreDebug_DEMCR_VC_BUSERR_Pos 8 /*!< CoreDebug DEMCR: VC_BUSERR Position */ +#define CoreDebug_DEMCR_VC_BUSERR_Msk (1UL << CoreDebug_DEMCR_VC_BUSERR_Pos) /*!< CoreDebug DEMCR: VC_BUSERR Mask */ + +#define CoreDebug_DEMCR_VC_STATERR_Pos 7 /*!< CoreDebug DEMCR: VC_STATERR Position */ +#define CoreDebug_DEMCR_VC_STATERR_Msk (1UL << CoreDebug_DEMCR_VC_STATERR_Pos) /*!< CoreDebug DEMCR: VC_STATERR Mask */ + +#define CoreDebug_DEMCR_VC_CHKERR_Pos 6 /*!< CoreDebug DEMCR: VC_CHKERR Position */ +#define CoreDebug_DEMCR_VC_CHKERR_Msk (1UL << CoreDebug_DEMCR_VC_CHKERR_Pos) /*!< CoreDebug DEMCR: VC_CHKERR Mask */ + +#define CoreDebug_DEMCR_VC_NOCPERR_Pos 5 /*!< CoreDebug DEMCR: VC_NOCPERR Position */ +#define CoreDebug_DEMCR_VC_NOCPERR_Msk (1UL << CoreDebug_DEMCR_VC_NOCPERR_Pos) /*!< CoreDebug DEMCR: VC_NOCPERR Mask */ + +#define CoreDebug_DEMCR_VC_MMERR_Pos 4 /*!< CoreDebug DEMCR: VC_MMERR Position */ +#define CoreDebug_DEMCR_VC_MMERR_Msk (1UL << CoreDebug_DEMCR_VC_MMERR_Pos) /*!< CoreDebug DEMCR: VC_MMERR Mask */ + +#define CoreDebug_DEMCR_VC_CORERESET_Pos 0 /*!< CoreDebug DEMCR: VC_CORERESET Position */ +#define CoreDebug_DEMCR_VC_CORERESET_Msk (1UL << CoreDebug_DEMCR_VC_CORERESET_Pos) /*!< CoreDebug DEMCR: VC_CORERESET Mask */ + +/*@} end of group CMSIS_CoreDebug */ + + +/** \ingroup CMSIS_core_register + \defgroup CMSIS_core_base Core Definitions + \brief Definitions for base addresses, unions, and structures. + @{ + */ + +/* Memory mapping of Cortex-M4 Hardware */ +#define SCS_BASE (0xE000E000UL) /*!< System Control Space Base Address */ +#define ITM_BASE (0xE0000000UL) /*!< ITM Base Address */ +#define DWT_BASE (0xE0001000UL) /*!< DWT Base Address */ +#define TPI_BASE (0xE0040000UL) /*!< TPI Base Address */ +#define CoreDebug_BASE (0xE000EDF0UL) /*!< Core Debug Base Address */ +#define SysTick_BASE (SCS_BASE + 0x0010UL) /*!< SysTick Base Address */ +#define NVIC_BASE (SCS_BASE + 0x0100UL) /*!< NVIC Base Address */ +#define SCB_BASE (SCS_BASE + 0x0D00UL) /*!< System Control Block Base Address */ + +#define SCnSCB ((SCnSCB_Type *) SCS_BASE ) /*!< System control Register not in SCB */ +#define SCB ((SCB_Type *) SCB_BASE ) /*!< SCB configuration struct */ +#define SysTick ((SysTick_Type *) SysTick_BASE ) /*!< SysTick configuration struct */ +#define NVIC ((NVIC_Type *) NVIC_BASE ) /*!< NVIC configuration struct */ +#define ITM ((ITM_Type *) ITM_BASE ) /*!< ITM configuration struct */ +#define DWT ((DWT_Type *) DWT_BASE ) /*!< DWT configuration struct */ +#define TPI ((TPI_Type *) TPI_BASE ) /*!< TPI configuration struct */ +#define CoreDebug ((CoreDebug_Type *) CoreDebug_BASE) /*!< Core Debug configuration struct */ + +#if (__MPU_PRESENT == 1) + #define MPU_BASE (SCS_BASE + 0x0D90UL) /*!< Memory Protection Unit */ + #define MPU ((MPU_Type *) MPU_BASE ) /*!< Memory Protection Unit */ +#endif + +#if (__FPU_PRESENT == 1) + #define FPU_BASE (SCS_BASE + 0x0F30UL) /*!< Floating Point Unit */ + #define FPU ((FPU_Type *) FPU_BASE ) /*!< Floating Point Unit */ +#endif + +/*@} */ + + + +/******************************************************************************* + * Hardware Abstraction Layer + Core Function Interface contains: + - Core NVIC Functions + - Core SysTick Functions + - Core Debug Functions + - Core Register Access Functions + ******************************************************************************/ +/** \defgroup CMSIS_Core_FunctionInterface Functions and Instructions Reference +*/ + + + +/* ########################## NVIC functions #################################### */ +/** \ingroup CMSIS_Core_FunctionInterface + \defgroup CMSIS_Core_NVICFunctions NVIC Functions + \brief Functions that manage interrupts and exceptions via the NVIC. + @{ + */ + +/** \brief Set Priority Grouping + + The function sets the priority grouping field using the required unlock sequence. + The parameter PriorityGroup is assigned to the field SCB->AIRCR [10:8] PRIGROUP field. + Only values from 0..7 are used. + In case of a conflict between priority grouping and available + priority bits (__NVIC_PRIO_BITS), the smallest possible priority group is set. + + \param [in] PriorityGroup Priority grouping field. + */ +__STATIC_INLINE void NVIC_SetPriorityGrouping(uint32_t PriorityGroup) +{ + uint32_t reg_value; + uint32_t PriorityGroupTmp = (PriorityGroup & (uint32_t)0x07); /* only values 0..7 are used */ + + reg_value = SCB->AIRCR; /* read old register configuration */ + reg_value &= ~(SCB_AIRCR_VECTKEY_Msk | SCB_AIRCR_PRIGROUP_Msk); /* clear bits to change */ + reg_value = (reg_value | + ((uint32_t)0x5FA << SCB_AIRCR_VECTKEY_Pos) | + (PriorityGroupTmp << 8)); /* Insert write key and priorty group */ + SCB->AIRCR = reg_value; +} + + +/** \brief Get Priority Grouping + + The function reads the priority grouping field from the NVIC Interrupt Controller. + + \return Priority grouping field (SCB->AIRCR [10:8] PRIGROUP field). + */ +__STATIC_INLINE uint32_t NVIC_GetPriorityGrouping(void) +{ + return ((SCB->AIRCR & SCB_AIRCR_PRIGROUP_Msk) >> SCB_AIRCR_PRIGROUP_Pos); /* read priority grouping field */ +} + + +/** \brief Enable External Interrupt + + The function enables a device-specific interrupt in the NVIC interrupt controller. + + \param [in] IRQn External interrupt number. Value cannot be negative. + */ +__STATIC_INLINE void NVIC_EnableIRQ(IRQn_Type IRQn) +{ +/* NVIC->ISER[((uint32_t)(IRQn) >> 5)] = (1 << ((uint32_t)(IRQn) & 0x1F)); enable interrupt */ + NVIC->ISER[(uint32_t)((int32_t)IRQn) >> 5] = (uint32_t)(1 << ((uint32_t)((int32_t)IRQn) & (uint32_t)0x1F)); /* enable interrupt */ +} + + +/** \brief Disable External Interrupt + + The function disables a device-specific interrupt in the NVIC interrupt controller. + + \param [in] IRQn External interrupt number. Value cannot be negative. + */ +__STATIC_INLINE void NVIC_DisableIRQ(IRQn_Type IRQn) +{ + NVIC->ICER[((uint32_t)(IRQn) >> 5)] = (1 << ((uint32_t)(IRQn) & 0x1F)); /* disable interrupt */ +} + + +/** \brief Get Pending Interrupt + + The function reads the pending register in the NVIC and returns the pending bit + for the specified interrupt. + + \param [in] IRQn Interrupt number. + + \return 0 Interrupt status is not pending. + \return 1 Interrupt status is pending. + */ +__STATIC_INLINE uint32_t NVIC_GetPendingIRQ(IRQn_Type IRQn) +{ + return((uint32_t) ((NVIC->ISPR[(uint32_t)(IRQn) >> 5] & (1 << ((uint32_t)(IRQn) & 0x1F)))?1:0)); /* Return 1 if pending else 0 */ +} + + +/** \brief Set Pending Interrupt + + The function sets the pending bit of an external interrupt. + + \param [in] IRQn Interrupt number. Value cannot be negative. + */ +__STATIC_INLINE void NVIC_SetPendingIRQ(IRQn_Type IRQn) +{ + NVIC->ISPR[((uint32_t)(IRQn) >> 5)] = (1 << ((uint32_t)(IRQn) & 0x1F)); /* set interrupt pending */ +} + + +/** \brief Clear Pending Interrupt + + The function clears the pending bit of an external interrupt. + + \param [in] IRQn External interrupt number. Value cannot be negative. + */ +__STATIC_INLINE void NVIC_ClearPendingIRQ(IRQn_Type IRQn) +{ + NVIC->ICPR[((uint32_t)(IRQn) >> 5)] = (1 << ((uint32_t)(IRQn) & 0x1F)); /* Clear pending interrupt */ +} + + +/** \brief Get Active Interrupt + + The function reads the active register in NVIC and returns the active bit. + + \param [in] IRQn Interrupt number. + + \return 0 Interrupt status is not active. + \return 1 Interrupt status is active. + */ +__STATIC_INLINE uint32_t NVIC_GetActive(IRQn_Type IRQn) +{ + return((uint32_t)((NVIC->IABR[(uint32_t)(IRQn) >> 5] & (1 << ((uint32_t)(IRQn) & 0x1F)))?1:0)); /* Return 1 if active else 0 */ +} + + +/** \brief Set Interrupt Priority + + The function sets the priority of an interrupt. + + \note The priority cannot be set for every core interrupt. + + \param [in] IRQn Interrupt number. + \param [in] priority Priority to set. + */ +__STATIC_INLINE void NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority) +{ + if(IRQn < 0) { + SCB->SHP[((uint32_t)(IRQn) & 0xF)-4] = ((priority << (8 - __NVIC_PRIO_BITS)) & 0xff); } /* set Priority for Cortex-M System Interrupts */ + else { + NVIC->IP[(uint32_t)(IRQn)] = ((priority << (8 - __NVIC_PRIO_BITS)) & 0xff); } /* set Priority for device specific Interrupts */ +} + + +/** \brief Get Interrupt Priority + + The function reads the priority of an interrupt. The interrupt + number can be positive to specify an external (device specific) + interrupt, or negative to specify an internal (core) interrupt. + + + \param [in] IRQn Interrupt number. + \return Interrupt Priority. Value is aligned automatically to the implemented + priority bits of the microcontroller. + */ +__STATIC_INLINE uint32_t NVIC_GetPriority(IRQn_Type IRQn) +{ + + if(IRQn < 0) { + return((uint32_t)(SCB->SHP[((uint32_t)(IRQn) & 0xF)-4] >> (8 - __NVIC_PRIO_BITS))); } /* get priority for Cortex-M system interrupts */ + else { + return((uint32_t)(NVIC->IP[(uint32_t)(IRQn)] >> (8 - __NVIC_PRIO_BITS))); } /* get priority for device specific interrupts */ +} + + +/** \brief Encode Priority + + The function encodes the priority for an interrupt with the given priority group, + preemptive priority value, and subpriority value. + In case of a conflict between priority grouping and available + priority bits (__NVIC_PRIO_BITS), the smallest possible priority group is set. + + \param [in] PriorityGroup Used priority group. + \param [in] PreemptPriority Preemptive priority value (starting from 0). + \param [in] SubPriority Subpriority value (starting from 0). + \return Encoded priority. Value can be used in the function \ref NVIC_SetPriority(). + */ +__STATIC_INLINE uint32_t NVIC_EncodePriority (uint32_t PriorityGroup, uint32_t PreemptPriority, uint32_t SubPriority) +{ + uint32_t PriorityGroupTmp = (PriorityGroup & 0x07); /* only values 0..7 are used */ + uint32_t PreemptPriorityBits; + uint32_t SubPriorityBits; + + PreemptPriorityBits = ((7 - PriorityGroupTmp) > __NVIC_PRIO_BITS) ? __NVIC_PRIO_BITS : 7 - PriorityGroupTmp; + SubPriorityBits = ((PriorityGroupTmp + __NVIC_PRIO_BITS) < 7) ? 0 : PriorityGroupTmp - 7 + __NVIC_PRIO_BITS; + + return ( + ((PreemptPriority & ((1 << (PreemptPriorityBits)) - 1)) << SubPriorityBits) | + ((SubPriority & ((1 << (SubPriorityBits )) - 1))) + ); +} + + +/** \brief Decode Priority + + The function decodes an interrupt priority value with a given priority group to + preemptive priority value and subpriority value. + In case of a conflict between priority grouping and available + priority bits (__NVIC_PRIO_BITS) the smallest possible priority group is set. + + \param [in] Priority Priority value, which can be retrieved with the function \ref NVIC_GetPriority(). + \param [in] PriorityGroup Used priority group. + \param [out] pPreemptPriority Preemptive priority value (starting from 0). + \param [out] pSubPriority Subpriority value (starting from 0). + */ +__STATIC_INLINE void NVIC_DecodePriority (uint32_t Priority, uint32_t PriorityGroup, uint32_t* pPreemptPriority, uint32_t* pSubPriority) +{ + uint32_t PriorityGroupTmp = (PriorityGroup & 0x07); /* only values 0..7 are used */ + uint32_t PreemptPriorityBits; + uint32_t SubPriorityBits; + + PreemptPriorityBits = ((7 - PriorityGroupTmp) > __NVIC_PRIO_BITS) ? __NVIC_PRIO_BITS : 7 - PriorityGroupTmp; + SubPriorityBits = ((PriorityGroupTmp + __NVIC_PRIO_BITS) < 7) ? 0 : PriorityGroupTmp - 7 + __NVIC_PRIO_BITS; + + *pPreemptPriority = (Priority >> SubPriorityBits) & ((1 << (PreemptPriorityBits)) - 1); + *pSubPriority = (Priority ) & ((1 << (SubPriorityBits )) - 1); +} + + +/** \brief System Reset + + The function initiates a system reset request to reset the MCU. + */ +__STATIC_INLINE void NVIC_SystemReset(void) +{ + __DSB(); /* Ensure all outstanding memory accesses included + buffered write are completed before reset */ + SCB->AIRCR = ((0x5FA << SCB_AIRCR_VECTKEY_Pos) | + (SCB->AIRCR & SCB_AIRCR_PRIGROUP_Msk) | + SCB_AIRCR_SYSRESETREQ_Msk); /* Keep priority group unchanged */ + __DSB(); /* Ensure completion of memory access */ + while(1); /* wait until reset */ +} + +/*@} end of CMSIS_Core_NVICFunctions */ + + + +/* ################################## SysTick function ############################################ */ +/** \ingroup CMSIS_Core_FunctionInterface + \defgroup CMSIS_Core_SysTickFunctions SysTick Functions + \brief Functions that configure the System. + @{ + */ + +#if (__Vendor_SysTickConfig == 0) + +/** \brief System Tick Configuration + + The function initializes the System Timer and its interrupt, and starts the System Tick Timer. + Counter is in free running mode to generate periodic interrupts. + + \param [in] ticks Number of ticks between two interrupts. + + \return 0 Function succeeded. + \return 1 Function failed. + + \note When the variable __Vendor_SysTickConfig is set to 1, then the + function SysTick_Config is not included. In this case, the file device.h + must contain a vendor-specific implementation of this function. + + */ +__STATIC_INLINE uint32_t SysTick_Config(uint32_t ticks) +{ + if ((ticks - 1) > SysTick_LOAD_RELOAD_Msk) return (1); /* Reload value impossible */ + + SysTick->LOAD = ticks - 1; /* set reload register */ + NVIC_SetPriority (SysTick_IRQn, (1<<__NVIC_PRIO_BITS) - 1); /* set Priority for Systick Interrupt */ + SysTick->VAL = 0; /* Load the SysTick Counter Value */ + SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk | + SysTick_CTRL_TICKINT_Msk | + SysTick_CTRL_ENABLE_Msk; /* Enable SysTick IRQ and SysTick Timer */ + return (0); /* Function successful */ +} + +#endif + +/*@} end of CMSIS_Core_SysTickFunctions */ + + + +/* ##################################### Debug In/Output function ########################################### */ +/** \ingroup CMSIS_Core_FunctionInterface + \defgroup CMSIS_core_DebugFunctions ITM Functions + \brief Functions that access the ITM debug interface. + @{ + */ + +extern volatile int32_t ITM_RxBuffer; /*!< External variable to receive characters. */ +#define ITM_RXBUFFER_EMPTY 0x5AA55AA5 /*!< Value identifying \ref ITM_RxBuffer is ready for next character. */ + + +/** \brief ITM Send Character + + The function transmits a character via the ITM channel 0, and + \li Just returns when no debugger is connected that has booked the output. + \li Is blocking when a debugger is connected, but the previous character sent has not been transmitted. + + \param [in] ch Character to transmit. + + \returns Character to transmit. + */ +__STATIC_INLINE uint32_t ITM_SendChar (uint32_t ch) +{ + if ((ITM->TCR & ITM_TCR_ITMENA_Msk) && /* ITM enabled */ + (ITM->TER & (1UL << 0) ) ) /* ITM Port #0 enabled */ + { + while (ITM->PORT[0].u32 == 0); + ITM->PORT[0].u8 = (uint8_t) ch; + } + return (ch); +} + + +/** \brief ITM Receive Character + + The function inputs a character via the external variable \ref ITM_RxBuffer. + + \return Received character. + \return -1 No character pending. + */ +__STATIC_INLINE int32_t ITM_ReceiveChar (void) { + int32_t ch = -1; /* no character available */ + + if (ITM_RxBuffer != ITM_RXBUFFER_EMPTY) { + ch = ITM_RxBuffer; + ITM_RxBuffer = ITM_RXBUFFER_EMPTY; /* ready for next character */ + } + + return (ch); +} + + +/** \brief ITM Check Character + + The function checks whether a character is pending for reading in the variable \ref ITM_RxBuffer. + + \return 0 No character available. + \return 1 Character available. + */ +__STATIC_INLINE int32_t ITM_CheckChar (void) { + + if (ITM_RxBuffer == ITM_RXBUFFER_EMPTY) { + return (0); /* no character available */ + } else { + return (1); /* character available */ + } +} + +/*@} end of CMSIS_core_DebugFunctions */ + + + + +#ifdef __cplusplus +} +#endif + +#endif /* __CORE_CM4_H_DEPENDANT */ + +#endif /* __CMSIS_GENERIC */ diff --git a/platform/mcu/xr871/include/driver/cmsis/core_cmFunc.h b/platform/mcu/xr871/include/driver/cmsis/core_cmFunc.h new file mode 100644 index 0000000000..01089f1333 --- /dev/null +++ b/platform/mcu/xr871/include/driver/cmsis/core_cmFunc.h @@ -0,0 +1,637 @@ +/**************************************************************************//** + * @file core_cmFunc.h + * @brief CMSIS Cortex-M Core Function Access Header File + * @version V4.00 + * @date 28. August 2014 + * + * @note + * + ******************************************************************************/ +/* Copyright (c) 2009 - 2014 ARM LIMITED + + All rights reserved. + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + - Neither the name of ARM nor the names of its contributors may be used + to endorse or promote products derived from this software without + specific prior written permission. + * + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDERS AND CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + ---------------------------------------------------------------------------*/ + + +#ifndef __CORE_CMFUNC_H +#define __CORE_CMFUNC_H + + +/* ########################### Core Function Access ########################### */ +/** \ingroup CMSIS_Core_FunctionInterface + \defgroup CMSIS_Core_RegAccFunctions CMSIS Core Register Access Functions + @{ + */ + +#if defined ( __CC_ARM ) /*------------------RealView Compiler -----------------*/ +/* ARM armcc specific functions */ + +#if (__ARMCC_VERSION < 400677) + #error "Please use ARM Compiler Toolchain V4.0.677 or later!" +#endif + +/* intrinsic void __enable_irq(); */ +/* intrinsic void __disable_irq(); */ + +/** \brief Get Control Register + + This function returns the content of the Control Register. + + \return Control Register value + */ +__STATIC_INLINE uint32_t __get_CONTROL(void) +{ + register uint32_t __regControl __ASM("control"); + return(__regControl); +} + + +/** \brief Set Control Register + + This function writes the given value to the Control Register. + + \param [in] control Control Register value to set + */ +__STATIC_INLINE void __set_CONTROL(uint32_t control) +{ + register uint32_t __regControl __ASM("control"); + __regControl = control; +} + + +/** \brief Get IPSR Register + + This function returns the content of the IPSR Register. + + \return IPSR Register value + */ +__STATIC_INLINE uint32_t __get_IPSR(void) +{ + register uint32_t __regIPSR __ASM("ipsr"); + return(__regIPSR); +} + + +/** \brief Get APSR Register + + This function returns the content of the APSR Register. + + \return APSR Register value + */ +__STATIC_INLINE uint32_t __get_APSR(void) +{ + register uint32_t __regAPSR __ASM("apsr"); + return(__regAPSR); +} + + +/** \brief Get xPSR Register + + This function returns the content of the xPSR Register. + + \return xPSR Register value + */ +__STATIC_INLINE uint32_t __get_xPSR(void) +{ + register uint32_t __regXPSR __ASM("xpsr"); + return(__regXPSR); +} + + +/** \brief Get Process Stack Pointer + + This function returns the current value of the Process Stack Pointer (PSP). + + \return PSP Register value + */ +__STATIC_INLINE uint32_t __get_PSP(void) +{ + register uint32_t __regProcessStackPointer __ASM("psp"); + return(__regProcessStackPointer); +} + + +/** \brief Set Process Stack Pointer + + This function assigns the given value to the Process Stack Pointer (PSP). + + \param [in] topOfProcStack Process Stack Pointer value to set + */ +__STATIC_INLINE void __set_PSP(uint32_t topOfProcStack) +{ + register uint32_t __regProcessStackPointer __ASM("psp"); + __regProcessStackPointer = topOfProcStack; +} + + +/** \brief Get Main Stack Pointer + + This function returns the current value of the Main Stack Pointer (MSP). + + \return MSP Register value + */ +__STATIC_INLINE uint32_t __get_MSP(void) +{ + register uint32_t __regMainStackPointer __ASM("msp"); + return(__regMainStackPointer); +} + + +/** \brief Set Main Stack Pointer + + This function assigns the given value to the Main Stack Pointer (MSP). + + \param [in] topOfMainStack Main Stack Pointer value to set + */ +__STATIC_INLINE void __set_MSP(uint32_t topOfMainStack) +{ + register uint32_t __regMainStackPointer __ASM("msp"); + __regMainStackPointer = topOfMainStack; +} + + +/** \brief Get Priority Mask + + This function returns the current state of the priority mask bit from the Priority Mask Register. + + \return Priority Mask value + */ +__STATIC_INLINE uint32_t __get_PRIMASK(void) +{ + register uint32_t __regPriMask __ASM("primask"); + return(__regPriMask); +} + + +/** \brief Set Priority Mask + + This function assigns the given value to the Priority Mask Register. + + \param [in] priMask Priority Mask + */ +__STATIC_INLINE void __set_PRIMASK(uint32_t priMask) +{ + register uint32_t __regPriMask __ASM("primask"); + __regPriMask = (priMask); +} + + +#if (__CORTEX_M >= 0x03) || (__CORTEX_SC >= 300) + +/** \brief Enable FIQ + + This function enables FIQ interrupts by clearing the F-bit in the CPSR. + Can only be executed in Privileged modes. + */ +#define __enable_fault_irq __enable_fiq + + +/** \brief Disable FIQ + + This function disables FIQ interrupts by setting the F-bit in the CPSR. + Can only be executed in Privileged modes. + */ +#define __disable_fault_irq __disable_fiq + + +/** \brief Get Base Priority + + This function returns the current value of the Base Priority register. + + \return Base Priority register value + */ +__STATIC_INLINE uint32_t __get_BASEPRI(void) +{ + register uint32_t __regBasePri __ASM("basepri"); + return(__regBasePri); +} + + +/** \brief Set Base Priority + + This function assigns the given value to the Base Priority register. + + \param [in] basePri Base Priority value to set + */ +__STATIC_INLINE void __set_BASEPRI(uint32_t basePri) +{ + register uint32_t __regBasePri __ASM("basepri"); + __regBasePri = (basePri & 0xff); +} + + +/** \brief Get Fault Mask + + This function returns the current value of the Fault Mask register. + + \return Fault Mask register value + */ +__STATIC_INLINE uint32_t __get_FAULTMASK(void) +{ + register uint32_t __regFaultMask __ASM("faultmask"); + return(__regFaultMask); +} + + +/** \brief Set Fault Mask + + This function assigns the given value to the Fault Mask register. + + \param [in] faultMask Fault Mask value to set + */ +__STATIC_INLINE void __set_FAULTMASK(uint32_t faultMask) +{ + register uint32_t __regFaultMask __ASM("faultmask"); + __regFaultMask = (faultMask & (uint32_t)1); +} + +#endif /* (__CORTEX_M >= 0x03) || (__CORTEX_SC >= 300) */ + + +#if (__CORTEX_M == 0x04) || (__CORTEX_M == 0x07) + +/** \brief Get FPSCR + + This function returns the current value of the Floating Point Status/Control register. + + \return Floating Point Status/Control register value + */ +__STATIC_INLINE uint32_t __get_FPSCR(void) +{ +#if (__FPU_PRESENT == 1) && (__FPU_USED == 1) + register uint32_t __regfpscr __ASM("fpscr"); + return(__regfpscr); +#else + return(0); +#endif +} + + +/** \brief Set FPSCR + + This function assigns the given value to the Floating Point Status/Control register. + + \param [in] fpscr Floating Point Status/Control value to set + */ +__STATIC_INLINE void __set_FPSCR(uint32_t fpscr) +{ +#if (__FPU_PRESENT == 1) && (__FPU_USED == 1) + register uint32_t __regfpscr __ASM("fpscr"); + __regfpscr = (fpscr); +#endif +} + +#endif /* (__CORTEX_M == 0x04) || (__CORTEX_M == 0x07) */ + + +#elif defined ( __GNUC__ ) /*------------------ GNU Compiler ---------------------*/ +/* GNU gcc specific functions */ + +/** \brief Enable IRQ Interrupts + + This function enables IRQ interrupts by clearing the I-bit in the CPSR. + Can only be executed in Privileged modes. + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE void __enable_irq(void) +{ + __ASM volatile ("cpsie i" : : : "memory"); +} + + +/** \brief Disable IRQ Interrupts + + This function disables IRQ interrupts by setting the I-bit in the CPSR. + Can only be executed in Privileged modes. + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE void __disable_irq(void) +{ + __ASM volatile ("cpsid i" : : : "memory"); +} + + +/** \brief Get Control Register + + This function returns the content of the Control Register. + + \return Control Register value + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __get_CONTROL(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, control" : "=r" (result) ); + return(result); +} + + +/** \brief Set Control Register + + This function writes the given value to the Control Register. + + \param [in] control Control Register value to set + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE void __set_CONTROL(uint32_t control) +{ + __ASM volatile ("MSR control, %0" : : "r" (control) : "memory"); +} + + +/** \brief Get IPSR Register + + This function returns the content of the IPSR Register. + + \return IPSR Register value + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __get_IPSR(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, ipsr" : "=r" (result) ); + return(result); +} + + +/** \brief Get APSR Register + + This function returns the content of the APSR Register. + + \return APSR Register value + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __get_APSR(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, apsr" : "=r" (result) ); + return(result); +} + + +/** \brief Get xPSR Register + + This function returns the content of the xPSR Register. + + \return xPSR Register value + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __get_xPSR(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, xpsr" : "=r" (result) ); + return(result); +} + + +/** \brief Get Process Stack Pointer + + This function returns the current value of the Process Stack Pointer (PSP). + + \return PSP Register value + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __get_PSP(void) +{ + register uint32_t result; + + __ASM volatile ("MRS %0, psp\n" : "=r" (result) ); + return(result); +} + + +/** \brief Set Process Stack Pointer + + This function assigns the given value to the Process Stack Pointer (PSP). + + \param [in] topOfProcStack Process Stack Pointer value to set + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE void __set_PSP(uint32_t topOfProcStack) +{ + __ASM volatile ("MSR psp, %0\n" : : "r" (topOfProcStack) : "sp"); +} + + +/** \brief Get Main Stack Pointer + + This function returns the current value of the Main Stack Pointer (MSP). + + \return MSP Register value + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __get_MSP(void) +{ + register uint32_t result; + + __ASM volatile ("MRS %0, msp\n" : "=r" (result) ); + return(result); +} + + +/** \brief Set Main Stack Pointer + + This function assigns the given value to the Main Stack Pointer (MSP). + + \param [in] topOfMainStack Main Stack Pointer value to set + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE void __set_MSP(uint32_t topOfMainStack) +{ + __ASM volatile ("MSR msp, %0\n" : : "r" (topOfMainStack) : "sp"); +} + + +/** \brief Get Priority Mask + + This function returns the current state of the priority mask bit from the Priority Mask Register. + + \return Priority Mask value + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __get_PRIMASK(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, primask" : "=r" (result) ); + return(result); +} + + +/** \brief Set Priority Mask + + This function assigns the given value to the Priority Mask Register. + + \param [in] priMask Priority Mask + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE void __set_PRIMASK(uint32_t priMask) +{ + __ASM volatile ("MSR primask, %0" : : "r" (priMask) : "memory"); +} + + +#if (__CORTEX_M >= 0x03) + +/** \brief Enable FIQ + + This function enables FIQ interrupts by clearing the F-bit in the CPSR. + Can only be executed in Privileged modes. + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE void __enable_fault_irq(void) +{ + __ASM volatile ("cpsie f" : : : "memory"); +} + + +/** \brief Disable FIQ + + This function disables FIQ interrupts by setting the F-bit in the CPSR. + Can only be executed in Privileged modes. + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE void __disable_fault_irq(void) +{ + __ASM volatile ("cpsid f" : : : "memory"); +} + + +/** \brief Get Base Priority + + This function returns the current value of the Base Priority register. + + \return Base Priority register value + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __get_BASEPRI(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, basepri_max" : "=r" (result) ); + return(result); +} + + +/** \brief Set Base Priority + + This function assigns the given value to the Base Priority register. + + \param [in] basePri Base Priority value to set + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE void __set_BASEPRI(uint32_t value) +{ + __ASM volatile ("MSR basepri, %0" : : "r" (value) : "memory"); +} + + +/** \brief Get Fault Mask + + This function returns the current value of the Fault Mask register. + + \return Fault Mask register value + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __get_FAULTMASK(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, faultmask" : "=r" (result) ); + return(result); +} + + +/** \brief Set Fault Mask + + This function assigns the given value to the Fault Mask register. + + \param [in] faultMask Fault Mask value to set + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE void __set_FAULTMASK(uint32_t faultMask) +{ + __ASM volatile ("MSR faultmask, %0" : : "r" (faultMask) : "memory"); +} + +#endif /* (__CORTEX_M >= 0x03) */ + + +#if (__CORTEX_M == 0x04) || (__CORTEX_M == 0x07) + +/** \brief Get FPSCR + + This function returns the current value of the Floating Point Status/Control register. + + \return Floating Point Status/Control register value + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __get_FPSCR(void) +{ +#if (__FPU_PRESENT == 1) && (__FPU_USED == 1) + uint32_t result; + + /* Empty asm statement works as a scheduling barrier */ + __ASM volatile (""); + __ASM volatile ("VMRS %0, fpscr" : "=r" (result) ); + __ASM volatile (""); + return(result); +#else + return(0); +#endif +} + + +/** \brief Set FPSCR + + This function assigns the given value to the Floating Point Status/Control register. + + \param [in] fpscr Floating Point Status/Control value to set + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE void __set_FPSCR(uint32_t fpscr) +{ +#if (__FPU_PRESENT == 1) && (__FPU_USED == 1) + /* Empty asm statement works as a scheduling barrier */ + __ASM volatile (""); + __ASM volatile ("VMSR fpscr, %0" : : "r" (fpscr) : "vfpcc"); + __ASM volatile (""); +#endif +} + +#endif /* (__CORTEX_M == 0x04) || (__CORTEX_M == 0x07) */ + + +#elif defined ( __ICCARM__ ) /*------------------ ICC Compiler -------------------*/ +/* IAR iccarm specific functions */ +#include + + +#elif defined ( __TMS470__ ) /*---------------- TI CCS Compiler ------------------*/ +/* TI CCS specific functions */ +#include + + +#elif defined ( __TASKING__ ) /*------------------ TASKING Compiler --------------*/ +/* TASKING carm specific functions */ +/* + * The CMSIS functions have been implemented as intrinsics in the compiler. + * Please use "carm -?i" to get an up to date list of all intrinsics, + * Including the CMSIS ones. + */ + + +#elif defined ( __CSMC__ ) /*------------------ COSMIC Compiler -------------------*/ +/* Cosmic specific functions */ +#include + +#endif + +/*@} end of CMSIS_Core_RegAccFunctions */ + +#endif /* __CORE_CMFUNC_H */ diff --git a/platform/mcu/xr871/include/driver/cmsis/core_cmInstr.h b/platform/mcu/xr871/include/driver/cmsis/core_cmInstr.h new file mode 100644 index 0000000000..d14110b2ab --- /dev/null +++ b/platform/mcu/xr871/include/driver/cmsis/core_cmInstr.h @@ -0,0 +1,880 @@ +/**************************************************************************//** + * @file core_cmInstr.h + * @brief CMSIS Cortex-M Core Instruction Access Header File + * @version V4.00 + * @date 28. August 2014 + * + * @note + * + ******************************************************************************/ +/* Copyright (c) 2009 - 2014 ARM LIMITED + + All rights reserved. + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + - Neither the name of ARM nor the names of its contributors may be used + to endorse or promote products derived from this software without + specific prior written permission. + * + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDERS AND CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + ---------------------------------------------------------------------------*/ + + +#ifndef __CORE_CMINSTR_H +#define __CORE_CMINSTR_H + + +/* ########################## Core Instruction Access ######################### */ +/** \defgroup CMSIS_Core_InstructionInterface CMSIS Core Instruction Interface + Access to dedicated instructions + @{ +*/ + +#if defined ( __CC_ARM ) /*------------------RealView Compiler -----------------*/ +/* ARM armcc specific functions */ + +#if (__ARMCC_VERSION < 400677) + #error "Please use ARM Compiler Toolchain V4.0.677 or later!" +#endif + + +/** \brief No Operation + + No Operation does nothing. This instruction can be used for code alignment purposes. + */ +#define __NOP __nop + + +/** \brief Wait For Interrupt + + Wait For Interrupt is a hint instruction that suspends execution + until one of a number of events occurs. + */ +#define __WFI __wfi + + +/** \brief Wait For Event + + Wait For Event is a hint instruction that permits the processor to enter + a low-power state until one of a number of events occurs. + */ +#define __WFE __wfe + + +/** \brief Send Event + + Send Event is a hint instruction. It causes an event to be signaled to the CPU. + */ +#define __SEV __sev + + +/** \brief Instruction Synchronization Barrier + + Instruction Synchronization Barrier flushes the pipeline in the processor, + so that all instructions following the ISB are fetched from cache or + memory, after the instruction has been completed. + */ +#define __ISB() __isb(0xF) + + +/** \brief Data Synchronization Barrier + + This function acts as a special kind of Data Memory Barrier. + It completes when all explicit memory accesses before this instruction complete. + */ +#define __DSB() __dsb(0xF) + + +/** \brief Data Memory Barrier + + This function ensures the apparent order of the explicit memory operations before + and after the instruction, without ensuring their completion. + */ +#define __DMB() __dmb(0xF) + + +/** \brief Reverse byte order (32 bit) + + This function reverses the byte order in integer value. + + \param [in] value Value to reverse + \return Reversed value + */ +#define __REV __rev + + +/** \brief Reverse byte order (16 bit) + + This function reverses the byte order in two unsigned short values. + + \param [in] value Value to reverse + \return Reversed value + */ +#ifndef __NO_EMBEDDED_ASM +__attribute__((section(".rev16_text"))) __STATIC_INLINE __ASM uint32_t __REV16(uint32_t value) +{ + rev16 r0, r0 + bx lr +} +#endif + +/** \brief Reverse byte order in signed short value + + This function reverses the byte order in a signed short value with sign extension to integer. + + \param [in] value Value to reverse + \return Reversed value + */ +#ifndef __NO_EMBEDDED_ASM +__attribute__((section(".revsh_text"))) __STATIC_INLINE __ASM int32_t __REVSH(int32_t value) +{ + revsh r0, r0 + bx lr +} +#endif + + +/** \brief Rotate Right in unsigned value (32 bit) + + This function Rotate Right (immediate) provides the value of the contents of a register rotated by a variable number of bits. + + \param [in] value Value to rotate + \param [in] value Number of Bits to rotate + \return Rotated value + */ +#define __ROR __ror + + +/** \brief Breakpoint + + This function causes the processor to enter Debug state. + Debug tools can use this to investigate system state when the instruction at a particular address is reached. + + \param [in] value is ignored by the processor. + If required, a debugger can use it to store additional information about the breakpoint. + */ +#define __BKPT(value) __breakpoint(value) + + +#if (__CORTEX_M >= 0x03) || (__CORTEX_SC >= 300) + +/** \brief Reverse bit order of value + + This function reverses the bit order of the given value. + + \param [in] value Value to reverse + \return Reversed value + */ +#define __RBIT __rbit + + +/** \brief LDR Exclusive (8 bit) + + This function executes a exclusive LDR instruction for 8 bit value. + + \param [in] ptr Pointer to data + \return value of type uint8_t at (*ptr) + */ +#define __LDREXB(ptr) ((uint8_t ) __ldrex(ptr)) + + +/** \brief LDR Exclusive (16 bit) + + This function executes a exclusive LDR instruction for 16 bit values. + + \param [in] ptr Pointer to data + \return value of type uint16_t at (*ptr) + */ +#define __LDREXH(ptr) ((uint16_t) __ldrex(ptr)) + + +/** \brief LDR Exclusive (32 bit) + + This function executes a exclusive LDR instruction for 32 bit values. + + \param [in] ptr Pointer to data + \return value of type uint32_t at (*ptr) + */ +#define __LDREXW(ptr) ((uint32_t ) __ldrex(ptr)) + + +/** \brief STR Exclusive (8 bit) + + This function executes a exclusive STR instruction for 8 bit values. + + \param [in] value Value to store + \param [in] ptr Pointer to location + \return 0 Function succeeded + \return 1 Function failed + */ +#define __STREXB(value, ptr) __strex(value, ptr) + + +/** \brief STR Exclusive (16 bit) + + This function executes a exclusive STR instruction for 16 bit values. + + \param [in] value Value to store + \param [in] ptr Pointer to location + \return 0 Function succeeded + \return 1 Function failed + */ +#define __STREXH(value, ptr) __strex(value, ptr) + + +/** \brief STR Exclusive (32 bit) + + This function executes a exclusive STR instruction for 32 bit values. + + \param [in] value Value to store + \param [in] ptr Pointer to location + \return 0 Function succeeded + \return 1 Function failed + */ +#define __STREXW(value, ptr) __strex(value, ptr) + + +/** \brief Remove the exclusive lock + + This function removes the exclusive lock which is created by LDREX. + + */ +#define __CLREX __clrex + + +/** \brief Signed Saturate + + This function saturates a signed value. + + \param [in] value Value to be saturated + \param [in] sat Bit position to saturate to (1..32) + \return Saturated value + */ +#define __SSAT __ssat + + +/** \brief Unsigned Saturate + + This function saturates an unsigned value. + + \param [in] value Value to be saturated + \param [in] sat Bit position to saturate to (0..31) + \return Saturated value + */ +#define __USAT __usat + + +/** \brief Count leading zeros + + This function counts the number of leading zeros of a data value. + + \param [in] value Value to count the leading zeros + \return number of leading zeros in value + */ +#define __CLZ __clz + + +/** \brief Rotate Right with Extend (32 bit) + + This function moves each bit of a bitstring right by one bit. The carry input is shifted in at the left end of the bitstring. + + \param [in] value Value to rotate + \return Rotated value + */ +#ifndef __NO_EMBEDDED_ASM +__attribute__((section(".rrx_text"))) __STATIC_INLINE __ASM uint32_t __RRX(uint32_t value) +{ + rrx r0, r0 + bx lr +} +#endif + + +/** \brief LDRT Unprivileged (8 bit) + + This function executes a Unprivileged LDRT instruction for 8 bit value. + + \param [in] ptr Pointer to data + \return value of type uint8_t at (*ptr) + */ +#define __LDRBT(ptr) ((uint8_t ) __ldrt(ptr)) + + +/** \brief LDRT Unprivileged (16 bit) + + This function executes a Unprivileged LDRT instruction for 16 bit values. + + \param [in] ptr Pointer to data + \return value of type uint16_t at (*ptr) + */ +#define __LDRHT(ptr) ((uint16_t) __ldrt(ptr)) + + +/** \brief LDRT Unprivileged (32 bit) + + This function executes a Unprivileged LDRT instruction for 32 bit values. + + \param [in] ptr Pointer to data + \return value of type uint32_t at (*ptr) + */ +#define __LDRT(ptr) ((uint32_t ) __ldrt(ptr)) + + +/** \brief STRT Unprivileged (8 bit) + + This function executes a Unprivileged STRT instruction for 8 bit values. + + \param [in] value Value to store + \param [in] ptr Pointer to location + */ +#define __STRBT(value, ptr) __strt(value, ptr) + + +/** \brief STRT Unprivileged (16 bit) + + This function executes a Unprivileged STRT instruction for 16 bit values. + + \param [in] value Value to store + \param [in] ptr Pointer to location + */ +#define __STRHT(value, ptr) __strt(value, ptr) + + +/** \brief STRT Unprivileged (32 bit) + + This function executes a Unprivileged STRT instruction for 32 bit values. + + \param [in] value Value to store + \param [in] ptr Pointer to location + */ +#define __STRT(value, ptr) __strt(value, ptr) + +#endif /* (__CORTEX_M >= 0x03) || (__CORTEX_SC >= 300) */ + + +#elif defined ( __GNUC__ ) /*------------------ GNU Compiler ---------------------*/ +/* GNU gcc specific functions */ + +/* Define macros for porting to both thumb1 and thumb2. + * For thumb1, use low register (r0-r7), specified by constrant "l" + * Otherwise, use general registers, specified by constrant "r" */ +#if defined (__thumb__) && !defined (__thumb2__) +#define __CMSIS_GCC_OUT_REG(r) "=l" (r) +#define __CMSIS_GCC_USE_REG(r) "l" (r) +#else +#define __CMSIS_GCC_OUT_REG(r) "=r" (r) +#define __CMSIS_GCC_USE_REG(r) "r" (r) +#endif + +/** \brief No Operation + + No Operation does nothing. This instruction can be used for code alignment purposes. + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE void __NOP(void) +{ + __ASM volatile ("nop"); +} + + +/** \brief Wait For Interrupt + + Wait For Interrupt is a hint instruction that suspends execution + until one of a number of events occurs. + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE void __WFI(void) +{ + __ASM volatile ("wfi"); +} + + +/** \brief Wait For Event + + Wait For Event is a hint instruction that permits the processor to enter + a low-power state until one of a number of events occurs. + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE void __WFE(void) +{ + __ASM volatile ("wfe"); +} + + +/** \brief Send Event + + Send Event is a hint instruction. It causes an event to be signaled to the CPU. + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE void __SEV(void) +{ + __ASM volatile ("sev"); +} + + +/** \brief Instruction Synchronization Barrier + + Instruction Synchronization Barrier flushes the pipeline in the processor, + so that all instructions following the ISB are fetched from cache or + memory, after the instruction has been completed. + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE void __ISB(void) +{ + __ASM volatile ("isb"); +} + + +/** \brief Data Synchronization Barrier + + This function acts as a special kind of Data Memory Barrier. + It completes when all explicit memory accesses before this instruction complete. + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE void __DSB(void) +{ + __ASM volatile ("dsb"); +} + + +/** \brief Data Memory Barrier + + This function ensures the apparent order of the explicit memory operations before + and after the instruction, without ensuring their completion. + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE void __DMB(void) +{ + __ASM volatile ("dmb"); +} + + +/** \brief Reverse byte order (32 bit) + + This function reverses the byte order in integer value. + + \param [in] value Value to reverse + \return Reversed value + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __REV(uint32_t value) +{ +#if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5) + return __builtin_bswap32(value); +#else + uint32_t result; + + __ASM volatile ("rev %0, %1" : __CMSIS_GCC_OUT_REG (result) : __CMSIS_GCC_USE_REG (value) ); + return(result); +#endif +} + + +/** \brief Reverse byte order (16 bit) + + This function reverses the byte order in two unsigned short values. + + \param [in] value Value to reverse + \return Reversed value + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __REV16(uint32_t value) +{ + uint32_t result; + + __ASM volatile ("rev16 %0, %1" : __CMSIS_GCC_OUT_REG (result) : __CMSIS_GCC_USE_REG (value) ); + return(result); +} + + +/** \brief Reverse byte order in signed short value + + This function reverses the byte order in a signed short value with sign extension to integer. + + \param [in] value Value to reverse + \return Reversed value + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE int32_t __REVSH(int32_t value) +{ +#if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8) + return (short)__builtin_bswap16(value); +#else + uint32_t result; + + __ASM volatile ("revsh %0, %1" : __CMSIS_GCC_OUT_REG (result) : __CMSIS_GCC_USE_REG (value) ); + return(result); +#endif +} + + +/** \brief Rotate Right in unsigned value (32 bit) + + This function Rotate Right (immediate) provides the value of the contents of a register rotated by a variable number of bits. + + \param [in] value Value to rotate + \param [in] value Number of Bits to rotate + \return Rotated value + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __ROR(uint32_t op1, uint32_t op2) +{ + return (op1 >> op2) | (op1 << (32 - op2)); +} + + +/** \brief Breakpoint + + This function causes the processor to enter Debug state. + Debug tools can use this to investigate system state when the instruction at a particular address is reached. + + \param [in] value is ignored by the processor. + If required, a debugger can use it to store additional information about the breakpoint. + */ +#define __BKPT(value) __ASM volatile ("bkpt "#value) + + +#if (__CORTEX_M >= 0x03) || (__CORTEX_SC >= 300) + +/** \brief Reverse bit order of value + + This function reverses the bit order of the given value. + + \param [in] value Value to reverse + \return Reversed value + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __RBIT(uint32_t value) +{ + uint32_t result; + + __ASM volatile ("rbit %0, %1" : "=r" (result) : "r" (value) ); + return(result); +} + + +/** \brief LDR Exclusive (8 bit) + + This function executes a exclusive LDR instruction for 8 bit value. + + \param [in] ptr Pointer to data + \return value of type uint8_t at (*ptr) + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE uint8_t __LDREXB(volatile uint8_t *addr) +{ + uint32_t result; + +#if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8) + __ASM volatile ("ldrexb %0, %1" : "=r" (result) : "Q" (*addr) ); +#else + /* Prior to GCC 4.8, "Q" will be expanded to [rx, #0] which is not + accepted by assembler. So has to use following less efficient pattern. + */ + __ASM volatile ("ldrexb %0, [%1]" : "=r" (result) : "r" (addr) : "memory" ); +#endif + return ((uint8_t) result); /* Add explicit type cast here */ +} + + +/** \brief LDR Exclusive (16 bit) + + This function executes a exclusive LDR instruction for 16 bit values. + + \param [in] ptr Pointer to data + \return value of type uint16_t at (*ptr) + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE uint16_t __LDREXH(volatile uint16_t *addr) +{ + uint32_t result; + +#if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8) + __ASM volatile ("ldrexh %0, %1" : "=r" (result) : "Q" (*addr) ); +#else + /* Prior to GCC 4.8, "Q" will be expanded to [rx, #0] which is not + accepted by assembler. So has to use following less efficient pattern. + */ + __ASM volatile ("ldrexh %0, [%1]" : "=r" (result) : "r" (addr) : "memory" ); +#endif + return ((uint16_t) result); /* Add explicit type cast here */ +} + + +/** \brief LDR Exclusive (32 bit) + + This function executes a exclusive LDR instruction for 32 bit values. + + \param [in] ptr Pointer to data + \return value of type uint32_t at (*ptr) + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __LDREXW(volatile uint32_t *addr) +{ + uint32_t result; + + __ASM volatile ("ldrex %0, %1" : "=r" (result) : "Q" (*addr) ); + return(result); +} + + +/** \brief STR Exclusive (8 bit) + + This function executes a exclusive STR instruction for 8 bit values. + + \param [in] value Value to store + \param [in] ptr Pointer to location + \return 0 Function succeeded + \return 1 Function failed + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __STREXB(uint8_t value, volatile uint8_t *addr) +{ + uint32_t result; + + __ASM volatile ("strexb %0, %2, %1" : "=&r" (result), "=Q" (*addr) : "r" ((uint32_t)value) ); + return(result); +} + + +/** \brief STR Exclusive (16 bit) + + This function executes a exclusive STR instruction for 16 bit values. + + \param [in] value Value to store + \param [in] ptr Pointer to location + \return 0 Function succeeded + \return 1 Function failed + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __STREXH(uint16_t value, volatile uint16_t *addr) +{ + uint32_t result; + + __ASM volatile ("strexh %0, %2, %1" : "=&r" (result), "=Q" (*addr) : "r" ((uint32_t)value) ); + return(result); +} + + +/** \brief STR Exclusive (32 bit) + + This function executes a exclusive STR instruction for 32 bit values. + + \param [in] value Value to store + \param [in] ptr Pointer to location + \return 0 Function succeeded + \return 1 Function failed + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __STREXW(uint32_t value, volatile uint32_t *addr) +{ + uint32_t result; + + __ASM volatile ("strex %0, %2, %1" : "=&r" (result), "=Q" (*addr) : "r" (value) ); + return(result); +} + + +/** \brief Remove the exclusive lock + + This function removes the exclusive lock which is created by LDREX. + + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE void __CLREX(void) +{ + __ASM volatile ("clrex" ::: "memory"); +} + + +/** \brief Signed Saturate + + This function saturates a signed value. + + \param [in] value Value to be saturated + \param [in] sat Bit position to saturate to (1..32) + \return Saturated value + */ +#define __SSAT(ARG1,ARG2) \ +({ \ + uint32_t __RES, __ARG1 = (ARG1); \ + __ASM ("ssat %0, %1, %2" : "=r" (__RES) : "I" (ARG2), "r" (__ARG1) ); \ + __RES; \ + }) + + +/** \brief Unsigned Saturate + + This function saturates an unsigned value. + + \param [in] value Value to be saturated + \param [in] sat Bit position to saturate to (0..31) + \return Saturated value + */ +#define __USAT(ARG1,ARG2) \ +({ \ + uint32_t __RES, __ARG1 = (ARG1); \ + __ASM ("usat %0, %1, %2" : "=r" (__RES) : "I" (ARG2), "r" (__ARG1) ); \ + __RES; \ + }) + + +/** \brief Count leading zeros + + This function counts the number of leading zeros of a data value. + + \param [in] value Value to count the leading zeros + \return number of leading zeros in value + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE uint8_t __CLZ(uint32_t value) +{ + uint32_t result; + + __ASM volatile ("clz %0, %1" : "=r" (result) : "r" (value) ); + return ((uint8_t) result); /* Add explicit type cast here */ +} + + +/** \brief Rotate Right with Extend (32 bit) + + This function moves each bit of a bitstring right by one bit. The carry input is shifted in at the left end of the bitstring. + + \param [in] value Value to rotate + \return Rotated value + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __RRX(uint32_t value) +{ + uint32_t result; + + __ASM volatile ("rrx %0, %1" : __CMSIS_GCC_OUT_REG (result) : __CMSIS_GCC_USE_REG (value) ); + return(result); +} + + +/** \brief LDRT Unprivileged (8 bit) + + This function executes a Unprivileged LDRT instruction for 8 bit value. + + \param [in] ptr Pointer to data + \return value of type uint8_t at (*ptr) + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE uint8_t __LDRBT(volatile uint8_t *addr) +{ + uint32_t result; + +#if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8) + __ASM volatile ("ldrbt %0, %1" : "=r" (result) : "Q" (*addr) ); +#else + /* Prior to GCC 4.8, "Q" will be expanded to [rx, #0] which is not + accepted by assembler. So has to use following less efficient pattern. + */ + __ASM volatile ("ldrbt %0, [%1]" : "=r" (result) : "r" (addr) : "memory" ); +#endif + return ((uint8_t) result); /* Add explicit type cast here */ +} + + +/** \brief LDRT Unprivileged (16 bit) + + This function executes a Unprivileged LDRT instruction for 16 bit values. + + \param [in] ptr Pointer to data + \return value of type uint16_t at (*ptr) + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE uint16_t __LDRHT(volatile uint16_t *addr) +{ + uint32_t result; + +#if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8) + __ASM volatile ("ldrht %0, %1" : "=r" (result) : "Q" (*addr) ); +#else + /* Prior to GCC 4.8, "Q" will be expanded to [rx, #0] which is not + accepted by assembler. So has to use following less efficient pattern. + */ + __ASM volatile ("ldrht %0, [%1]" : "=r" (result) : "r" (addr) : "memory" ); +#endif + return ((uint16_t) result); /* Add explicit type cast here */ +} + + +/** \brief LDRT Unprivileged (32 bit) + + This function executes a Unprivileged LDRT instruction for 32 bit values. + + \param [in] ptr Pointer to data + \return value of type uint32_t at (*ptr) + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __LDRT(volatile uint32_t *addr) +{ + uint32_t result; + + __ASM volatile ("ldrt %0, %1" : "=r" (result) : "Q" (*addr) ); + return(result); +} + + +/** \brief STRT Unprivileged (8 bit) + + This function executes a Unprivileged STRT instruction for 8 bit values. + + \param [in] value Value to store + \param [in] ptr Pointer to location + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE void __STRBT(uint8_t value, volatile uint8_t *addr) +{ + __ASM volatile ("strbt %1, %0" : "=Q" (*addr) : "r" ((uint32_t)value) ); +} + + +/** \brief STRT Unprivileged (16 bit) + + This function executes a Unprivileged STRT instruction for 16 bit values. + + \param [in] value Value to store + \param [in] ptr Pointer to location + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE void __STRHT(uint16_t value, volatile uint16_t *addr) +{ + __ASM volatile ("strht %1, %0" : "=Q" (*addr) : "r" ((uint32_t)value) ); +} + + +/** \brief STRT Unprivileged (32 bit) + + This function executes a Unprivileged STRT instruction for 32 bit values. + + \param [in] value Value to store + \param [in] ptr Pointer to location + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE void __STRT(uint32_t value, volatile uint32_t *addr) +{ + __ASM volatile ("strt %1, %0" : "=Q" (*addr) : "r" (value) ); +} + +#endif /* (__CORTEX_M >= 0x03) || (__CORTEX_SC >= 300) */ + + +#elif defined ( __ICCARM__ ) /*------------------ ICC Compiler -------------------*/ +/* IAR iccarm specific functions */ +#include + + +#elif defined ( __TMS470__ ) /*---------------- TI CCS Compiler ------------------*/ +/* TI CCS specific functions */ +#include + + +#elif defined ( __TASKING__ ) /*------------------ TASKING Compiler --------------*/ +/* TASKING carm specific functions */ +/* + * The CMSIS functions have been implemented as intrinsics in the compiler. + * Please use "carm -?i" to get an up to date list of all intrinsics, + * Including the CMSIS ones. + */ + + +#elif defined ( __CSMC__ ) /*------------------ COSMIC Compiler -------------------*/ +/* Cosmic specific functions */ +#include + +#endif + +/*@}*/ /* end of group CMSIS_Core_InstructionInterface */ + +#endif /* __CORE_CMINSTR_H */ diff --git a/platform/mcu/xr871/include/driver/cmsis/core_cmSimd.h b/platform/mcu/xr871/include/driver/cmsis/core_cmSimd.h new file mode 100644 index 0000000000..ee58eee56d --- /dev/null +++ b/platform/mcu/xr871/include/driver/cmsis/core_cmSimd.h @@ -0,0 +1,697 @@ +/**************************************************************************//** + * @file core_cmSimd.h + * @brief CMSIS Cortex-M SIMD Header File + * @version V4.00 + * @date 22. August 2014 + * + * @note + * + ******************************************************************************/ +/* Copyright (c) 2009 - 2014 ARM LIMITED + + All rights reserved. + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + - Neither the name of ARM nor the names of its contributors may be used + to endorse or promote products derived from this software without + specific prior written permission. + * + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDERS AND CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + ---------------------------------------------------------------------------*/ + + +#if defined ( __ICCARM__ ) + #pragma system_include /* treat file as system include file for MISRA check */ +#endif + +#ifndef __CORE_CMSIMD_H +#define __CORE_CMSIMD_H + +#ifdef __cplusplus + extern "C" { +#endif + + +/******************************************************************************* + * Hardware Abstraction Layer + ******************************************************************************/ + + +/* ################### Compiler specific Intrinsics ########################### */ +/** \defgroup CMSIS_SIMD_intrinsics CMSIS SIMD Intrinsics + Access to dedicated SIMD instructions + @{ +*/ + +#if defined ( __CC_ARM ) /*------------------RealView Compiler -----------------*/ +/* ARM armcc specific functions */ +#define __SADD8 __sadd8 +#define __QADD8 __qadd8 +#define __SHADD8 __shadd8 +#define __UADD8 __uadd8 +#define __UQADD8 __uqadd8 +#define __UHADD8 __uhadd8 +#define __SSUB8 __ssub8 +#define __QSUB8 __qsub8 +#define __SHSUB8 __shsub8 +#define __USUB8 __usub8 +#define __UQSUB8 __uqsub8 +#define __UHSUB8 __uhsub8 +#define __SADD16 __sadd16 +#define __QADD16 __qadd16 +#define __SHADD16 __shadd16 +#define __UADD16 __uadd16 +#define __UQADD16 __uqadd16 +#define __UHADD16 __uhadd16 +#define __SSUB16 __ssub16 +#define __QSUB16 __qsub16 +#define __SHSUB16 __shsub16 +#define __USUB16 __usub16 +#define __UQSUB16 __uqsub16 +#define __UHSUB16 __uhsub16 +#define __SASX __sasx +#define __QASX __qasx +#define __SHASX __shasx +#define __UASX __uasx +#define __UQASX __uqasx +#define __UHASX __uhasx +#define __SSAX __ssax +#define __QSAX __qsax +#define __SHSAX __shsax +#define __USAX __usax +#define __UQSAX __uqsax +#define __UHSAX __uhsax +#define __USAD8 __usad8 +#define __USADA8 __usada8 +#define __SSAT16 __ssat16 +#define __USAT16 __usat16 +#define __UXTB16 __uxtb16 +#define __UXTAB16 __uxtab16 +#define __SXTB16 __sxtb16 +#define __SXTAB16 __sxtab16 +#define __SMUAD __smuad +#define __SMUADX __smuadx +#define __SMLAD __smlad +#define __SMLADX __smladx +#define __SMLALD __smlald +#define __SMLALDX __smlaldx +#define __SMUSD __smusd +#define __SMUSDX __smusdx +#define __SMLSD __smlsd +#define __SMLSDX __smlsdx +#define __SMLSLD __smlsld +#define __SMLSLDX __smlsldx +#define __SEL __sel +#define __QADD __qadd +#define __QSUB __qsub + +#define __PKHBT(ARG1,ARG2,ARG3) ( ((((uint32_t)(ARG1)) ) & 0x0000FFFFUL) | \ + ((((uint32_t)(ARG2)) << (ARG3)) & 0xFFFF0000UL) ) + +#define __PKHTB(ARG1,ARG2,ARG3) ( ((((uint32_t)(ARG1)) ) & 0xFFFF0000UL) | \ + ((((uint32_t)(ARG2)) >> (ARG3)) & 0x0000FFFFUL) ) + +#define __SMMLA(ARG1,ARG2,ARG3) ( (int32_t)((((int64_t)(ARG1) * (ARG2)) + \ + ((int64_t)(ARG3) << 32) ) >> 32)) + + +#elif defined ( __GNUC__ ) /*------------------ GNU Compiler ---------------------*/ +/* GNU gcc specific functions */ +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __SADD8(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("sadd8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __QADD8(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("qadd8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __SHADD8(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("shadd8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __UADD8(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("uadd8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __UQADD8(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("uqadd8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __UHADD8(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("uhadd8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + + +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __SSUB8(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("ssub8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __QSUB8(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("qsub8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __SHSUB8(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("shsub8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __USUB8(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("usub8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __UQSUB8(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("uqsub8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __UHSUB8(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("uhsub8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + + +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __SADD16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("sadd16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __QADD16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("qadd16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __SHADD16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("shadd16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __UADD16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("uadd16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __UQADD16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("uqadd16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __UHADD16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("uhadd16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __SSUB16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("ssub16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __QSUB16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("qsub16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __SHSUB16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("shsub16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __USUB16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("usub16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __UQSUB16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("uqsub16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __UHSUB16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("uhsub16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __SASX(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("sasx %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __QASX(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("qasx %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __SHASX(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("shasx %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __UASX(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("uasx %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __UQASX(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("uqasx %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __UHASX(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("uhasx %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __SSAX(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("ssax %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __QSAX(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("qsax %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __SHSAX(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("shsax %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __USAX(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("usax %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __UQSAX(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("uqsax %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __UHSAX(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("uhsax %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __USAD8(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("usad8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __USADA8(uint32_t op1, uint32_t op2, uint32_t op3) +{ + uint32_t result; + + __ASM volatile ("usada8 %0, %1, %2, %3" : "=r" (result) : "r" (op1), "r" (op2), "r" (op3) ); + return(result); +} + +#define __SSAT16(ARG1,ARG2) \ +({ \ + uint32_t __RES, __ARG1 = (ARG1); \ + __ASM ("ssat16 %0, %1, %2" : "=r" (__RES) : "I" (ARG2), "r" (__ARG1) ); \ + __RES; \ + }) + +#define __USAT16(ARG1,ARG2) \ +({ \ + uint32_t __RES, __ARG1 = (ARG1); \ + __ASM ("usat16 %0, %1, %2" : "=r" (__RES) : "I" (ARG2), "r" (__ARG1) ); \ + __RES; \ + }) + +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __UXTB16(uint32_t op1) +{ + uint32_t result; + + __ASM volatile ("uxtb16 %0, %1" : "=r" (result) : "r" (op1)); + return(result); +} + +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __UXTAB16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("uxtab16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __SXTB16(uint32_t op1) +{ + uint32_t result; + + __ASM volatile ("sxtb16 %0, %1" : "=r" (result) : "r" (op1)); + return(result); +} + +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __SXTAB16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("sxtab16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __SMUAD (uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("smuad %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __SMUADX (uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("smuadx %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __SMLAD (uint32_t op1, uint32_t op2, uint32_t op3) +{ + uint32_t result; + + __ASM volatile ("smlad %0, %1, %2, %3" : "=r" (result) : "r" (op1), "r" (op2), "r" (op3) ); + return(result); +} + +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __SMLADX (uint32_t op1, uint32_t op2, uint32_t op3) +{ + uint32_t result; + + __ASM volatile ("smladx %0, %1, %2, %3" : "=r" (result) : "r" (op1), "r" (op2), "r" (op3) ); + return(result); +} + +__attribute__( ( always_inline ) ) __STATIC_INLINE uint64_t __SMLALD (uint32_t op1, uint32_t op2, uint64_t acc) +{ + union llreg_u{ + uint32_t w32[2]; + uint64_t w64; + } llr; + llr.w64 = acc; + +#ifndef __ARMEB__ // Little endian + __ASM volatile ("smlald %0, %1, %2, %3" : "=r" (llr.w32[0]), "=r" (llr.w32[1]): "r" (op1), "r" (op2) , "0" (llr.w32[0]), "1" (llr.w32[1]) ); +#else // Big endian + __ASM volatile ("smlald %0, %1, %2, %3" : "=r" (llr.w32[1]), "=r" (llr.w32[0]): "r" (op1), "r" (op2) , "0" (llr.w32[1]), "1" (llr.w32[0]) ); +#endif + + return(llr.w64); +} + +__attribute__( ( always_inline ) ) __STATIC_INLINE uint64_t __SMLALDX (uint32_t op1, uint32_t op2, uint64_t acc) +{ + union llreg_u{ + uint32_t w32[2]; + uint64_t w64; + } llr; + llr.w64 = acc; + +#ifndef __ARMEB__ // Little endian + __ASM volatile ("smlaldx %0, %1, %2, %3" : "=r" (llr.w32[0]), "=r" (llr.w32[1]): "r" (op1), "r" (op2) , "0" (llr.w32[0]), "1" (llr.w32[1]) ); +#else // Big endian + __ASM volatile ("smlaldx %0, %1, %2, %3" : "=r" (llr.w32[1]), "=r" (llr.w32[0]): "r" (op1), "r" (op2) , "0" (llr.w32[1]), "1" (llr.w32[0]) ); +#endif + + return(llr.w64); +} + +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __SMUSD (uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("smusd %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __SMUSDX (uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("smusdx %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __SMLSD (uint32_t op1, uint32_t op2, uint32_t op3) +{ + uint32_t result; + + __ASM volatile ("smlsd %0, %1, %2, %3" : "=r" (result) : "r" (op1), "r" (op2), "r" (op3) ); + return(result); +} + +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __SMLSDX (uint32_t op1, uint32_t op2, uint32_t op3) +{ + uint32_t result; + + __ASM volatile ("smlsdx %0, %1, %2, %3" : "=r" (result) : "r" (op1), "r" (op2), "r" (op3) ); + return(result); +} + +__attribute__( ( always_inline ) ) __STATIC_INLINE uint64_t __SMLSLD (uint32_t op1, uint32_t op2, uint64_t acc) +{ + union llreg_u{ + uint32_t w32[2]; + uint64_t w64; + } llr; + llr.w64 = acc; + +#ifndef __ARMEB__ // Little endian + __ASM volatile ("smlsld %0, %1, %2, %3" : "=r" (llr.w32[0]), "=r" (llr.w32[1]): "r" (op1), "r" (op2) , "0" (llr.w32[0]), "1" (llr.w32[1]) ); +#else // Big endian + __ASM volatile ("smlsld %0, %1, %2, %3" : "=r" (llr.w32[1]), "=r" (llr.w32[0]): "r" (op1), "r" (op2) , "0" (llr.w32[1]), "1" (llr.w32[0]) ); +#endif + + return(llr.w64); +} + +__attribute__( ( always_inline ) ) __STATIC_INLINE uint64_t __SMLSLDX (uint32_t op1, uint32_t op2, uint64_t acc) +{ + union llreg_u{ + uint32_t w32[2]; + uint64_t w64; + } llr; + llr.w64 = acc; + +#ifndef __ARMEB__ // Little endian + __ASM volatile ("smlsldx %0, %1, %2, %3" : "=r" (llr.w32[0]), "=r" (llr.w32[1]): "r" (op1), "r" (op2) , "0" (llr.w32[0]), "1" (llr.w32[1]) ); +#else // Big endian + __ASM volatile ("smlsldx %0, %1, %2, %3" : "=r" (llr.w32[1]), "=r" (llr.w32[0]): "r" (op1), "r" (op2) , "0" (llr.w32[1]), "1" (llr.w32[0]) ); +#endif + + return(llr.w64); +} + +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __SEL (uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("sel %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __QADD(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("qadd %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __QSUB(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("qsub %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +#define __PKHBT(ARG1,ARG2,ARG3) \ +({ \ + uint32_t __RES, __ARG1 = (ARG1), __ARG2 = (ARG2); \ + __ASM ("pkhbt %0, %1, %2, lsl %3" : "=r" (__RES) : "r" (__ARG1), "r" (__ARG2), "I" (ARG3) ); \ + __RES; \ + }) + +#define __PKHTB(ARG1,ARG2,ARG3) \ +({ \ + uint32_t __RES, __ARG1 = (ARG1), __ARG2 = (ARG2); \ + if (ARG3 == 0) \ + __ASM ("pkhtb %0, %1, %2" : "=r" (__RES) : "r" (__ARG1), "r" (__ARG2) ); \ + else \ + __ASM ("pkhtb %0, %1, %2, asr %3" : "=r" (__RES) : "r" (__ARG1), "r" (__ARG2), "I" (ARG3) ); \ + __RES; \ + }) + +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __SMMLA (int32_t op1, int32_t op2, int32_t op3) +{ + int32_t result; + + __ASM volatile ("smmla %0, %1, %2, %3" : "=r" (result): "r" (op1), "r" (op2), "r" (op3) ); + return(result); +} + + +#elif defined ( __ICCARM__ ) /*------------------ ICC Compiler -------------------*/ +/* IAR iccarm specific functions */ +#include + + +#elif defined ( __TMS470__ ) /*---------------- TI CCS Compiler ------------------*/ +/* TI CCS specific functions */ +#include + + +#elif defined ( __TASKING__ ) /*------------------ TASKING Compiler --------------*/ +/* TASKING carm specific functions */ +/* not yet supported */ + + +#elif defined ( __CSMC__ ) /*------------------ COSMIC Compiler -------------------*/ +/* Cosmic specific functions */ +#include + +#endif + +/*@} end of group CMSIS_SIMD_intrinsics */ + + +#ifdef __cplusplus +} +#endif + +#endif /* __CORE_CMSIMD_H */ diff --git a/platform/mcu/xr871/include/driver/hal_board.h b/platform/mcu/xr871/include/driver/hal_board.h new file mode 100644 index 0000000000..e705262b0d --- /dev/null +++ b/platform/mcu/xr871/include/driver/hal_board.h @@ -0,0 +1,67 @@ +/** + * @file hal_board.h + * @author XRADIO IOT WLAN Team + */ + +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _DRIVER_HAL_BOARD_H_ +#define _DRIVER_HAL_BOARD_H_ + +#include "driver/chip/hal_def.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief HAL board I/O control request + * + * @note Every request MUST be related to board's configuration + */ +typedef enum { + HAL_BIR_PINMUX_INIT, /* init pinmux */ + HAL_BIR_PINMUX_DEINIT, /* deinit pinmux */ + HAL_BIR_CHIP_CLOCK_INIT, /* init chip clock */ + HAL_BIR_GET_CFG, /* get configuration */ +} HAL_BoardIoctlReq; + +/** @brief type define of HAL board I/O control callback function */ +typedef HAL_Status (*HAL_BoardIoctlCb)(HAL_BoardIoctlReq req, uint32_t param0, uint32_t param1); + +HAL_BoardIoctlCb HAL_BoardIoctlCbRegister(HAL_BoardIoctlCb cb); + +HAL_Status HAL_BoardIoctl(HAL_BoardIoctlReq req, uint32_t param0, uint32_t param1); + +#ifdef __cplusplus +} +#endif + +#endif /* _DRIVER_HAL_BOARD_H_ */ diff --git a/platform/mcu/xr871/include/driver/hal_dev.h b/platform/mcu/xr871/include/driver/hal_dev.h new file mode 100644 index 0000000000..bef41be259 --- /dev/null +++ b/platform/mcu/xr871/include/driver/hal_dev.h @@ -0,0 +1,94 @@ +/** + * @file hal_dev.h + * @author XRADIO IOT WLAN Team + */ + +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _DRIVER_HAL_DEV_H_ +#define _DRIVER_HAL_DEV_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief HAL device number + * + * HAL device number is made up of major number and minor number. + * - Major number is defined as HAL_DEV_MAJOR_XXX + * - minor number is defined by the specific driver + */ +typedef uint32_t HAL_Dev_t; + +#define HAL_DEV_MINOR_BITS 16 +#define HAL_DEV_MINOR_MASK ((1U << HAL_DEV_MINOR_BITS) - 1) + +/** @brief Get major number from device number */ +#define HAL_DEV_MAJOR(dev) ((uint32_t)((dev) >> HAL_DEV_MINOR_BITS)) + +/** @brief Get minor number from device number */ +#define HAL_DEV_MINOR(dev) ((uint32_t)((dev) & HAL_DEV_MINOR_MASK)) + +/** @brief Make device number from major number and minor number */ +#define HAL_MKDEV(major, minor) \ + (((HAL_Dev_t)(major) << HAL_DEV_MINOR_BITS) | \ + ((HAL_Dev_t)(minor) & HAL_DEV_MINOR_MASK)) + +/** + * @brief Major number of device + */ +enum { + /* peripheral interface of chip */ + HAL_DEV_MAJOR_UART = 0U, + HAL_DEV_MAJOR_I2C, + HAL_DEV_MAJOR_SPI, + HAL_DEV_MAJOR_IRRX, + HAL_DEV_MAJOR_IRTX, + HAL_DEV_MAJOR_I2S, + HAL_DEV_MAJOR_DMIC, + HAL_DEV_MAJOR_ADC, + HAL_DEV_MAJOR_PWM, + HAL_DEV_MAJOR_FLASHC, /* FLASH controller interface */ + HAL_DEV_MAJOR_SDC, + HAL_DEV_MAJOR_CSI, + + /* external component */ + HAL_DEV_MAJOR_FLASH = 100U, + HAL_DEV_MAJOR_AUDIO_CODEC, +}; + +#ifdef __cplusplus +} +#endif + +#endif /* _DRIVER_HAL_DEV_H_ */ diff --git a/platform/mcu/xr871/include/efpg/efpg.h b/platform/mcu/xr871/include/efpg/efpg.h new file mode 100644 index 0000000000..ac2691d255 --- /dev/null +++ b/platform/mcu/xr871/include/efpg/efpg.h @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _EFPG_EFPG_H_ +#define _EFPG_EFPG_H_ + +#include +#include "driver/chip/hal_uart.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief EFPG field definition + */ +typedef enum efpg_field { + EFPG_FIELD_HOSC = 0, /* data buffer size: 1 byte */ + EFPG_FIELD_BOOT = 1, /* data buffer size: 32 bytes */ + EFPG_FIELD_DCXO = 2, /* data buffer size: 1 byte */ + EFPG_FIELD_POUT = 3, /* data buffer size: 3 bytes */ + EFPG_FIELD_MAC = 4, /* data buffer size: 6 bytes */ + EFPG_FIELD_CHIPID = 5, /* data buffer size: 16 bytes */ + EFPG_FIELD_UA = 6, /* data buffer size: 601 bytes */ + EFPG_FIELD_NUM = 7, +} efpg_field_t; + +/** + * @brief EFPG HOSC value definition + */ +#define EFPG_HOSC_24M (0x06) +#define EFPG_HOSC_26M (0x03) +#define EFPG_HOSC_40M (0x0C) +#define EFPG_HOSC_52M (0x09) + +/** @brief Type define of EFPG callback function */ +typedef void (*efpg_cb_t)(void); + +int efpg_start(uint8_t *key, uint8_t key_len, UART_ID uart_id, efpg_cb_t start_cb, efpg_cb_t stop_cb); + +int efpg_read(efpg_field_t field, uint8_t *data); + +int efpg_read_ua(uint32_t start, uint32_t num, uint8_t *data); + +#ifdef __cplusplus +} +#endif + +#endif /* _EFPG_EFPG_H_ */ \ No newline at end of file diff --git a/platform/mcu/xr871/include/fs/fatfs/ff.h b/platform/mcu/xr871/include/fs/fatfs/ff.h new file mode 100644 index 0000000000..90659b7836 --- /dev/null +++ b/platform/mcu/xr871/include/fs/fatfs/ff.h @@ -0,0 +1,365 @@ +/*----------------------------------------------------------------------------/ +/ FatFs - Generic FAT Filesystem module R0.13 / +/-----------------------------------------------------------------------------/ +/ +/ Copyright (C) 2017, ChaN, all right reserved. +/ +/ FatFs module is an open source software. Redistribution and use of FatFs in +/ source and binary forms, with or without modification, are permitted provided +/ that the following condition is met: + +/ 1. Redistributions of source code must retain the above copyright notice, +/ this condition and the following disclaimer. +/ +/ This software is provided by the copyright holder and contributors "AS IS" +/ and any warranties related to this software are DISCLAIMED. +/ The copyright owner or contributors be NOT LIABLE for any damages caused +/ by use of this software. +/ +/----------------------------------------------------------------------------*/ + + +#ifndef FF_DEFINED +#define FF_DEFINED 87030 /* Revision ID */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include /* Standard errno definations */ +#include "integer.h" /* Basic integer types */ +#include "ffconf.h" /* FatFs configuration options */ + +#if FF_DEFINED != FFCONF_DEF +#error Wrong configuration file (ffconf.h). +#endif + + + +/* Definitions of volume management */ + +#if FF_MULTI_PARTITION /* Multiple partition configuration */ +typedef struct { + BYTE pd; /* Physical drive number */ + BYTE pt; /* Partition: 0:Auto detect, 1-4:Forced partition) */ +} PARTITION; +extern PARTITION VolToPart[]; /* Volume - Partition resolution table */ +#endif + + + +/* Type of path name strings on FatFs API */ + +#if FF_LFN_UNICODE && FF_USE_LFN /* Unicode (UTF-16) string */ +#ifndef _INC_TCHAR +typedef WCHAR TCHAR; +#define _T(x) L ## x +#define _TEXT(x) L ## x +#define _INC_TCHAR +#endif +#else /* ANSI/OEM string */ +#ifndef _INC_TCHAR +typedef char TCHAR; +#define _T(x) x +#define _TEXT(x) x +#define _INC_TCHAR +#endif +#endif + + + +/* Type of file size variables */ + +#if FF_FS_EXFAT +#if !FF_USE_LFN +#error LFN must be enabled when enable exFAT +#endif +typedef QWORD FSIZE_t; +#else +typedef DWORD FSIZE_t; +#endif + + + +/* Filesystem object structure (FATFS) */ + +typedef struct { + BYTE fs_type; /* Filesystem type (0:N/A) */ + BYTE pdrv; /* Physical drive number */ + BYTE n_fats; /* Number of FATs (1 or 2) */ + BYTE wflag; /* win[] flag (b0:dirty) */ + BYTE fsi_flag; /* FSINFO flags (b7:disabled, b0:dirty) */ + WORD id; /* Volume mount ID */ + WORD n_rootdir; /* Number of root directory entries (FAT12/16) */ + WORD csize; /* Cluster size [sectors] */ +#if FF_MAX_SS != FF_MIN_SS + WORD ssize; /* Sector size (512, 1024, 2048 or 4096) */ +#endif +#if FF_USE_LFN + WCHAR* lfnbuf; /* LFN working buffer */ +#endif +#if FF_FS_EXFAT + BYTE* dirbuf; /* Directory entry block scratchpad buffer for exFAT */ +#endif +#if FF_FS_REENTRANT + FF_SYNC_t sobj; /* Identifier of sync object */ +#endif +#if !FF_FS_READONLY + DWORD last_clst; /* Last allocated cluster */ + DWORD free_clst; /* Number of free clusters */ +#endif +#if FF_FS_RPATH + DWORD cdir; /* Current directory start cluster (0:root) */ +#if FF_FS_EXFAT + DWORD cdc_scl; /* Containing directory start cluster (invalid when cdir is 0) */ + DWORD cdc_size; /* b31-b8:Size of containing directory, b7-b0: Chain status */ + DWORD cdc_ofs; /* Offset in the containing directory (invalid when cdir is 0) */ +#endif +#endif + DWORD n_fatent; /* Number of FAT entries (number of clusters + 2) */ + DWORD fsize; /* Size of an FAT [sectors] */ + DWORD volbase; /* Volume base sector */ + DWORD fatbase; /* FAT base sector */ + DWORD dirbase; /* Root directory base sector/cluster */ + DWORD database; /* Data base sector */ + DWORD winsect; /* Current sector appearing in the win[] */ + BYTE win[FF_MAX_SS]; /* Disk access window for Directory, FAT (and file data at tiny cfg) */ +} FATFS; + + + +/* Object ID and allocation information (FFOBJID) */ + +typedef struct { + FATFS* fs; /* Pointer to the hosting volume of this object */ + WORD id; /* Hosting volume mount ID */ + BYTE attr; /* Object attribute */ + BYTE stat; /* Object chain status (b1-0: =0:not contiguous, =2:contiguous, =3:flagmented in this session, b2:sub-directory stretched) */ + DWORD sclust; /* Object data start cluster (0:no cluster or root directory) */ + FSIZE_t objsize; /* Object size (valid when sclust != 0) */ +#if FF_FS_EXFAT + DWORD n_cont; /* Size of first fragment - 1 (valid when stat == 3) */ + DWORD n_frag; /* Size of last fragment needs to be written to FAT (valid when not zero) */ + DWORD c_scl; /* Containing directory start cluster (valid when sclust != 0) */ + DWORD c_size; /* b31-b8:Size of containing directory, b7-b0: Chain status (valid when c_scl != 0) */ + DWORD c_ofs; /* Offset in the containing directory (valid when file object and sclust != 0) */ +#endif +#if FF_FS_LOCK + UINT lockid; /* File lock ID origin from 1 (index of file semaphore table Files[]) */ +#endif +} FFOBJID; + + + +/* File object structure (FIL) */ + +typedef struct { + FFOBJID obj; /* Object identifier (must be the 1st member to detect invalid object pointer) */ + BYTE flag; /* File status flags */ + BYTE err; /* Abort flag (error code) */ + FSIZE_t fptr; /* File read/write pointer (Zeroed on file open) */ + DWORD clust; /* Current cluster of fpter (invalid when fptr is 0) */ + DWORD sect; /* Sector number appearing in buf[] (0:invalid) */ +#if !FF_FS_READONLY + DWORD dir_sect; /* Sector number containing the directory entry (not used at exFAT) */ + BYTE* dir_ptr; /* Pointer to the directory entry in the win[] (not used at exFAT) */ +#endif +#if FF_USE_FASTSEEK + DWORD* cltbl; /* Pointer to the cluster link map table (nulled on open, set by application) */ +#endif +#if !FF_FS_TINY + BYTE buf[FF_MAX_SS]; /* File private data read/write window */ +#endif +} FIL; + + + +/* Directory object structure (DIR) */ + +typedef struct { + FFOBJID obj; /* Object identifier */ + DWORD dptr; /* Current read/write offset */ + DWORD clust; /* Current cluster */ + DWORD sect; /* Current sector (0:Read operation has terminated) */ + BYTE* dir; /* Pointer to the directory item in the win[] */ + BYTE fn[12]; /* SFN (in/out) {body[8],ext[3],status[1]} */ +#if FF_USE_LFN + DWORD blk_ofs; /* Offset of current entry block being processed (0xFFFFFFFF:Invalid) */ +#endif +#if FF_USE_FIND + const TCHAR* pat; /* Pointer to the name matching pattern */ +#endif +} FF_DIR; + + + +/* File information structure (FILINFO) */ + +typedef struct { + FSIZE_t fsize; /* File size */ + WORD fdate; /* Modified date */ + WORD ftime; /* Modified time */ + BYTE fattrib; /* File attribute */ +#if FF_USE_LFN + TCHAR altname[13]; /* Altenative file name */ + TCHAR fname[FF_MAX_LFN + 1]; /* Primary file name */ +#else + TCHAR fname[13]; /* File name */ +#endif +} FILINFO; + + + +/* File function return code (FRESULT) */ + +typedef enum { + FR_OK = 0, /* (0) Succeeded */ + FR_DISK_ERR = -EIO, /* (1) A hard error occurred in the low level disk I/O layer */ + FR_INT_ERR = -EIO, /* (2) Assertion failed */ + FR_NOT_READY = -ENODEV, /* (3) The physical drive cannot work */ + FR_NO_FILE = -ENOENT, /* (4) Could not find the file */ + FR_NO_PATH = -ENOENT, /* (5) Could not find the path */ + FR_INVALID_NAME = -EINVAL, /* (6) The path name format is invalid */ + FR_DENIED = -EACCES, /* (7) Access denied due to prohibited access or directory full */ + FR_EXIST = -EEXIST, /* (8) Access denied due to prohibited access */ + FR_INVALID_OBJECT = -EBADF, /* (9) The file/directory object is invalid */ + FR_WRITE_PROTECTED = -EACCES, /* (10) The physical drive is write protected */ + FR_INVALID_DRIVE = -ENXIO, /* (11) The logical drive number is invalid */ + FR_NOT_ENABLED = -ENODEV, /* (12) The volume has no work area */ + FR_NO_FILESYSTEM = -ENODEV, /* (13) There is no valid FAT volume */ + FR_MKFS_ABORTED = -EINTR, /* (14) The f_mkfs() aborted due to any problem */ + FR_TIMEOUT = -ETIMEDOUT, /* (15) Could not get a grant to access the volume within defined period */ + FR_LOCKED = -EACCES, /* (16) The operation is rejected according to the file sharing policy */ + FR_NOT_ENOUGH_CORE = -ENOMEM, /* (17) LFN working buffer could not be allocated */ + FR_TOO_MANY_OPEN_FILES = -ENFILE, /* (18) Number of open files > FF_FS_LOCK */ + FR_INVALID_PARAMETER = -EINVAL /* (19) Given parameter is invalid */ +} FRESULT; + + + +/*--------------------------------------------------------------*/ +/* FatFs module application interface */ + +FRESULT f_open (FIL* fp, const TCHAR* path, BYTE mode); /* Open or create a file */ +FRESULT f_close (FIL* fp); /* Close an open file object */ +FRESULT f_read (FIL* fp, void* buff, UINT btr, UINT* br); /* Read data from the file */ +FRESULT f_write (FIL* fp, const void* buff, UINT btw, UINT* bw); /* Write data to the file */ +FRESULT f_lseek (FIL* fp, FSIZE_t ofs); /* Move file pointer of the file object */ +FRESULT f_truncate (FIL* fp); /* Truncate the file */ +FRESULT f_sync (FIL* fp); /* Flush cached data of the writing file */ +FRESULT f_opendir (FF_DIR* dp, const TCHAR* path); /* Open a directory */ +FRESULT f_closedir (FF_DIR* dp); /* Close an open directory */ +FRESULT f_readdir (FF_DIR* dp, FILINFO* fno); /* Read a directory item */ +FRESULT f_findfirst (FF_DIR* dp, FILINFO* fno, const TCHAR* path, const TCHAR* pattern); /* Find first file */ +FRESULT f_findnext (FF_DIR* dp, FILINFO* fno); /* Find next file */ +FRESULT f_mkdir (const TCHAR* path); /* Create a sub directory */ +FRESULT f_unlink (const TCHAR* path); /* Delete an existing file or directory */ +FRESULT f_rename (const TCHAR* path_old, const TCHAR* path_new); /* Rename/Move a file or directory */ +FRESULT f_stat (const TCHAR* path, FILINFO* fno); /* Get file status */ +FRESULT f_chmod (const TCHAR* path, BYTE attr, BYTE mask); /* Change attribute of a file/dir */ +FRESULT f_utime (const TCHAR* path, const FILINFO* fno); /* Change timestamp of a file/dir */ +FRESULT f_chdir (const TCHAR* path); /* Change current directory */ +FRESULT f_chdrive (const TCHAR* path); /* Change current drive */ +FRESULT f_getcwd (TCHAR* buff, UINT len); /* Get current directory */ +FRESULT f_getfree (const TCHAR* path, DWORD* nclst, FATFS** fatfs); /* Get number of free clusters on the drive */ +FRESULT f_getlabel (const TCHAR* path, TCHAR* label, DWORD* vsn); /* Get volume label */ +FRESULT f_setlabel (const TCHAR* label); /* Set volume label */ +FRESULT f_forward (FIL* fp, UINT(*func)(const BYTE*,UINT), UINT btf, UINT* bf); /* Forward data to the stream */ +FRESULT f_expand (FIL* fp, FSIZE_t szf, BYTE opt); /* Allocate a contiguous block to the file */ +FRESULT f_mount (FATFS* fs, const TCHAR* path, BYTE opt); /* Mount/Unmount a logical drive */ +FRESULT f_mkfs (const TCHAR* path, BYTE opt, DWORD au, void* work, UINT len); /* Create a FAT volume */ +FRESULT f_fdisk (BYTE pdrv, const DWORD* szt, void* work); /* Divide a physical drive into some partitions */ +FRESULT f_setcp (WORD cp); /* Set current code page */ +int f_putc (TCHAR c, FIL* fp); /* Put a character to the file */ +int f_puts (const TCHAR* str, FIL* cp); /* Put a string to the file */ +int f_printf (FIL* fp, const TCHAR* str, ...); /* Put a formatted string to the file */ +TCHAR* f_gets (TCHAR* buff, int len, FIL* fp); /* Get a string from the file */ + +#define f_eof(fp) ((int)((fp)->fptr == (fp)->obj.objsize)) +#define f_error(fp) ((fp)->err) +#define f_tell(fp) ((fp)->fptr) +#define f_size(fp) ((fp)->obj.objsize) +#define f_rewind(fp) f_lseek((fp), 0) +#define f_rewinddir(dp) f_readdir((dp), 0) +#define f_rmdir(path) f_unlink(path) +#define f_unmount(path) f_mount(0, path, 0) + +#ifndef EOF +#define EOF (-1) +#endif + + + + +/*--------------------------------------------------------------*/ +/* Additional user defined functions */ + +/* RTC function */ +#if !FF_FS_READONLY && !FF_FS_NORTC +DWORD get_fattime (void); +#endif + +/* LFN support functions */ +#if FF_USE_LFN /* Code conversion (defined in unicode.c) */ +WCHAR ff_oem2uni (WCHAR oem, WORD cp); /* OEM code to Unicode conversion */ +WCHAR ff_uni2oem (WCHAR uni, WORD cp); /* Unicode to OEM code conversion */ +WCHAR ff_wtoupper (WCHAR uni); /* Unicode upper-case conversion */ +#endif +#if FF_USE_LFN == 3 /* Dynamic memory allocation */ +void* ff_memalloc (UINT msize); /* Allocate memory block */ +void ff_memfree (void* mblock); /* Free memory block */ +#endif + +/* Sync functions */ +#if FF_FS_REENTRANT +int ff_cre_syncobj (BYTE vol, FF_SYNC_t* sobj); /* Create a sync object */ +int ff_req_grant (FF_SYNC_t sobj); /* Lock sync object */ +void ff_rel_grant (FF_SYNC_t sobj); /* Unlock sync object */ +int ff_del_syncobj (FF_SYNC_t sobj); /* Delete a sync object */ +#endif + + + + +/*--------------------------------------------------------------*/ +/* Flags and offset address */ + + +/* File access mode and open method flags (3rd argument of f_open) */ +#define FA_READ 0x01 +#define FA_WRITE 0x02 +#define FA_OPEN_EXISTING 0x00 +#define FA_CREATE_NEW 0x04 +#define FA_CREATE_ALWAYS 0x08 +#define FA_OPEN_ALWAYS 0x10 +#define FA_OPEN_APPEND 0x30 + +/* Fast seek controls (2nd argument of f_lseek) */ +#define CREATE_LINKMAP ((FSIZE_t)0 - 1) + +/* Format options (2nd argument of f_mkfs) */ +#define FM_FAT 0x01 +#define FM_FAT32 0x02 +#define FM_EXFAT 0x04 +#define FM_ANY 0x07 +#define FM_SFD 0x08 + +/* Filesystem type (FATFS.fs_type) */ +#define FS_FAT12 1 +#define FS_FAT16 2 +#define FS_FAT32 3 +#define FS_EXFAT 4 + +/* File attribute bits for directory entry (FILINFO.fattrib) */ +#define AM_RDO 0x01 /* Read only */ +#define AM_HID 0x02 /* Hidden */ +#define AM_SYS 0x04 /* System */ +#define AM_DIR 0x10 /* Directory */ +#define AM_ARC 0x20 /* Archive */ + + +#ifdef __cplusplus +} +#endif + +#endif /* FF_DEFINED */ diff --git a/platform/mcu/xr871/include/kernel/os/YunOS/os_common.h b/platform/mcu/xr871/include/kernel/os/YunOS/os_common.h new file mode 100644 index 0000000000..605c8f1e18 --- /dev/null +++ b/platform/mcu/xr871/include/kernel/os/YunOS/os_common.h @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef _KERNEL_OS_YUNOS_OS_COMMON_H_ +#define _KERNEL_OS_YUNOS_OS_COMMON_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + OS_PRIORITY_IDLE = RHINO_IDLE_PRI, + OS_PRIORITY_LOW = 11, + OS_PRIORITY_BELOW_NORMAL = 10, + OS_PRIORITY_NORMAL = 9, + OS_PRIORITY_ABOVE_NORMAL = 8, + OS_PRIORITY_HIGH = (RHINO_CONFIG_TIMER_TASK_PRI + 1), + OS_PRIORITY_REAL_TIME = (RHINO_CONFIG_TIMER_TASK_PRI) +} OS_Priority; + +typedef enum { + OS_OK = 0, + OS_FAIL = -1, + OS_E_NOMEM = -2, + OS_E_PARAM = -3, + OS_E_TIMEOUT = -4, + OS_E_ISR = -5, +} OS_Status; + +typedef uint32_t OS_Time_t; + +#define OS_WAIT_FOREVER 0xffffffffU +#define OS_SEMAPHORE_MAX_COUNT 0xffffffffU +#define OS_INVALID_HANDLE NULL + +#ifdef __cplusplus +} +#endif + +#endif /* _KERNEL_OS_YUNOS_OS_COMMON_H_ */ diff --git a/platform/mcu/xr871/include/kernel/os/YunOS/os_errno.h b/platform/mcu/xr871/include/kernel/os/YunOS/os_errno.h new file mode 100644 index 0000000000..f3c0339e58 --- /dev/null +++ b/platform/mcu/xr871/include/kernel/os/YunOS/os_errno.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef _KERNEL_OS_YUNOS_OS_ERRNO_H_ +#define _KERNEL_OS_YUNOS_OS_ERRNO_H_ + +#include "kernel/os/YunOS/os_common.h" + +/* nothing to define for BSP */ + +static __inline int OS_GetErrno(void) +{ + return 0; +} + +static __inline void OS_SetErrno(int err) +{ +} + +#endif /* _KERNEL_OS_YUNOS_OS_ERRNO_H_ */ diff --git a/platform/mcu/xr871/include/kernel/os/YunOS/os_mutex.h b/platform/mcu/xr871/include/kernel/os/YunOS/os_mutex.h new file mode 100644 index 0000000000..2987832d4f --- /dev/null +++ b/platform/mcu/xr871/include/kernel/os/YunOS/os_mutex.h @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef _KERNEL_OS_YUNOS_OS_MUTEX_H_ +#define _KERNEL_OS_YUNOS_OS_MUTEX_H_ + +#include "kernel/os/YunOS/os_common.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct OS_Mutex { + kmutex_t mutex; +} OS_Mutex_t; + +static __inline OS_Status OS_MutexCreate(OS_Mutex_t *mutex) +{ + if (RHINO_SUCCESS == krhino_mutex_create(&mutex->mutex, "UNDEF")) { + return OS_OK; + } else { + return OS_FAIL; + } +} + + +static __inline OS_Status OS_MutexDelete(OS_Mutex_t *mutex) +{ + if (RHINO_SUCCESS == krhino_mutex_del(&mutex->mutex)) { + return OS_OK; + } else { + return OS_FAIL; + } +} + +static __inline OS_Status OS_MutexLock(OS_Mutex_t *mutex, OS_Time_t waitMS) +{ + tick_t ticks = OS_MSecsToTicks(waitMS); + + if (RHINO_SUCCESS == krhino_mutex_lock(&mutex->mutex, ticks)) { + return OS_OK; + } else { + return OS_E_TIMEOUT; + } +} + +static __inline OS_Status OS_MutexUnlock(OS_Mutex_t *mutex) +{ + if (RHINO_SUCCESS == krhino_mutex_unlock(&mutex->mutex)) { + return OS_OK; + } else { + return OS_FAIL; + } +} + +static __inline OS_Status OS_RecursiveMutexCreate(OS_Mutex_t *mutex) +{ + return OS_MutexCreate(mutex); +} + +static __inline OS_Status OS_RecursiveMutexDelete(OS_Mutex_t *mutex) +{ + return OS_MutexDelete(mutex); +} + +static __inline OS_Status OS_RecursiveMutexLock(OS_Mutex_t *mutex, OS_Time_t waitMS) +{ + return OS_MutexLock(mutex, waitMS); +} + +static __inline OS_Status OS_RecursiveMutexUnlock(OS_Mutex_t *mutex) +{ + return OS_MutexUnlock(mutex); +} + +static __inline int OS_MutexIsValid(OS_Mutex_t *mutex) +{ + return (mutex->mutex.blk_obj.obj_type == RHINO_MUTEX_OBJ_TYPE); +} + +static __inline void OS_MutexSetInvalid(OS_Mutex_t *mutex) +{ + mutex->mutex.blk_obj.obj_type = RHINO_OBJ_TYPE_NONE; +} + +#ifdef __cplusplus +} +#endif + +#endif /* _KERNEL_OS_YUNOS_OS_MUTEX_H_ */ diff --git a/platform/mcu/xr871/include/kernel/os/YunOS/os_queue.h b/platform/mcu/xr871/include/kernel/os/YunOS/os_queue.h new file mode 100644 index 0000000000..6a541354ed --- /dev/null +++ b/platform/mcu/xr871/include/kernel/os/YunOS/os_queue.h @@ -0,0 +1,135 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef _KERNEL_OS_YUNOS_OS_QUEUE_H_ +#define _KERNEL_OS_YUNOS_OS_QUEUE_H_ + +#include "kernel/os/YunOS/os_time.h" +#include "kernel/os/YunOS/os_common.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct OS_Queue { + kbuf_queue_t queue; + uint32_t item_size; + void* msg_start; +} OS_Queue_t; + +static __inline int OS_QueueIsValid(OS_Queue_t *queue) +{ + return (queue->queue.blk_obj.obj_type == RHINO_BUF_QUEUE_OBJ_TYPE); +} + +static __inline void OS_QueueSetInvalid(OS_Queue_t *queue) +{ + queue->queue.blk_obj.obj_type = RHINO_OBJ_TYPE_NONE; +} + + +static __inline OS_Status OS_QueueCreate(OS_Queue_t *queue, uint32_t queueLen, uint32_t itemSize) +{ + void *msg_start = NULL; + + msg_start = malloc(queueLen * itemSize); + if (msg_start == NULL) { + return OS_E_NOMEM; + } + + if (RHINO_SUCCESS == krhino_buf_queue_create(&queue->queue, "UNDEF", (void**)msg_start, queueLen * itemSize, itemSize)) { + queue->msg_start = msg_start; + queue->item_size = itemSize; + return OS_OK; + } else { + free(msg_start); + msg_start = NULL; + return OS_FAIL; + } +} + +static __inline OS_Status OS_QueueDelete(OS_Queue_t *queue) +{ + if ((queue != NULL)) { + if (RHINO_SUCCESS == krhino_buf_queue_del(&queue->queue)) { + if(queue->msg_start != NULL) + free(queue->msg_start); + return OS_OK; + } else { + return OS_FAIL; + } + } + return OS_OK; +} + +static __inline OS_Status OS_QueueSend(OS_Queue_t *queue, const void *item, OS_Time_t waitMS) +{ + if (RHINO_SUCCESS == krhino_buf_queue_send(&queue->queue, item, queue->item_size)) { + return OS_OK; + } else { + return OS_FAIL; + } +} + +static __inline OS_Status OS_QueueReceive(OS_Queue_t *queue, void *item, OS_Time_t waitMS) +{ + tick_t ticks = waitMS == OS_WAIT_FOREVER ? OS_WAIT_FOREVER : OS_MSecsToTicks(waitMS); + size_t msg_len; + + if (RHINO_SUCCESS == krhino_buf_queue_recv(&queue->queue, ticks, item, &msg_len)) { + return OS_OK; + } else { + return OS_E_TIMEOUT; + } +} + +static __inline OS_Status OS_MsgQueueCreate(OS_Queue_t *queue, uint32_t queueLen) +{ + return OS_QueueCreate(queue, queueLen, sizeof(void*)); +} + +static __inline OS_Status OS_MsgQueueDelete(OS_Queue_t *queue) +{ + return OS_QueueDelete(queue); +} + +static __inline OS_Status OS_MsgQueueSend(OS_Queue_t *queue, void *msg, OS_Time_t waitMS) +{ + return OS_QueueSend(queue, &msg, waitMS); +} + +static __inline OS_Status OS_MsgQueueReceive(OS_Queue_t *queue, void **msg, OS_Time_t waitMS) +{ + return OS_QueueReceive(queue, msg, waitMS); +} + +#ifdef __cplusplus +} +#endif + +#endif /* _KERNEL_OS_YUNOS_OS_QUEUE_H_ */ diff --git a/platform/mcu/xr871/include/kernel/os/YunOS/os_semaphore.h b/platform/mcu/xr871/include/kernel/os/YunOS/os_semaphore.h new file mode 100644 index 0000000000..6904fea90e --- /dev/null +++ b/platform/mcu/xr871/include/kernel/os/YunOS/os_semaphore.h @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef _KERNEL_OS_YUNOS_OS_SEMAPHORE_H_ +#define _KERNEL_OS_YUNOS_OS_SEMAPHORE_H_ + +#include "kernel/os/YunOS/os_common.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct OS_Semaphore { + ksem_t sem; + uint32_t count; + uint32_t max_count; +} OS_Semaphore_t; + +static __inline OS_Status OS_SemaphoreCreate(OS_Semaphore_t *sem, uint32_t initCount, uint32_t maxCount) +{ + if (RHINO_SUCCESS == krhino_sem_create(&sem->sem, "UNDEF", (sem_count_t)initCount)) { + sem->count = initCount; + sem->max_count = maxCount; + return OS_OK; + } else { + return OS_FAIL; + } +} + +static __inline OS_Status OS_SemaphoreCreateBinary(OS_Semaphore_t *sem) +{ + if (RHINO_SUCCESS == krhino_sem_create(&sem->sem, "UNDEF", 0)) { + sem->count = 0; + sem->max_count = 1; + return OS_OK; + } else { + return OS_FAIL; + } +} + +static __inline OS_Status OS_SemaphoreDelete(OS_Semaphore_t *sem) +{ + if (RHINO_SUCCESS == krhino_sem_del(&sem->sem)) { + return OS_OK; + } else { + return OS_FAIL; + } +} + +static __inline OS_Status OS_SemaphoreWait(OS_Semaphore_t *sem, OS_Time_t waitMS) +{ + tick_t ticks = waitMS == OS_WAIT_FOREVER ? OS_WAIT_FOREVER : OS_MSecsToTicks(waitMS); + OS_Status ret; + kstat_t sta; + uint32_t time_tick = OS_GetTicks(); + + if (sem == NULL) { + return OS_FAIL; + } + + sta = krhino_sem_take(&sem->sem, ticks); + if(sta == RHINO_SUCCESS) { + if (sem->count) + sem->count--; + ret = OS_OK; + } else { + ret = OS_E_TIMEOUT; + } + + return ret; +} + +static __inline OS_Status OS_SemaphoreRelease(OS_Semaphore_t *sem) +{ + if (sem->max_count == 1 && sem->count == 1) { + return OS_FAIL; + } + if (RHINO_SUCCESS == krhino_sem_give(&sem->sem)) { + if (sem->count < sem->max_count) + sem->count++; + return OS_OK; + } else { + return OS_FAIL; + } +} + +static __inline int OS_SemaphoreIsValid(OS_Semaphore_t *sem) +{ + return (sem->sem.blk_obj.obj_type == RHINO_SEM_OBJ_TYPE); +} + +static __inline void OS_SemaphoreSetInvalid(OS_Semaphore_t *sem) +{ + sem->sem.blk_obj.obj_type = RHINO_OBJ_TYPE_NONE; +} + +#ifdef __cplusplus +} +#endif + +#endif /* _KERNEL_OS_YUNOS_OS_SEMAPHORE_H_ */ diff --git a/platform/mcu/xr871/include/kernel/os/YunOS/os_thread.h b/platform/mcu/xr871/include/kernel/os/YunOS/os_thread.h new file mode 100644 index 0000000000..2c13881352 --- /dev/null +++ b/platform/mcu/xr871/include/kernel/os/YunOS/os_thread.h @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef _KERNEL_OS_YUNOS_OS_THREAD_H_ +#define _KERNEL_OS_YUNOS_OS_THREAD_H_ + +#include +#include "kernel/os/YunOS/os_common.h" +#include "kernel/os/YunOS/os_time.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* thread priority */ +#define OS_THREAD_PRIO_DRV_BH OS_PRIORITY_HIGH +#define OS_THREAD_PRIO_DRV_WORK OS_PRIORITY_ABOVE_NORMAL +#define OS_THREAD_PRIO_NET80211 OS_PRIORITY_ABOVE_NORMAL +#define OS_THREAD_PRIO_CTRL OS_PRIORITY_ABOVE_NORMAL +#define OS_THREAD_PRIO_WPAS OS_PRIORITY_ABOVE_NORMAL +#define OS_THREAD_PRIO_DRV_RX OS_PRIORITY_NORMAL +#define OS_THREAD_PRIO_LWIP OS_PRIORITY_NORMAL +#define OS_THREAD_PRIO_CONSOLE OS_PRIORITY_ABOVE_NORMAL +#define OS_THREAD_PRIO_SYS_CTRL OS_PRIORITY_ABOVE_NORMAL +#define OS_THREAD_PRIO_APP OS_PRIORITY_NORMAL + +#ifdef __CONFIG_ARCH_DUAL_CORE +#define OS_THREAD_PRIO_DUCC_APP_NORMAL OS_PRIORITY_ABOVE_NORMAL +#define OS_THREAD_PRIO_DUCC_APP_DATA OS_PRIORITY_NORMAL +#define OS_THREAD_PRIO_DUCC_NET_NORMAL OS_PRIORITY_ABOVE_NORMAL +#define OS_THREAD_PRIO_DUCC_NET_DATA OS_PRIORITY_NORMAL +#endif /* __CONFIG_ARCH_DUAL_CORE */ + + +typedef task_entry_t OS_ThreadEntry_t; +typedef void * OS_ThreadHandle_t; + +typedef struct OS_Thread { + OS_ThreadHandle_t handle; +} OS_Thread_t; + +static __inline OS_Status OS_ThreadCreate(OS_Thread_t *thread, const char *name, + OS_ThreadEntry_t entry, void *arg, + OS_Priority priority, uint32_t stackSize) +{ + if (RHINO_SUCCESS == krhino_task_dyn_create((ktask_t**)&thread->handle, name, arg, + priority, 0, stackSize/sizeof(cpu_stack_t), entry, 1u)) { + return OS_OK; + } else { + thread->handle = OS_INVALID_HANDLE; + return OS_FAIL; + } +} + +static __inline OS_Status OS_ThreadDelete(OS_Thread_t *thread) +{ + if (RHINO_SUCCESS == krhino_task_dyn_del(thread->handle)) { + return OS_OK; + } else { + return OS_FAIL; + } +} + +static __inline int OS_ThreadIsValid(OS_Thread_t *thread) +{ + return (thread->handle != OS_INVALID_HANDLE); +} + +static __inline void OS_ThreadSetInvalid(OS_Thread_t *thread) +{ + thread->handle = OS_INVALID_HANDLE; +} + +static __inline void OS_ThreadSuspendScheduler(void) +{ + krhino_sched_disable(); +} + +static __inline void OS_ThreadResumeScheduler(void) +{ + krhino_sched_enable(); +} + +static __inline void OS_ThreadSleep(OS_Time_t msec) +{ + krhino_task_sleep((tick_t)OS_MSecsToTicks(msec)); +} + +static __inline void OS_ThreadYield() +{ + krhino_task_yield(); +} + +static __inline OS_ThreadHandle_t OS_ThreadGetCurrentHandle(void) +{ + return (OS_ThreadHandle_t)krhino_cur_task_get(); +} + +#ifdef __cplusplus +} +#endif + +#endif /* _KERNEL_OS_YUNOS_OS_THREAD_H_ */ diff --git a/platform/mcu/xr871/include/kernel/os/YunOS/os_time.h b/platform/mcu/xr871/include/kernel/os/YunOS/os_time.h new file mode 100644 index 0000000000..6ba0ac5cfd --- /dev/null +++ b/platform/mcu/xr871/include/kernel/os/YunOS/os_time.h @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef _KERNEL_OS_YUNOS_OS_TIME_H_ +#define _KERNEL_OS_YUNOS_OS_TIME_H_ + +#include "kernel/os/YunOS/os_common.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Parameters of time */ +#define OS_MSEC_PER_SEC 1000UL +#define OS_USEC_PER_MSEC 1000UL +#define OS_USEC_PER_SEC 1000000UL + +/* system clock's frequency, ticks per second */ +#define OS_HZ RHINO_CONFIG_TICKS_PER_SECOND + +/* usec per tick (1000000 / OS_HZ) */ +#define OS_TICK (OS_USEC_PER_SEC / OS_HZ) + +/* The number of ticks since boot. */ +#define OS_GetTicks() ((uint32_t)(krhino_sys_tick_get() / OS_MSEC_PER_SEC)) + /* Due to portTICK_TYPE_IS_ATOMIC is 1, call xTaskGetTickCount() in ISR is safe also */ + +/* The number of seconds since boot. */ +#define OS_GetTime() (krhino_sys_time_get() / OS_HZ) + +/* Time conversion */ +#define OS_SecsToTicks(sec) ((uint32_t)(sec) * OS_HZ) +#define OS_MSecsToTicks(msec) ((uint32_t)(krhino_ms_to_ticks(msec))) +#define OS_TicksToMSecs(t) ((uint32_t)(krhino_ticks_to_ms(t))) +#define OS_TicksToSecs(t) ((uint32_t)((krhino_ticks_to_ms(t)) / OS_MSEC_PER_SEC)) + +#define OS_GetJiffies() OS_GetTicks() +#define OS_SecsToJiffies(sec) OS_SecsToTicks(sec) +#define OS_MSecsToJiffies(msec) OS_MSecsToTicks(msec) +#define OS_JiffiesToMSecs(j) OS_TicksToMSecs(j) +#define OS_JiffiesToSecs(j) OS_TicksToSecs(j) + +/* sleep */ +#define OS_MSleep(msec) krhino_task_sleep(krhino_ms_to_ticks(msec)) +#define OS_Sleep(sec) OS_MSleep((sec) * OS_MSEC_PER_SEC) +#define OS_SSleep(sec) OS_Sleep(sec) + +#define OS_TimeAfter(a, b) ((int32_t)(b) - (int32_t)(a) < 0) +#define OS_TimeBefore(a, b) OS_TimeAfter(b, a) +#define OS_TimeAfterEqual(a, b) ((int32_t)(a) - (int32_t)(b) >= 0) +#define OS_TimeBeforeEqual(a, b) OS_TimeAfterEqual(b, a) + +/* rand, read value from Cortex-M SYST_CVR register */ +#define OS_Rand32() \ + ((uint32_t)(((*((volatile uint32_t *)0xE000E018)) & 0xffffff) | \ + (OS_GetTicks() << 24))) + +#ifdef __cplusplus +} +#endif + +#endif /* _KERNEL_OS_YUNOS_OS_TIME_H_ */ diff --git a/platform/mcu/xr871/include/kernel/os/YunOS/os_timer.h b/platform/mcu/xr871/include/kernel/os/YunOS/os_timer.h new file mode 100644 index 0000000000..b1efe681b6 --- /dev/null +++ b/platform/mcu/xr871/include/kernel/os/YunOS/os_timer.h @@ -0,0 +1,169 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef _KERNEL_OS_YUNOS_OS_TIMER_H_ +#define _KERNEL_OS_YUNOS_OS_TIMER_H_ + +#include "kernel/os/YunOS/os_common.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + OS_TIMER_ONCE = 0, + OS_TIMER_PERIODIC = 1 +} OS_TimerType; + +typedef void (*OS_TimerCallback_t)(void *arg); + +typedef struct OS_Timer { + void* handle; /* must be first */ + OS_TimerCallback_t cb; + OS_TimerType type; +} OS_Timer_t; + +static __inline void krhino_timer_cb(void *timer, void *arg) +{ + OS_Timer_t *tmr = timer; + tmr->cb(arg); +} + +kstat_t krhino_timer_dyn_create(ktimer_t **timer, const name_t *name, timer_cb_t cb, + tick_t first, tick_t round, void *arg, uint8_t auto_run); + +static __inline OS_Status OS_TimerCreate(OS_Timer_t *timer, OS_TimerType type, + OS_TimerCallback_t cb, void *ctx, OS_Time_t periodMS) +{ + tick_t first = krhino_ms_to_ticks((uint32_t)periodMS); + tick_t round = type == OS_TIMER_ONCE ? 0 : first; +#if (RHINO_CONFIG_KOBJ_DYN_ALLOC > 0) + ktimer_t *timer_obj = NULL; + if (RHINO_SUCCESS == krhino_timer_dyn_create(&timer_obj, "UNDEF", krhino_timer_cb, + first, round, ctx, 1)) { + timer->handle = (void*)timer_obj; + timer->cb = cb; + timer->type = type; + return OS_OK; + } else { + timer->handle = NULL; + timer->cb = NULL; + return OS_FAIL; + } +#else + ktimer_t *timer_obj = = yoc_mm_alloc(sizeof(ktimer_t)); + if (timer_obj == NULL) { + return OS_FAIL; + } + + if (RHINO_SUCCESS == krhino_timer_create(timer_obj, "UNDEF", krhino_timer_cb, + first, round, ctx, 1)) { + timer->handle = (void*)timer_obj; + timer->cb = cb; + return OS_OK; + } else { + yoc_mm_free(timer_obj); + timer->handle = NULL; + timer->cb = NULL; + return OS_FAIL; + } + +#endif +} + +static __inline OS_Status OS_TimerDelete(OS_Timer_t *timer) +{ +#if (RHINO_CONFIG_KOBJ_DYN_ALLOC > 0) + if (RHINO_SUCCESS == krhino_timer_dyn_del(timer->handle)) { + timer->handle = NULL; + timer->cb = NULL; + return OS_OK; + } else { + return OS_FAIL; + } +#else + if (RHINO_SUCCESS == krhino_timer_del(timer->handle)) { + yoc_mm_free(timer->handle); + timer->handle = NULL; + timer->cb = cb; + return OS_OK; + } else { + return OS_FAIL; + } +#endif +} + +static __inline OS_Status OS_TimerStart(OS_Timer_t *timer) +{ + if (RHINO_SUCCESS == krhino_timer_start(timer->handle)) { + return OS_OK; + } else { + return OS_FAIL; + } +} + +static __inline OS_Status OS_TimerChangePeriod(OS_Timer_t *timer, OS_Time_t periodMS) +{ + tick_t first = krhino_ms_to_ticks((uint32_t)periodMS); + tick_t round = timer->type == OS_TIMER_ONCE ? 0 : first; + if (RHINO_SUCCESS == krhino_timer_change(timer->handle, first, round)) { + return OS_OK; + } else { + return OS_FAIL; + } +} + +static __inline OS_Status OS_TimerStop(OS_Timer_t *timer) +{ + if (RHINO_SUCCESS == krhino_timer_stop(timer->handle)) { + return OS_OK; + } else { + return OS_FAIL; + } +} + +static __inline int OS_TimerIsValid(OS_Timer_t *timer) +{ + return (timer->handle != OS_INVALID_HANDLE); +} + +static __inline void OS_TimerSetInvalid(OS_Timer_t *timer) +{ + timer->handle = OS_INVALID_HANDLE; +} + +static __inline void *OS_TimerGetContext(void *arg) +{ + return arg; +} + +#ifdef __cplusplus +} +#endif + +#endif /* _KERNEL_OS_YUNOS_OS_TIMER_H_ */ diff --git a/platform/mcu/xr871/include/kernel/os/os.h b/platform/mcu/xr871/include/kernel/os/os.h new file mode 100644 index 0000000000..fc2d719018 --- /dev/null +++ b/platform/mcu/xr871/include/kernel/os/os.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _KERNEL_OS_OS_H_ +#define _KERNEL_OS_OS_H_ + +#include "kernel/os/os_time.h" +#include "kernel/os/os_thread.h" +#include "kernel/os/os_queue.h" +#include "kernel/os/os_semaphore.h" +#include "kernel/os/os_mutex.h" +#include "kernel/os/os_timer.h" +#include "kernel/os/os_time.h" +#include "kernel/os/os_errno.h" + + +#endif /* _KERNEL_OS_OS_H_ */ diff --git a/platform/mcu/xr871/include/kernel/os/os_common.h b/platform/mcu/xr871/include/kernel/os/os_common.h new file mode 100644 index 0000000000..df6465cf25 --- /dev/null +++ b/platform/mcu/xr871/include/kernel/os/os_common.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _KERNEL_OS_OS_COMMON_H_ +#define _KERNEL_OS_OS_COMMON_H_ + +#if defined (__CONFIG_OS_USE_YUNOS) +#include "kernel/os/YunOS/os_common.h" +#else +#error "No OS defined!" +#endif + +#endif diff --git a/platform/mcu/xr871/include/kernel/os/os_errno.h b/platform/mcu/xr871/include/kernel/os/os_errno.h new file mode 100644 index 0000000000..e4bf47dc62 --- /dev/null +++ b/platform/mcu/xr871/include/kernel/os/os_errno.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _KERNEL_OS_OS_ERRNO_H_ +#define _KERNEL_OS_OS_ERRNO_H_ + +#if defined (__CONFIG_OS_USE_YUNOS) +#include "kernel/os/YunOS/os_errno.h" +#else +#error "No OS defined!" +#endif + +#endif /* _KERNEL_OS_OS_ERRNO_H_ */ diff --git a/platform/mcu/xr871/include/kernel/os/os_mutex.h b/platform/mcu/xr871/include/kernel/os/os_mutex.h new file mode 100644 index 0000000000..391e65a917 --- /dev/null +++ b/platform/mcu/xr871/include/kernel/os/os_mutex.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _KERNEL_OS_OS_MUTEX_H_ +#define _KERNEL_OS_OS_MUTEX_H_ + +#if defined (__CONFIG_OS_USE_YUNOS) +#include "kernel/os/YunOS/os_time.h" +#include "kernel/os/YunOS/os_mutex.h" +#else +#error "No OS defined!" +#endif + +#endif /* _KERNEL_OS_OS_MUTEX_H_ */ diff --git a/platform/mcu/xr871/include/kernel/os/os_queue.h b/platform/mcu/xr871/include/kernel/os/os_queue.h new file mode 100644 index 0000000000..860d6d7c40 --- /dev/null +++ b/platform/mcu/xr871/include/kernel/os/os_queue.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _KERNEL_OS_OS_QUEUE_H_ +#define _KERNEL_OS_OS_QUEUE_H_ + +#if defined (__CONFIG_OS_USE_YUNOS) +#include "kernel/os/YunOS/os_queue.h" +#else +#error "No OS defined!" +#endif + +#endif /* _KERNEL_OS_OS_QUEUE_H_ */ diff --git a/platform/mcu/xr871/include/kernel/os/os_semaphore.h b/platform/mcu/xr871/include/kernel/os/os_semaphore.h new file mode 100644 index 0000000000..e9d7f5b463 --- /dev/null +++ b/platform/mcu/xr871/include/kernel/os/os_semaphore.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _KERNEL_OS_OS_SEMAPHORE_H_ +#define _KERNEL_OS_OS_SEMAPHORE_H_ + +#if defined (__CONFIG_OS_USE_YUNOS) +#include "kernel/os/YunOS/os_semaphore.h" +#else +#error "No OS defined!" +#endif + +#endif /* _KERNEL_OS_OS_SEMAPHORE_H_ */ diff --git a/platform/mcu/xr871/include/kernel/os/os_thread.h b/platform/mcu/xr871/include/kernel/os/os_thread.h new file mode 100644 index 0000000000..e22dc75e5b --- /dev/null +++ b/platform/mcu/xr871/include/kernel/os/os_thread.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _KERNEL_OS_OS_THREAD_H_ +#define _KERNEL_OS_OS_THREAD_H_ + +#if defined (__CONFIG_OS_USE_YUNOS) +#include "kernel/os/YunOS/os_thread.h" +#else +#error "No OS defined!" +#endif + +#endif /* _KERNEL_OS_OS_THREAD_H_ */ diff --git a/platform/mcu/xr871/include/kernel/os/os_time.h b/platform/mcu/xr871/include/kernel/os/os_time.h new file mode 100644 index 0000000000..865ff568ef --- /dev/null +++ b/platform/mcu/xr871/include/kernel/os/os_time.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _KERNEL_OS_OS_TIME_H_ +#define _KERNEL_OS_OS_TIME_H_ + +#if defined (__CONFIG_OS_USE_YUNOS) +#include "kernel/os/YunOS/os_time.h" +#else +#error "No OS defined!" +#endif + +#endif /* _KERNEL_OS_OS_TIME_H_ */ diff --git a/platform/mcu/xr871/include/kernel/os/os_timer.h b/platform/mcu/xr871/include/kernel/os/os_timer.h new file mode 100644 index 0000000000..8998b623c0 --- /dev/null +++ b/platform/mcu/xr871/include/kernel/os/os_timer.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _KERNEL_OS_OS_TIMER_H_ +#define _KERNEL_OS_OS_TIMER_H_ + +#if defined (__CONFIG_OS_USE_YUNOS) +#include "kernel/os/YunOS/os_timer.h" +#else +#error "No OS defined!" +#endif + +#endif /* _KERNEL_OS_OS_TIMER_H_ */ diff --git a/platform/mcu/xr871/include/libc/errno.h b/platform/mcu/xr871/include/libc/errno.h new file mode 100644 index 0000000000..b848aacffa --- /dev/null +++ b/platform/mcu/xr871/include/libc/errno.h @@ -0,0 +1,180 @@ +#ifndef _LIBC_ERRNO_H_ +#define _LIBC_ERRNO_H_ + +#include "kernel/os/os_errno.h" + +#define errno OS_GetErrno() + + +#define EPERM 1 /* Operation not permitted */ +#define ENOENT 2 /* No such file or directory */ +#define ESRCH 3 /* No such process */ +#define EINTR 4 /* Interrupted system call */ +#define EIO 5 /* I/O error */ +#define ENXIO 6 /* No such device or address */ +#define E2BIG 7 /* Argument list too long */ +#define ENOEXEC 8 /* Exec format error */ +#define EBADF 9 /* Bad file number */ +#define ECHILD 10 /* No child processes */ +#define EAGAIN 11 /* Try again */ +#define ENOMEM 12 /* Out of memory */ +#define EACCES 13 /* Permission denied */ +#define EFAULT 14 /* Bad address */ +#define ENOTBLK 15 /* Block device required */ +#define EBUSY 16 /* Device or resource busy */ +#define EEXIST 17 /* File exists */ +#define EXDEV 18 /* Cross-device link */ +#define ENODEV 19 /* No such device */ +#define ENOTDIR 20 /* Not a directory */ +#define EISDIR 21 /* Is a directory */ +#define EINVAL 22 /* Invalid argument */ +#define ENFILE 23 /* File table overflow */ +#define EMFILE 24 /* Too many open files */ +#define ENOTTY 25 /* Not a typewriter */ +#define ETXTBSY 26 /* Text file busy */ +#define EFBIG 27 /* File too large */ +#define ENOSPC 28 /* No space left on device */ +#define ESPIPE 29 /* Illegal seek */ +#define EROFS 30 /* Read-only file system */ +#define EMLINK 31 /* Too many links */ +#define EPIPE 32 /* Broken pipe */ +#define EDOM 33 /* Math argument out of domain of func */ +#define ERANGE 34 /* Math result not representable */ + +#define EDEADLK 35 /* Resource deadlock would occur */ +#define ENAMETOOLONG 36 /* File name too long */ +#define ENOLCK 37 /* No record locks available */ +#define ENOSYS 38 /* Function not implemented */ +#define ENOTEMPTY 39 /* Directory not empty */ +#define ELOOP 40 /* Too many symbolic links encountered */ +#define EWOULDBLOCK EAGAIN /* Operation would block */ +#define ENOMSG 42 /* No message of desired type */ +#define EIDRM 43 /* Identifier removed */ +#define ECHRNG 44 /* Channel number out of range */ +#define EL2NSYNC 45 /* Level 2 not synchronized */ +#define EL3HLT 46 /* Level 3 halted */ +#define EL3RST 47 /* Level 3 reset */ +#define ELNRNG 48 /* Link number out of range */ +#define EUNATCH 49 /* Protocol driver not attached */ +#define ENOCSI 50 /* No CSI structure available */ +#define EL2HLT 51 /* Level 2 halted */ +#define EBADE 52 /* Invalid exchange */ +#define EBADR 53 /* Invalid request descriptor */ +#define EXFULL 54 /* Exchange full */ +#define ENOANO 55 /* No anode */ +#define EBADRQC 56 /* Invalid request code */ +#define EBADSLT 57 /* Invalid slot */ + +#define EDEADLOCK EDEADLK + +#define EBFONT 59 /* Bad font file format */ +#define ENOSTR 60 /* Device not a stream */ +#define ENODATA 61 /* No data available */ +#define ETIME 62 /* Timer expired */ +#define ENOSR 63 /* Out of streams resources */ +#define ENONET 64 /* Machine is not on the network */ +#define ENOPKG 65 /* Package not installed */ +#define EREMOTE 66 /* Object is remote */ +#define ENOLINK 67 /* Link has been severed */ +#define EADV 68 /* Advertise error */ +#define ESRMNT 69 /* Srmount error */ +#define ECOMM 70 /* Communication error on send */ +#define EPROTO 71 /* Protocol error */ +#define EMULTIHOP 72 /* Multihop attempted */ +#define EDOTDOT 73 /* RFS specific error */ +#define EBADMSG 74 /* Not a data message */ +#define EOVERFLOW 75 /* Value too large for defined data type */ +#define ENOTUNIQ 76 /* Name not unique on network */ +#define EBADFD 77 /* File descriptor in bad state */ +#define EREMCHG 78 /* Remote address changed */ +#define ELIBACC 79 /* Can not access a needed shared library */ +#define ELIBBAD 80 /* Accessing a corrupted shared library */ +#define ELIBSCN 81 /* .lib section in a.out corrupted */ +#define ELIBMAX 82 /* Attempting to link in too many shared libraries */ +#define ELIBEXEC 83 /* Cannot exec a shared library directly */ +#define EILSEQ 84 /* Illegal byte sequence */ +#define ERESTART 85 /* Interrupted system call should be restarted */ +#define ESTRPIPE 86 /* Streams pipe error */ +#define EUSERS 87 /* Too many users */ +#define ENOTSOCK 88 /* Socket operation on non-socket */ +#define EDESTADDRREQ 89 /* Destination address required */ +#define EMSGSIZE 90 /* Message too long */ +#define EPROTOTYPE 91 /* Protocol wrong type for socket */ +#define ENOPROTOOPT 92 /* Protocol not available */ +#define EPROTONOSUPPORT 93 /* Protocol not supported */ +#define ESOCKTNOSUPPORT 94 /* Socket type not supported */ +#define EOPNOTSUPP 95 /* Operation not supported on transport endpoint */ +#define EPFNOSUPPORT 96 /* Protocol family not supported */ +#define EAFNOSUPPORT 97 /* Address family not supported by protocol */ +#define EADDRINUSE 98 /* Address already in use */ +#define EADDRNOTAVAIL 99 /* Cannot assign requested address */ +#define ENETDOWN 100 /* Network is down */ +#define ENETUNREACH 101 /* Network is unreachable */ +#define ENETRESET 102 /* Network dropped connection because of reset */ +#define ECONNABORTED 103 /* Software caused connection abort */ +#define ECONNRESET 104 /* Connection reset by peer */ +#define ENOBUFS 105 /* No buffer space available */ +#define EISCONN 106 /* Transport endpoint is already connected */ +#define ENOTCONN 107 /* Transport endpoint is not connected */ +#define ESHUTDOWN 108 /* Cannot send after transport endpoint shutdown */ +#define ETOOMANYREFS 109 /* Too many references: cannot splice */ +#define ETIMEDOUT 110 /* Connection timed out */ +#define ECONNREFUSED 111 /* Connection refused */ +#define EHOSTDOWN 112 /* Host is down */ +#define EHOSTUNREACH 113 /* No route to host */ +#define EALREADY 114 /* Operation already in progress */ +#define EINPROGRESS 115 /* Operation now in progress */ +#define ESTALE 116 /* Stale file handle */ +#define EUCLEAN 117 /* Structure needs cleaning */ +#define ENOTNAM 118 /* Not a XENIX named type file */ +#define ENAVAIL 119 /* No XENIX semaphores available */ +#define EISNAM 120 /* Is a named type file */ +#define EREMOTEIO 121 /* Remote I/O error */ +#define EDQUOT 122 /* Quota exceeded */ + +#define ENOMEDIUM 123 /* No medium found */ +#define EMEDIUMTYPE 124 /* Wrong medium type */ +#define ECANCELED 125 /* Operation Canceled */ +#define ENOKEY 126 /* Required key not available */ +#define EKEYEXPIRED 127 /* Key has expired */ +#define EKEYREVOKED 128 /* Key has been revoked */ +#define EKEYREJECTED 129 /* Key was rejected by service */ + +/* for robust mutexes */ +#define EOWNERDEAD 130 /* Owner died */ +#define ENOTRECOVERABLE 131 /* State not recoverable */ + +#define ERFKILL 132 /* Operation not possible due to RF-kill */ + +#define EHWPOISON 133 /* Memory page has hardware error */ + + +/* + * These should never be seen by user programs. To return one of ERESTART* + * codes, signal_pending() MUST be set. Note that ptrace can observe these + * at syscall exit tracing, but they will never be left for the debugged user + * process to see. + */ +#define ERESTARTSYS 512 +#define ERESTARTNOINTR 513 +#define ERESTARTNOHAND 514 /* restart if no handler.. */ +#define ENOIOCTLCMD 515 /* No ioctl command */ +#define ERESTART_RESTARTBLOCK 516 /* restart by calling sys_restart_syscall */ +#define EPROBE_DEFER 517 /* Driver requests probe retry */ +#define EOPENSTALE 518 /* open found a stale dentry */ + +/* Defined for the NFSv3 protocol */ +#define EBADHANDLE 521 /* Illegal NFS file handle */ +#define ENOTSYNC 522 /* Update synchronization mismatch */ +#define EBADCOOKIE 523 /* Cookie is stale */ +#define ENOTSUPP 524 /* Operation is not supported */ +#define ETOOSMALL 525 /* Buffer or request is too small */ +#define ESERVERFAULT 526 /* An untranslatable error occurred */ +#define EBADTYPE 527 /* Type not supported by server */ +#define EJUKEBOX 528 /* Request initiated, but will not complete before timeout */ +#define EIOCBQUEUED 529 /* iocb queued, will get completion event */ + +/* alias */ +#define ENOTSUP EOPNOTSUPP /* Operation not supported */ + +#endif /* _LIBC_ERRNO_H_ */ diff --git a/platform/mcu/xr871/include/libc/sys/features.h b/platform/mcu/xr871/include/libc/sys/features.h new file mode 100644 index 0000000000..5b314bc263 --- /dev/null +++ b/platform/mcu/xr871/include/libc/sys/features.h @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * In order to redefine int32_t to int when compiles using GCC, this file is + * added to override of GCC. + */ + +#ifndef _LIBC_SYS_FEATURES_H_ +#define _LIBC_SYS_FEATURES_H_ + +#ifdef __CONFIG_LIBC_REDEFINE_GCC_INT32_TYPE + +/* + * Redefine some macros defined by GCC + */ +#ifdef __INT32_MAX__ +#undef __INT32_MAX__ +#endif +#define __INT32_MAX__ 2147483647 + +#ifdef __UINT32_MAX__ +#undef __UINT32_MAX__ +#endif +#define __UINT32_MAX__ 4294967295U + +#ifdef __INT_LEAST32_MAX__ +#undef __INT_LEAST32_MAX__ +#endif +#define __INT_LEAST32_MAX__ 2147483647 + +#ifdef __UINT_LEAST32_MAX__ +#undef __UINT_LEAST32_MAX__ +#endif +#define __UINT_LEAST32_MAX__ 4294967295U + +#ifdef __INT_LEAST32_TYPE__ +#undef __INT_LEAST32_TYPE__ +#endif +#define __INT_LEAST32_TYPE__ int + +#ifdef __UINT_LEAST32_TYPE__ +#undef __UINT_LEAST32_TYPE__ +#endif +#define __UINT_LEAST32_TYPE__ unsigned int + +#ifdef __INT32_C +#undef __INT32_C +#endif +#define __INT32_C(c) c + +#ifdef __UINT32_C +#undef __UINT32_C +#endif +#define __UINT32_C(c) c ## U + +#ifdef __INT32_TYPE__ +#undef __INT32_TYPE__ +#endif +#define __INT32_TYPE__ int + +#ifdef __UINT32_TYPE__ +#undef __UINT32_TYPE__ +#endif +#define __UINT32_TYPE__ unsigned int + +#endif /* __CONFIG_LIBC_REDEFINE_GCC_INT32_TYPE */ + +/* + * Include of compiler + */ +#include_next + +#endif /* _LIBC_SYS_FEATURES_H_ */ diff --git a/platform/mcu/xr871/include/net/lwip/arch/cc.h b/platform/mcu/xr871/include/net/lwip/arch/cc.h new file mode 100644 index 0000000000..258c7c1842 --- /dev/null +++ b/platform/mcu/xr871/include/net/lwip/arch/cc.h @@ -0,0 +1,153 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef __ARCH_CC_H__ +#define __ARCH_CC_H__ + + +#include +#include +#include + +#include "types.h" +#include "sys/defs.h" + +/* Types based on stdint.h */ +typedef uint8_t u8_t; +typedef int8_t s8_t; +typedef uint16_t u16_t; +typedef int16_t s16_t; +typedef uint32_t u32_t; +typedef int32_t s32_t; +typedef uintptr_t mem_ptr_t; + +//#define LWIP_COMPAT_MUTEX 1 +#define LWIP_MAILBOX_QUEUE 1 + +#define LWIP_TIMEVAL_PRIVATE 0 + +#define LWIP_NO_INTTYPES_H 1 + +/* Define (sn)printf formatters for these lwIP types */ +#if LWIP_NO_INTTYPES_H +#define X8_F "02x" +#define U16_F "hu" +#define S16_F "hd" +#define X16_F "hx" +#define U32_F "u" +#define S32_F "d" +#define X32_F "x" +#define SZT_F "uz" +#endif + +/* Define platform endianness */ +#ifndef BYTE_ORDER +#define BYTE_ORDER LITTLE_ENDIAN +#endif /* BYTE_ORDER */ + + +#if (defined(__GNUC__)) + /* GNU Compiler */ + #define PACK_STRUCT_BEGIN + #define PACK_STRUCT_STRUCT __attribute__((__packed__)) + #define PACK_STRUCT_END + #define PACK_STRUCT_FIELD(fld) fld + #define ALIGNED(n) __attribute__((aligned (n))) +#elif defined(__CC_ARM) + /* ARM Compiler */ + #define PACK_STRUCT_BEGIN __packed + #define PACK_STRUCT_STRUCT + #define PACK_STRUCT_END + #define PACK_STRUCT_FIELD(fld) fld + #define ALIGNED(n) __align(n) +#else + #error "Compiler not supported." +#endif + +/* Provide Thumb-2 routines for GCC to improve performance */ +#if defined(__GNUC__) && defined(__thumb2__) + #define MEMCPY(dst,src,len) thumb2_memcpy(dst,src,len) + #define SMEMCPY(dst,src,len) thumb2_memcpy(dst,src,len) + #define LWIP_CHKSUM thumb2_checksum + /* Set algorithm to 0 so that unused lwip_standard_chksum function + doesn't generate compiler warning */ + #define LWIP_CHKSUM_ALGORITHM 0 + + void thumb2_memcpy(void* pDest, const void* pSource, size_t length); + u16_t thumb2_checksum(void* pData, int length); +#else + #include + #define MEMCPY(dst,src,len) memcpy(dst,src,len) + #define SMEMCPY(dst,src,len) memcpy(dst,src,len) + /* Used with IP headers only */ + #define LWIP_CHKSUM_ALGORITHM 1 +#endif + + +#include + +#ifdef NDEBUG +#define LWIP_NOASSERT +#else // Assertions enabled + +// If assertions are on, the default LWIP_ERROR handler behaviour is to +// abort w/ an assertion failure. Don't do this, instead just print the error (if LWIP_DEBUG is set) +// and run the handler (same as the LWIP_ERROR behaviour if LWIP_NOASSERT is set). +#ifdef LWIP_DEBUG +#define LWIP_ERROR(message, expression, handler) do { if (!(expression)) { \ + puts(message); handler;}} while(0) +#else +// If LWIP_DEBUG is not set, return the error silently (default LWIP behaviour, also.) +#define LWIP_ERROR(message, expression, handler) do { if (!(expression)) { \ + handler;}} while(0) +#endif // LWIP_DEBUG + +#endif /* NDEBUG */ + +/* Plaform specific diagnostic output */ +#define LWIP_PLATFORM_DIAG(x) do {printf x;} while(0) +#define LWIP_PLATFORM_ASSERT(x) \ + do { \ + printf("%s at line %d in %s\n", x, __LINE__, __FILE__); \ + } while(0) + +#include "kernel/os/os_time.h" +#define LWIP_RAND() OS_Rand32() + +#include "sys/endian.h" +#define LWIP_PLATFORM_BYTESWAP 1 +#define LWIP_PLATFORM_HTONS(x) htobe16(x) //__REV16(x) +#define LWIP_PLATFORM_HTONL(x) htobe32(x) //__REV(x) + +#include "compiler.h" +#define LWIP_INLINE __inline + +#endif /* __ARCH_CC_H__ */ diff --git a/platform/mcu/xr871/include/net/lwip/lwipopts.h b/platform/mcu/xr871/include/net/lwip/lwipopts.h new file mode 100644 index 0000000000..294f234fb3 --- /dev/null +++ b/platform/mcu/xr871/include/net/lwip/lwipopts.h @@ -0,0 +1,2341 @@ +/** + * @file + * + * lwIP Options Configuration + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#if !defined LWIP_HDR_LWIPOPTS_H +#define LWIP_HDR_LWIPOPTS_H + +/* + * Include user defined options first. Anything not defined in these files + * will be set to standard values. Override anything you don't like! + */ + +/** + * @defgroup lwip_opts Options (lwipopts.h) + * @ingroup lwip + * + * @defgroup lwip_opts_debug Debugging + * @ingroup lwip_opts + * + * @defgroup lwip_opts_infrastructure Infrastructure + * @ingroup lwip_opts + * + * @defgroup lwip_opts_callback Callback-style APIs + * @ingroup lwip_opts + * + * @defgroup lwip_opts_threadsafe_apis Thread-safe APIs + * @ingroup lwip_opts + */ + +/* + -------------------------------------------------- + ---------- New options added for XRadio ---------- + -------------------------------------------------- +*/ + +#define LWIP_XR_IMPL 1 // XRadio's implementation +#define LWIP_XR_MEM 0 // XRadio's implementation of memory +#define LWIP_XR_DEINIT 0 // LwIP deinit +#define LWIP_SUPPRESS_WARNING 1 +#define LWIP_RESOURCE_TRACE 0 // trace resource usage for debugging +#define LWIP_MBOX_TRACE 0 // trace mbox usage for debugging + +/** + * LWIP_MBUF_SUPPORT==1: Reserve some head/tail space in pbuf for adding data, + * which used by mbuf. + */ +#define LWIP_MBUF_SUPPORT __CONFIG_MBUF_IMPL_MODE +#if LWIP_MBUF_SUPPORT +#define LWIP_PBUF_POOL_SMALL 1 // add small PBUF_POOL_SMALL to save memory +#endif /* LWIP_MBUF_SUPPORT */ + +/* + ------------------------------------ + ---------- Packet options ---------- + ------------------------------------ +*/ +/** + * LWIP_PACKET==1: Enable application layer to access datalink directly. + */ +#define LWIP_PACKET 0 + +/** + * DEFAULT_PKT_RECVMBOX_SIZE: The mailbox size for the incoming packets on a + * NETCONN_PKT. The queue size value itself is platform-dependent, but is passed + * to sys_mbox_new() when the recvmbox is created. + */ +#define DEFAULT_PKT_RECVMBOX_SIZE 4 + +/** + * MEMP_NUM_PKT_PCB: Number of packet connection PCBs + * (requires the LWIP_PACKET option) + */ +#define MEMP_NUM_PKT_PCB 2 + +/** + * PKT_DEBUG: Enable debugging in packet.c. + */ +#define PKT_DEBUG LWIP_DBG_OFF + +/* + ------------------------------------ + -------------- NO SYS -------------- + ------------------------------------ +*/ +/** + * @defgroup lwip_opts_nosys NO_SYS + * @ingroup lwip_opts_infrastructure + * @{ + */ +/** + * NO_SYS==1: Use lwIP without OS-awareness (no thread, semaphores, mutexes or + * mboxes). This means threaded APIs cannot be used (socket, netconn, + * i.e. everything in the 'api' folder), only the callback-style raw API is + * available (and you have to watch out for yourself that you don't access + * lwIP functions/structures from more than one context at a time!) + */ +#define NO_SYS 0 +/** + * @} + */ + +/** + * @defgroup lwip_opts_timers Timers + * @ingroup lwip_opts_infrastructure + * @{ + */ +/** + * LWIP_TIMERS==0: Drop support for sys_timeout and lwip-internal cyclic timers. + * (the array of lwip-internal cyclic timers is still provided) + * (check NO_SYS_NO_TIMERS for compatibility to old versions) + */ +#define LWIP_TIMERS 1 + +/** + * LWIP_TIMERS_CUSTOM==1: Provide your own timer implementation. + * Function prototypes in timeouts.h and the array of lwip-internal cyclic timers + * are still included, but the implementation is not. The following functions + * will be required: sys_timeouts_init(), sys_timeout(), sys_untimeout(), + * sys_timeouts_mbox_fetch() + */ +#define LWIP_TIMERS_CUSTOM 0 +/** + * @} + */ + +/** + * @defgroup lwip_opts_memcpy memcpy + * @ingroup lwip_opts_infrastructure + * @{ + */ +/** + * MEMCPY: override this if you have a faster implementation at hand than the + * one included in your C library + */ +// define in +//#define MEMCPY(dst,src,len) memcpy(dst,src,len) + +/** + * SMEMCPY: override this with care! Some compilers (e.g. gcc) can inline a + * call to memcpy() if the length is known at compile time and is small. + */ +// define in +//#define SMEMCPY(dst,src,len) memcpy(dst,src,len) +/** + * @} + */ + +/* + ------------------------------------ + ----------- Core locking ----------- + ------------------------------------ +*/ +/** + * @defgroup lwip_opts_lock Core locking and MPU + * @ingroup lwip_opts_infrastructure + * @{ + */ +/** + * LWIP_MPU_COMPATIBLE: enables special memory management mechanism + * which makes lwip able to work on MPU (Memory Protection Unit) system + * by not passing stack-pointers to other threads + * (this decreases performance as memory is allocated from pools instead + * of keeping it on the stack) + */ +#define LWIP_MPU_COMPATIBLE 0 + +/** + * LWIP_TCPIP_CORE_LOCKING + * Creates a global mutex that is held during TCPIP thread operations. + * Can be locked by client code to perform lwIP operations without changing + * into TCPIP thread using callbacks. See LOCK_TCPIP_CORE() and + * UNLOCK_TCPIP_CORE(). + * Your system should provide mutexes supporting priority inversion to use this. + */ +#define LWIP_TCPIP_CORE_LOCKING 0 // ??? + +/** + * LWIP_TCPIP_CORE_LOCKING_INPUT: when LWIP_TCPIP_CORE_LOCKING is enabled, + * this lets tcpip_input() grab the mutex for input packets as well, + * instead of allocating a message and passing it to tcpip_thread. + * + * ATTENTION: this does not work when tcpip_input() is called from + * interrupt context! + */ +#define LWIP_TCPIP_CORE_LOCKING_INPUT 0 + +/** + * SYS_LIGHTWEIGHT_PROT==1: enable inter-task protection (and task-vs-interrupt + * protection) for certain critical regions during buffer allocation, deallocation + * and memory allocation and deallocation. + * ATTENTION: This is required when using lwIP from more than one context! If + * you disable this, you must be sure what you are doing! + */ +#define SYS_LIGHTWEIGHT_PROT 1 +/** + * @} + */ + +/* + ------------------------------------ + ---------- Memory options ---------- + ------------------------------------ +*/ +/** + * @defgroup lwip_opts_mem Heap and memory pools + * @ingroup lwip_opts_infrastructure + * @{ + */ +/** + * MEM_LIBC_MALLOC==1: Use malloc/free/realloc provided by your C-library + * instead of the lwip internal allocator. Can save code size if you + * already use it. + */ +#define MEM_LIBC_MALLOC LWIP_XR_MEM + +/** + * MEMP_MEM_MALLOC==1: Use mem_malloc/mem_free instead of the lwip pool allocator. + * Especially useful with MEM_LIBC_MALLOC but handle with care regarding execution + * speed (heap alloc can be much slower than pool alloc) and usage from interrupts + * (especially if your netif driver allocates PBUF_POOL pbufs for received frames + * from interrupt)! + * ATTENTION: Currently, this uses the heap for ALL pools (also for private pools, + * not only for internal pools defined in memp_std.h)! + */ +#define MEMP_MEM_MALLOC LWIP_XR_MEM + +/** + * MEM_ALIGNMENT: should be set to the alignment of the CPU + * 4 byte alignment -> \#define MEM_ALIGNMENT 4 + * 2 byte alignment -> \#define MEM_ALIGNMENT 2 + */ +#define MEM_ALIGNMENT 4 + +/** + * MEM_SIZE: the size of the heap memory. If the application will send + * a lot of data that needs to be copied, this should be set high. + */ +#define MEM_SIZE (24 * 1024) + +/** + * MEMP_OVERFLOW_CHECK: memp overflow protection reserves a configurable + * amount of bytes before and after each memp element in every pool and fills + * it with a prominent default value. + * MEMP_OVERFLOW_CHECK == 0 no checking + * MEMP_OVERFLOW_CHECK == 1 checks each element when it is freed + * MEMP_OVERFLOW_CHECK >= 2 checks each element in every pool every time + * memp_malloc() or memp_free() is called (useful but slow!) + */ +#define MEMP_OVERFLOW_CHECK 0 + +/** + * MEMP_SANITY_CHECK==1: run a sanity check after each memp_free() to make + * sure that there are no cycles in the linked lists. + */ +#define MEMP_SANITY_CHECK 0 + +/** + * MEM_USE_POOLS==1: Use an alternative to malloc() by allocating from a set + * of memory pools of various sizes. When mem_malloc is called, an element of + * the smallest pool that can provide the length needed is returned. + * To use this, MEMP_USE_CUSTOM_POOLS also has to be enabled. + */ +#define MEM_USE_POOLS 0 + +/** + * MEM_USE_POOLS_TRY_BIGGER_POOL==1: if one malloc-pool is empty, try the next + * bigger pool - WARNING: THIS MIGHT WASTE MEMORY but it can make a system more + * reliable. */ +#define MEM_USE_POOLS_TRY_BIGGER_POOL 0 + +/** + * MEMP_USE_CUSTOM_POOLS==1: whether to include a user file lwippools.h + * that defines additional pools beyond the "standard" ones required + * by lwIP. If you set this to 1, you must have lwippools.h in your + * include path somewhere. + */ +#define MEMP_USE_CUSTOM_POOLS 0 + +/** + * Set this to 1 if you want to free PBUF_RAM pbufs (or call mem_free()) from + * interrupt context (or another context that doesn't allow waiting for a + * semaphore). + * If set to 1, mem_malloc will be protected by a semaphore and SYS_ARCH_PROTECT, + * while mem_free will only use SYS_ARCH_PROTECT. mem_malloc SYS_ARCH_UNPROTECTs + * with each loop so that mem_free can run. + * + * ATTENTION: As you can see from the above description, this leads to dis-/ + * enabling interrupts often, which can be slow! Also, on low memory, mem_malloc + * can need longer. + * + * If you don't want that, at least for NO_SYS=0, you can still use the following + * functions to enqueue a deallocation call which then runs in the tcpip_thread + * context: + * - pbuf_free_callback(p); + * - mem_free_callback(m); + */ +#define LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT 1 +/** + * @} + */ + +/* + ------------------------------------------------ + ---------- Internal Memory Pool Sizes ---------- + ------------------------------------------------ +*/ +/** + * @defgroup lwip_opts_memp Internal memory pools + * @ingroup lwip_opts_infrastructure + * @{ + */ +/** + * MEMP_NUM_PBUF: the number of memp struct pbufs (used for PBUF_ROM and PBUF_REF). + * If the application sends a lot of data out of ROM (or other static memory), + * this should be set high. + */ +#define MEMP_NUM_PBUF 6 + +/** + * MEMP_NUM_RAW_PCB: Number of raw connection PCBs + * (requires the LWIP_RAW option) + */ +#define MEMP_NUM_RAW_PCB 2 + +/** + * MEMP_NUM_UDP_PCB: the number of UDP protocol control blocks. One + * per active UDP "connection". + * (requires the LWIP_UDP option) + */ +#define MEMP_NUM_UDP_PCB 10 + +/** + * MEMP_NUM_TCP_PCB: the number of simultaneously active TCP connections. + * (requires the LWIP_TCP option) + */ +#define MEMP_NUM_TCP_PCB 8 + +/** + * MEMP_NUM_TCP_PCB_LISTEN: the number of listening TCP connections. + * (requires the LWIP_TCP option) + */ +#define MEMP_NUM_TCP_PCB_LISTEN 2 + +/** + * MEMP_NUM_TCP_SEG: the number of simultaneously queued TCP segments. + * (requires the LWIP_TCP option) + */ +#define MEMP_NUM_TCP_SEG 24 + +/** + * MEMP_NUM_REASSDATA: the number of IP packets simultaneously queued for + * reassembly (whole packets, not fragments!) + */ +#define MEMP_NUM_REASSDATA 2 + +/** + * MEMP_NUM_FRAG_PBUF: the number of IP fragments simultaneously sent + * (fragments, not whole packets!). + * This is only used with LWIP_NETIF_TX_SINGLE_PBUF==0 and only has to be > 1 + * with DMA-enabled MACs where the packet is not yet sent when netif->output + * returns. + */ +#define MEMP_NUM_FRAG_PBUF 2 + +/** + * MEMP_NUM_ARP_QUEUE: the number of simultaneously queued outgoing + * packets (pbufs) that are waiting for an ARP request (to resolve + * their destination address) to finish. + * (requires the ARP_QUEUEING option) + */ +#define MEMP_NUM_ARP_QUEUE 16 + +/** + * MEMP_NUM_IGMP_GROUP: The number of multicast groups whose network interfaces + * can be members at the same time (one per netif - allsystems group -, plus one + * per netif membership). + * (requires the LWIP_IGMP option) + */ +#define MEMP_NUM_IGMP_GROUP 3 + +/** + * MEMP_NUM_SYS_TIMEOUT: the number of simultaneously active timeouts. + * The default number of timeouts is calculated here for all enabled modules. + * The formula expects settings to be either '0' or '1'. + */ +#define MEMP_NUM_SYS_TIMEOUT (LWIP_TCP + IP_REASSEMBLY + LWIP_ARP + (2*LWIP_DHCP) + LWIP_AUTOIP + LWIP_IGMP + LWIP_DNS + (PPP_SUPPORT*6*MEMP_NUM_PPP_PCB) + (LWIP_IPV6 ? (1 + LWIP_IPV6_REASS + LWIP_IPV6_MLD) : 0)) + +/** + * MEMP_NUM_NETBUF: the number of struct netbufs. + * (only needed if you use the sequential API, like api_lib.c) + */ +#define MEMP_NUM_NETBUF 10 + +/** + * MEMP_NUM_NETCONN: the number of struct netconns. + * (only needed if you use the sequential API, like api_lib.c) + */ +#define MEMP_NUM_NETCONN 20 + +/** + * MEMP_NUM_TCPIP_MSG_API: the number of struct tcpip_msg, which are used + * for callback/timeout API communication. + * (only needed if you use tcpip.c) + */ +#define MEMP_NUM_TCPIP_MSG_API 4 + +/** + * MEMP_NUM_TCPIP_MSG_INPKT: the number of struct tcpip_msg, which are used + * for incoming packets. + * (only needed if you use tcpip.c) + */ +#define MEMP_NUM_TCPIP_MSG_INPKT 16 + +/** + * MEMP_NUM_NETDB: the number of concurrently running lwip_addrinfo() calls + * (before freeing the corresponding memory using lwip_freeaddrinfo()). + */ +#define MEMP_NUM_NETDB 1 + +/** + * MEMP_NUM_LOCALHOSTLIST: the number of host entries in the local host list + * if DNS_LOCAL_HOSTLIST_IS_DYNAMIC==1. + */ +#define MEMP_NUM_LOCALHOSTLIST 1 + +/** + * PBUF_POOL_SIZE: the number of buffers in the pbuf pool. + */ +#if (MEMP_MEM_MALLOC && LWIP_XR_MEM) +#define PBUF_POOL_SIZE 14 +#else +#define PBUF_POOL_SIZE 8 +#endif + +/** MEMP_NUM_API_MSG: the number of concurrently active calls to various + * socket, netconn, and tcpip functions + */ +#define MEMP_NUM_API_MSG MEMP_NUM_TCPIP_MSG_API // ??? + +/** MEMP_NUM_DNS_API_MSG: the number of concurrently active calls to netconn_gethostbyname + */ +#define MEMP_NUM_DNS_API_MSG MEMP_NUM_TCPIP_MSG_API // ??? + +/** MEMP_NUM_SOCKET_SETGETSOCKOPT_DATA: the number of concurrently active calls + * to getsockopt/setsockopt + */ +#define MEMP_NUM_SOCKET_SETGETSOCKOPT_DATA MEMP_NUM_TCPIP_MSG_API // ??? + +/** MEMP_NUM_NETIFAPI_MSG: the number of concurrently active calls to the + * netifapi functions + */ +#define MEMP_NUM_NETIFAPI_MSG MEMP_NUM_TCPIP_MSG_API // ??? +/** + * @} + */ + +/* + --------------------------------- + ---------- ARP options ---------- + --------------------------------- +*/ +/** + * @defgroup lwip_opts_arp ARP + * @ingroup lwip_opts_ipv4 + * @{ + */ +/** + * LWIP_ARP==1: Enable ARP functionality. + */ +#define LWIP_ARP 1 + +/** + * ARP_TABLE_SIZE: Number of active MAC-IP address pairs cached. + */ +#define ARP_TABLE_SIZE 8 + +/** the time an ARP entry stays valid after its last update, + * for ARP_TMR_INTERVAL = 1000, this is + * (60 * 5) seconds = 5 minutes. + */ +#define ARP_MAXAGE 300 // ??? + +/** + * ARP_QUEUEING==1: Multiple outgoing packets are queued during hardware address + * resolution. By default, only the most recent packet is queued per IP address. + * This is sufficient for most protocols and mainly reduces TCP connection + * startup time. Set this to 1 if you know your application sends more than one + * packet in a row to an IP address that is not in the ARP cache. + */ +#define ARP_QUEUEING 1 + +/** The maximum number of packets which may be queued for each + * unresolved address by other network layers. Defaults to 3, 0 means disabled. + * Old packets are dropped, new packets are queued. + */ +#define ARP_QUEUE_LEN 3 // ??? + +/** + * ETHARP_SUPPORT_VLAN==1: support receiving and sending ethernet packets with + * VLAN header. See the description of LWIP_HOOK_VLAN_CHECK and + * LWIP_HOOK_VLAN_SET hooks to check/set VLAN headers. + * Additionally, you can define ETHARP_VLAN_CHECK to an u16_t VLAN ID to check. + * If ETHARP_VLAN_CHECK is defined, only VLAN-traffic for this VLAN is accepted. + * If ETHARP_VLAN_CHECK is not defined, all traffic is accepted. + * Alternatively, define a function/define ETHARP_VLAN_CHECK_FN(eth_hdr, vlan) + * that returns 1 to accept a packet or 0 to drop a packet. + */ +#define ETHARP_SUPPORT_VLAN 0 + +/** LWIP_ETHERNET==1: enable ethernet support even though ARP might be disabled + */ +#define LWIP_ETHERNET LWIP_ARP + +/** ETH_PAD_SIZE: number of bytes added before the ethernet header to ensure + * alignment of payload after that header. Since the header is 14 bytes long, + * without this padding e.g. addresses in the IP header will not be aligned + * on a 32-bit boundary, so setting this to 2 can speed up 32-bit-platforms. + */ +#define ETH_PAD_SIZE 0 + +/** ETHARP_SUPPORT_STATIC_ENTRIES==1: enable code to support static ARP table + * entries (using etharp_add_static_entry/etharp_remove_static_entry). + */ +#define ETHARP_SUPPORT_STATIC_ENTRIES 0 + +/** ETHARP_TABLE_MATCH_NETIF==1: Match netif for ARP table entries. + * If disabled, duplicate IP address on multiple netifs are not supported + * (but this should only occur for AutoIP). + */ +#define ETHARP_TABLE_MATCH_NETIF 0 // ??? +/** + * @} + */ + +/* + -------------------------------- + ---------- IP options ---------- + -------------------------------- +*/ +/** + * @defgroup lwip_opts_ipv4 IPv4 + * @ingroup lwip_opts + * @{ + */ +/** + * LWIP_IPV4==1: Enable IPv4 + */ +#define LWIP_IPV4 1 + +/** + * IP_FORWARD==1: Enables the ability to forward IP packets across network + * interfaces. If you are going to run lwIP on a device with only one network + * interface, define this to 0. + */ +#define IP_FORWARD 0 + +/** + * IP_REASSEMBLY==1: Reassemble incoming fragmented IP packets. Note that + * this option does not affect outgoing packet sizes, which can be controlled + * via IP_FRAG. + */ +#define IP_REASSEMBLY 1 + +/** + * IP_FRAG==1: Fragment outgoing IP packets if their size exceeds MTU. Note + * that this option does not affect incoming packet sizes, which can be + * controlled via IP_REASSEMBLY. + */ +#define IP_FRAG 1 + +/** + * IP_OPTIONS_ALLOWED: Defines the behavior for IP options. + * IP_OPTIONS_ALLOWED==0: All packets with IP options are dropped. + * IP_OPTIONS_ALLOWED==1: IP options are allowed (but not parsed). + */ +#define IP_OPTIONS_ALLOWED 1 + +/** + * IP_REASS_MAXAGE: Maximum time (in multiples of IP_TMR_INTERVAL - so seconds, normally) + * a fragmented IP packet waits for all fragments to arrive. If not all fragments arrived + * in this time, the whole packet is discarded. + */ +#define IP_REASS_MAXAGE 3 + +/** + * IP_REASS_MAX_PBUFS: Total maximum amount of pbufs waiting to be reassembled. + * Since the received pbufs are enqueued, be sure to configure + * PBUF_POOL_SIZE > IP_REASS_MAX_PBUFS so that the stack is still able to receive + * packets even if the maximum amount of fragments is enqueued for reassembly! + */ +#define IP_REASS_MAX_PBUFS 4 + +/** + * IP_DEFAULT_TTL: Default value for Time-To-Live used by transport layers. + */ +#define IP_DEFAULT_TTL 255 + +/** + * IP_SOF_BROADCAST=1: Use the SOF_BROADCAST field to enable broadcast + * filter per pcb on udp and raw send operations. To enable broadcast filter + * on recv operations, you also have to set IP_SOF_BROADCAST_RECV=1. + */ +#define IP_SOF_BROADCAST 1 + +/** + * IP_SOF_BROADCAST_RECV (requires IP_SOF_BROADCAST=1) enable the broadcast + * filter on recv operations. + */ +#define IP_SOF_BROADCAST_RECV 1 + +/** + * IP_FORWARD_ALLOW_TX_ON_RX_NETIF==1: allow ip_forward() to send packets back + * out on the netif where it was received. This should only be used for + * wireless networks. + * ATTENTION: When this is 1, make sure your netif driver correctly marks incoming + * link-layer-broadcast/multicast packets as such using the corresponding pbuf flags! + */ +#define IP_FORWARD_ALLOW_TX_ON_RX_NETIF 0 + +/** + * LWIP_RANDOMIZE_INITIAL_LOCAL_PORTS==1: randomize the local port for the first + * local TCP/UDP pcb (default==0). This can prevent creating predictable port + * numbers after booting a device. + */ +#define LWIP_RANDOMIZE_INITIAL_LOCAL_PORTS 0 +/** + * @} + */ + +/* + ---------------------------------- + ---------- ICMP options ---------- + ---------------------------------- +*/ +/** + * @defgroup lwip_opts_icmp ICMP + * @ingroup lwip_opts_ipv4 + * @{ + */ +/** + * LWIP_ICMP==1: Enable ICMP module inside the IP stack. + * Be careful, disable that make your product non-compliant to RFC1122 + */ +#define LWIP_ICMP 1 + +/** + * ICMP_TTL: Default value for Time-To-Live used by ICMP packets. + */ +#define ICMP_TTL (IP_DEFAULT_TTL) + +/** + * LWIP_BROADCAST_PING==1: respond to broadcast pings (default is unicast only) + */ +#define LWIP_BROADCAST_PING 0 + +/** + * LWIP_MULTICAST_PING==1: respond to multicast pings (default is unicast only) + */ +#define LWIP_MULTICAST_PING 0 +/** + * @} + */ + +/* + --------------------------------- + ---------- RAW options ---------- + --------------------------------- +*/ +/** + * @defgroup lwip_opts_raw RAW + * @ingroup lwip_opts_callback + * @{ + */ +/** + * LWIP_RAW==1: Enable application layer to hook into the IP layer itself. + */ +#define LWIP_RAW 1 + +/** + * LWIP_RAW==1: Enable application layer to hook into the IP layer itself. + */ +#define RAW_TTL (IP_DEFAULT_TTL) +/** + * @} + */ + +/* + ---------------------------------- + ---------- DHCP options ---------- + ---------------------------------- +*/ +/** + * @defgroup lwip_opts_dhcp DHCP + * @ingroup lwip_opts_ipv4 + * @{ + */ +/** + * LWIP_DHCP==1: Enable DHCP module. + */ +#define LWIP_DHCP 1 + +/** + * DHCP_DOES_ARP_CHECK==1: Do an ARP check on the offered address. + */ +#define DHCP_DOES_ARP_CHECK ((LWIP_DHCP) && (LWIP_ARP)) + +/** + * LWIP_DHCP_CHECK_LINK_UP==1: dhcp_start() only really starts if the netif has + * NETIF_FLAG_LINK_UP set in its flags. As this is only an optimization and + * netif drivers might not set this flag, the default is off. If enabled, + * netif_set_link_up() must be called to continue dhcp starting. + */ +#define LWIP_DHCP_CHECK_LINK_UP 1 // ??? + +/** + * LWIP_DHCP_BOOTP_FILE==1: Store offered_si_addr and boot_file_name. + */ +#define LWIP_DHCP_BOOTP_FILE 0 // ??? + +/** + * LWIP_DHCP_GETS_NTP==1: Request NTP servers with discover/select. For each + * response packet, an callback is called, which has to be provided by the port: + * void dhcp_set_ntp_servers(u8_t num_ntp_servers, ip_addr_t* ntp_server_addrs); +*/ +#define LWIP_DHCP_GET_NTP_SRV 0 // ??? + +/** + * The maximum of NTP servers requested + */ +#define LWIP_DHCP_MAX_NTP_SERVERS 1 // ??? + +/** + * LWIP_DHCP_MAX_DNS_SERVERS > 0: Request DNS servers with discover/select. + * DHCP servers received in the response are passed to DNS via @ref dns_setserver() + * (up to the maximum limit defined here). + */ +#define LWIP_DHCP_MAX_DNS_SERVERS DNS_MAX_SERVERS // ??? +/** + * @} + */ + +/* + ------------------------------------ + ---------- AUTOIP options ---------- + ------------------------------------ +*/ +/** + * @defgroup lwip_opts_autoip AUTOIP + * @ingroup lwip_opts_ipv4 + * @{ + */ +/** + * LWIP_AUTOIP==1: Enable AUTOIP module. + */ +#define LWIP_AUTOIP 0 + +/** + * LWIP_DHCP_AUTOIP_COOP==1: Allow DHCP and AUTOIP to be both enabled on + * the same interface at the same time. + */ +#define LWIP_DHCP_AUTOIP_COOP 0 + +/** + * LWIP_DHCP_AUTOIP_COOP_TRIES: Set to the number of DHCP DISCOVER probes + * that should be sent before falling back on AUTOIP (the DHCP client keeps + * running in this case). This can be set as low as 1 to get an AutoIP address + * very quickly, but you should be prepared to handle a changing IP address + * when DHCP overrides AutoIP. + */ +#define LWIP_DHCP_AUTOIP_COOP_TRIES 9 +/** + * @} + */ + +/* + ---------------------------------- + ----- SNMP MIB2 support ----- + ---------------------------------- +*/ +/** + * @defgroup lwip_opts_mib2 SNMP MIB2 callbacks + * @ingroup lwip_opts_infrastructure + * @{ + */ +/** + * LWIP_MIB2_CALLBACKS==1: Turn on SNMP MIB2 callbacks. + * Turn this on to get callbacks needed to implement MIB2. + * Usually MIB2_STATS should be enabled, too. + */ +#define LWIP_MIB2_CALLBACKS 0 +/** + * @} + */ + +/* + ---------------------------------- + ----- Multicast/IGMP options ----- + ---------------------------------- +*/ +/** + * @defgroup lwip_opts_igmp IGMP + * @ingroup lwip_opts_ipv4 + * @{ + */ +/** + * LWIP_IGMP==1: Turn on IGMP module. + */ +#define LWIP_IGMP 1 + +/** + * LWIP_MULTICAST_TX_OPTIONS==1: Enable multicast TX support like the socket options + * IP_MULTICAST_TTL/IP_MULTICAST_IF/IP_MULTICAST_LOOP + */ +#define LWIP_MULTICAST_TX_OPTIONS (LWIP_IGMP && LWIP_UDP) +/** + * @} + */ + +/* + ---------------------------------- + ---------- DNS options ----------- + ---------------------------------- +*/ +/** + * @defgroup lwip_opts_dns DNS + * @ingroup lwip_opts_callback + * @{ + */ +/** + * LWIP_DNS==1: Turn on DNS module. UDP must be available for DNS + * transport. + */ +#define LWIP_DNS 1 + +/** DNS maximum number of entries to maintain locally. */ +#define DNS_TABLE_SIZE 3 + +/** DNS maximum host name length supported in the name table. */ +#define DNS_MAX_NAME_LENGTH 256 + +/** The maximum of DNS servers + * The first server can be initialized automatically by defining + * DNS_SERVER_ADDRESS(ipaddr), where 'ipaddr' is an 'ip_addr_t*' + */ +#define DNS_MAX_SERVERS 2 + +/** DNS do a name checking between the query and the response. */ +#define DNS_DOES_NAME_CHECK 1 + +/** LWIP_DNS_SECURE: controls the security level of the DNS implementation + * Use all DNS security features by default. + * This is overridable but should only be needed by very small targets + * or when using against non standard DNS servers. */ +#define LWIP_DNS_SECURE (LWIP_DNS_SECURE_RAND_XID | LWIP_DNS_SECURE_NO_MULTIPLE_OUTSTANDING | LWIP_DNS_SECURE_RAND_SRC_PORT) // ??? + +/** DNS_LOCAL_HOSTLIST: Implements a local host-to-address list. If enabled, you have to define an initializer: + * \#define DNS_LOCAL_HOSTLIST_INIT {DNS_LOCAL_HOSTLIST_ELEM("host_ip4", IPADDR4_INIT_BYTES(1,2,3,4)), \ + * DNS_LOCAL_HOSTLIST_ELEM("host_ip6", IPADDR6_INIT_HOST(123, 234, 345, 456)} + * + * Instead, you can also use an external function: + * \#define DNS_LOOKUP_LOCAL_EXTERN(x) extern err_t my_lookup_function(const char *name, ip_addr_t *addr, u8_t dns_addrtype) + * that looks up the IP address and returns ERR_OK if found (LWIP_DNS_ADDRTYPE_xxx is passed in dns_addrtype). + */ +#define DNS_LOCAL_HOSTLIST 0 + +/** If this is turned on, the local host-list can be dynamically changed + * at runtime. */ +#define DNS_LOCAL_HOSTLIST_IS_DYNAMIC 0 + +/** Set this to 1 to enable querying ".local" names via mDNS + * using a One-Shot Multicast DNS Query */ +#define LWIP_DNS_SUPPORT_MDNS_QUERIES 0 // ??? +/** + * @} + */ + +/* + --------------------------------- + ---------- UDP options ---------- + --------------------------------- +*/ +/** + * @defgroup lwip_opts_udp UDP + * @ingroup lwip_opts_callback + * @{ + */ +/** + * LWIP_UDP==1: Turn on UDP. + */ +#define LWIP_UDP 1 + +/** + * LWIP_UDPLITE==1: Turn on UDP-Lite. (Requires LWIP_UDP) + */ +#define LWIP_UDPLITE 0 + +/** + * UDP_TTL: Default Time-To-Live value. + */ +#define UDP_TTL (IP_DEFAULT_TTL) + +/** + * LWIP_NETBUF_RECVINFO==1: append destination addr and port to every netbuf. + */ +#define LWIP_NETBUF_RECVINFO 0 +/** + * @} + */ + +/* + --------------------------------- + ---------- TCP options ---------- + --------------------------------- +*/ +/** + * @defgroup lwip_opts_tcp TCP + * @ingroup lwip_opts_callback + * @{ + */ +/** + * LWIP_TCP==1: Turn on TCP. + */ +#define LWIP_TCP 1 + +/** + * TCP_TTL: Default Time-To-Live value. + */ +#define TCP_TTL (IP_DEFAULT_TTL) + +/** + * TCP_WND: The size of a TCP window. This must be at least + * (2 * TCP_MSS) for things to work well. + * ATTENTION: when using TCP_RCV_SCALE, TCP_WND is the total size + * with scaling applied. Maximum window value in the TCP header + * will be TCP_WND >> TCP_RCV_SCALE + */ +#define TCP_WND (4 * TCP_MSS) + +/** + * TCP_MAXRTX: Maximum number of retransmissions of data segments. + */ +#define TCP_MAXRTX 12 + +/** + * TCP_SYNMAXRTX: Maximum number of retransmissions of SYN segments. + */ +#define TCP_SYNMAXRTX 6 + +/** + * TCP_QUEUE_OOSEQ==1: TCP will queue segments that arrive out of order. + * Define to 0 if your device is low on memory. + */ +#define TCP_QUEUE_OOSEQ (LWIP_TCP) + +/** + * TCP_MSS: TCP Maximum segment size. (default is 536, a conservative default, + * you might want to increase this.) + * For the receive side, this MSS is advertised to the remote side + * when opening a connection. For the transmit size, this MSS sets + * an upper limit on the MSS advertised by the remote host. + */ +#define TCP_MSS 1460 + +/** + * TCP_CALCULATE_EFF_SEND_MSS: "The maximum size of a segment that TCP really + * sends, the 'effective send MSS,' MUST be the smaller of the send MSS (which + * reflects the available reassembly buffer size at the remote host) and the + * largest size permitted by the IP layer" (RFC 1122) + * Setting this to 1 enables code that checks TCP_MSS against the MTU of the + * netif used for a connection and limits the MSS if it would be too big otherwise. + */ +#define TCP_CALCULATE_EFF_SEND_MSS 1 + + +/** + * TCP_SND_BUF: TCP sender buffer space (bytes). + * To achieve good performance, this should be at least 2 * TCP_MSS. + */ +#define TCP_SND_BUF (2 * TCP_MSS) + +/** + * TCP_SND_QUEUELEN: TCP sender buffer space (pbufs). This must be at least + * as much as (2 * TCP_SND_BUF/TCP_MSS) for things to work. + */ +#define TCP_SND_QUEUELEN ((4 * (TCP_SND_BUF) + (TCP_MSS - 1))/(TCP_MSS)) + +/** + * TCP_SNDLOWAT: TCP writable space (bytes). This must be less than + * TCP_SND_BUF. It is the amount of space which must be available in the + * TCP snd_buf for select to return writable (combined with TCP_SNDQUEUELOWAT). + */ +#define TCP_SNDLOWAT LWIP_MIN(LWIP_MAX(((TCP_SND_BUF)/2), (2 * TCP_MSS) + 1), (TCP_SND_BUF) - 1) + +/** + * TCP_SNDQUEUELOWAT: TCP writable bufs (pbuf count). This must be less + * than TCP_SND_QUEUELEN. If the number of pbufs queued on a pcb drops below + * this number, select returns writable (combined with TCP_SNDLOWAT). + */ +#define TCP_SNDQUEUELOWAT LWIP_MAX(((TCP_SND_QUEUELEN)/2), 5) + +/** + * TCP_OOSEQ_MAX_BYTES: The maximum number of bytes queued on ooseq per pcb. + * Default is 0 (no limit). Only valid for TCP_QUEUE_OOSEQ==1. + */ +#define TCP_OOSEQ_MAX_BYTES 0 + +/** + * TCP_OOSEQ_MAX_PBUFS: The maximum number of pbufs queued on ooseq per pcb. + * Default is 0 (no limit). Only valid for TCP_QUEUE_OOSEQ==1. + */ +#define TCP_OOSEQ_MAX_PBUFS 0 + +/** + * TCP_LISTEN_BACKLOG: Enable the backlog option for tcp listen pcb. + */ +#define TCP_LISTEN_BACKLOG 1 + +/** + * The maximum allowed backlog for TCP listen netconns. + * This backlog is used unless another is explicitly specified. + * 0xff is the maximum (u8_t). + */ +#define TCP_DEFAULT_LISTEN_BACKLOG 0xff + +/** + * TCP_OVERSIZE: The maximum number of bytes that tcp_write may + * allocate ahead of time in an attempt to create shorter pbuf chains + * for transmission. The meaningful range is 0 to TCP_MSS. Some + * suggested values are: + * + * 0: Disable oversized allocation. Each tcp_write() allocates a new + pbuf (old behaviour). + * 1: Allocate size-aligned pbufs with minimal excess. Use this if your + * scatter-gather DMA requires aligned fragments. + * 128: Limit the pbuf/memory overhead to 20%. + * TCP_MSS: Try to create unfragmented TCP packets. + * TCP_MSS/4: Try to create 4 fragments or less per TCP packet. + */ +#define TCP_OVERSIZE TCP_MSS + +/** + * LWIP_TCP_TIMESTAMPS==1: support the TCP timestamp option. + * The timestamp option is currently only used to help remote hosts, it is not + * really used locally. Therefore, it is only enabled when a TS option is + * received in the initial SYN packet from a remote host. + */ +#define LWIP_TCP_TIMESTAMPS 0 + +/** + * TCP_WND_UPDATE_THRESHOLD: difference in window to trigger an + * explicit window update + */ +#define TCP_WND_UPDATE_THRESHOLD LWIP_MIN((TCP_WND / 4), (TCP_MSS * 4)) + +/** + * LWIP_EVENT_API and LWIP_CALLBACK_API: Only one of these should be set to 1. + * LWIP_EVENT_API==1: The user defines lwip_tcp_event() to receive all + * events (accept, sent, etc) that happen in the system. + * LWIP_CALLBACK_API==1: The PCB callback function is called directly + * for the event. This is the default. + */ +#define LWIP_EVENT_API 0 +#define LWIP_CALLBACK_API 1 + +/** + * LWIP_WND_SCALE and TCP_RCV_SCALE: + * Set LWIP_WND_SCALE to 1 to enable window scaling. + * Set TCP_RCV_SCALE to the desired scaling factor (shift count in the + * range of [0..14]). + * When LWIP_WND_SCALE is enabled but TCP_RCV_SCALE is 0, we can use a large + * send window while having a small receive window only. + */ +#define LWIP_WND_SCALE 0 // ??? +#define TCP_RCV_SCALE 0 // ??? +/** + * @} + */ + +/* + ---------------------------------- + ---------- Pbuf options ---------- + ---------------------------------- +*/ +/** + * @defgroup lwip_opts_pbuf PBUF + * @ingroup lwip_opts + * @{ + */ +/** + * PBUF_LINK_HLEN: the number of bytes that should be allocated for a + * link level header. The default is 14, the standard value for + * Ethernet. + */ +#if defined LWIP_HOOK_VLAN_SET +#define PBUF_LINK_HLEN (18 + ETH_PAD_SIZE) +#else /* LWIP_HOOK_VLAN_SET */ +#define PBUF_LINK_HLEN (14 + ETH_PAD_SIZE) +#endif /* LWIP_HOOK_VLAN_SET */ + +/** + * PBUF_LINK_ENCAPSULATION_HLEN: the number of bytes that should be allocated + * for an additional encapsulation header before ethernet headers (e.g. 802.11) + */ +#define PBUF_LINK_ENCAPSULATION_HLEN 0u + +/** + * PBUF_POOL_BUFSIZE: the size of each pbuf in the pbuf pool. The default is + * designed to accommodate single full size TCP frame in one pbuf, including + * TCP_MSS, IP header, and link header. + */ +#define PBUF_POOL_BUFSIZE LWIP_MEM_ALIGN_SIZE(TCP_MSS+40+PBUF_LINK_ENCAPSULATION_HLEN+PBUF_LINK_HLEN) +/** + * @} + */ + +/* + ------------------------------------------------ + ---------- Network Interfaces options ---------- + ------------------------------------------------ +*/ +/** + * @defgroup lwip_opts_netif NETIF + * @ingroup lwip_opts + * @{ + */ +/** + * LWIP_NETIF_HOSTNAME==1: use DHCP_OPTION_HOSTNAME with netif's hostname + * field. + */ +#define LWIP_NETIF_HOSTNAME 1 + +/** + * LWIP_NETIF_API==1: Support netif api (in netifapi.c) + */ +#define LWIP_NETIF_API 1 + +/** + * LWIP_NETIF_STATUS_CALLBACK==1: Support a callback function whenever an interface + * changes its up/down status (i.e., due to DHCP IP acquisition) + */ +#define LWIP_NETIF_STATUS_CALLBACK 1 + +/** + * LWIP_NETIF_LINK_CALLBACK==1: Support a callback function from an interface + * whenever the link changes (i.e., link down) + */ +#define LWIP_NETIF_LINK_CALLBACK 1 + +/** + * LWIP_NETIF_REMOVE_CALLBACK==1: Support a callback function that is called + * when a netif has been removed + */ +#define LWIP_NETIF_REMOVE_CALLBACK 0 + +/** + * LWIP_NETIF_HWADDRHINT==1: Cache link-layer-address hints (e.g. table + * indices) in struct netif. TCP and UDP can make use of this to prevent + * scanning the ARP table for every sent packet. While this is faster for big + * ARP tables or many concurrent connections, it might be counterproductive + * if you have a tiny ARP table or if there never are concurrent connections. + */ +#define LWIP_NETIF_HWADDRHINT 0 + +/** + * LWIP_NETIF_TX_SINGLE_PBUF: if this is set to 1, lwIP tries to put all data + * to be sent into one single pbuf. This is for compatibility with DMA-enabled + * MACs that do not support scatter-gather. + * Beware that this might involve CPU-memcpy before transmitting that would not + * be needed without this flag! Use this only if you need to! + * + * @todo: TCP and IP-frag do not work with this, yet: + */ +#define LWIP_NETIF_TX_SINGLE_PBUF 0 + +/** + * LWIP_NUM_NETIF_CLIENT_DATA: Number of clients that may store + * data in client_data member array of struct netif. + */ +#define LWIP_NUM_NETIF_CLIENT_DATA 0 // ??? +/** + * @} + */ + +/* + ------------------------------------ + ---------- LOOPIF options ---------- + ------------------------------------ +*/ +/** + * @defgroup lwip_opts_loop Loopback interface + * @ingroup lwip_opts_netif + * @{ + */ +/** + * LWIP_HAVE_LOOPIF==1: Support loop interface (127.0.0.1). + * This is only needed when no real netifs are available. If at least one other + * netif is available, loopback traffic uses this netif. + */ +#define LWIP_HAVE_LOOPIF 1 + +/** + * LWIP_LOOPIF_MULTICAST==1: Support multicast/IGMP on loop interface (127.0.0.1). + */ +#define LWIP_LOOPIF_MULTICAST 0 + +/** + * LWIP_NETIF_LOOPBACK==1: Support sending packets with a destination IP + * address equal to the netif IP address, looping them back up the stack. + */ +#define LWIP_NETIF_LOOPBACK 1 + +/** + * LWIP_LOOPBACK_MAX_PBUFS: Maximum number of pbufs on queue for loopback + * sending for each netif (0 = disabled) + */ +#define LWIP_LOOPBACK_MAX_PBUFS 0 + +/** + * LWIP_NETIF_LOOPBACK_MULTITHREADING: Indicates whether threading is enabled in + * the system, as netifs must change how they behave depending on this setting + * for the LWIP_NETIF_LOOPBACK option to work. + * Setting this is needed to avoid reentering non-reentrant functions like + * tcp_input(). + * LWIP_NETIF_LOOPBACK_MULTITHREADING==1: Indicates that the user is using a + * multithreaded environment like tcpip.c. In this case, netif->input() + * is called directly. + * LWIP_NETIF_LOOPBACK_MULTITHREADING==0: Indicates a polling (or NO_SYS) setup. + * The packets are put on a list and netif_poll() must be called in + * the main application loop. + */ +#define LWIP_NETIF_LOOPBACK_MULTITHREADING (!NO_SYS) +/** + * @} + */ + +/* + ------------------------------------ + ---------- Thread options ---------- + ------------------------------------ +*/ +/** + * @defgroup lwip_opts_thread Threading + * @ingroup lwip_opts_infrastructure + * @{ + */ +/** + * TCPIP_THREAD_NAME: The name assigned to the main tcpip thread. + */ +#define TCPIP_THREAD_NAME "tcpip" + +/** + * TCPIP_THREAD_STACKSIZE: The stack size used by the main tcpip thread. + * The stack size value itself is platform-dependent, but is passed to + * sys_thread_new() when the thread is created. + */ +#define TCPIP_THREAD_STACKSIZE (2 * 1024) + +/** + * TCPIP_THREAD_PRIO: The priority assigned to the main tcpip thread. + * The priority value itself is platform-dependent, but is passed to + * sys_thread_new() when the thread is created. + */ +#define TCPIP_THREAD_PRIO (9) + +/** + * TCPIP_MBOX_SIZE: The mailbox size for the tcpip thread messages + * The queue size value itself is platform-dependent, but is passed to + * sys_mbox_new() when tcpip_init is called. + */ +#define TCPIP_MBOX_SIZE 32 + +/** + * Define this to something that triggers a watchdog. This is called from + * tcpip_thread after processing a message. + */ +#define LWIP_TCPIP_THREAD_ALIVE() + +/** + * SLIPIF_THREAD_NAME: The name assigned to the slipif_loop thread. + */ +#define SLIPIF_THREAD_NAME "slipif_loop" + +/** + * SLIP_THREAD_STACKSIZE: The stack size used by the slipif_loop thread. + * The stack size value itself is platform-dependent, but is passed to + * sys_thread_new() when the thread is created. + */ +#define SLIPIF_THREAD_STACKSIZE (4 * 1024) + +/** + * SLIPIF_THREAD_PRIO: The priority assigned to the slipif_loop thread. + * The priority value itself is platform-dependent, but is passed to + * sys_thread_new() when the thread is created. + */ +#define SLIPIF_THREAD_PRIO (TCPIP_THREAD_PRIO) + +/** + * DEFAULT_THREAD_NAME: The name assigned to any other lwIP thread. + */ +#define DEFAULT_THREAD_NAME "lwIP" + +/** + * DEFAULT_THREAD_STACKSIZE: The stack size used by any other lwIP thread. + * The stack size value itself is platform-dependent, but is passed to + * sys_thread_new() when the thread is created. + */ +#define DEFAULT_THREAD_STACKSIZE (4 * 1024) + +/** + * DEFAULT_THREAD_PRIO: The priority assigned to any other lwIP thread. + * The priority value itself is platform-dependent, but is passed to + * sys_thread_new() when the thread is created. + */ +#define DEFAULT_THREAD_PRIO (TCPIP_THREAD_PRIO) + +/** + * DEFAULT_RAW_RECVMBOX_SIZE: The mailbox size for the incoming packets on a + * NETCONN_RAW. The queue size value itself is platform-dependent, but is passed + * to sys_mbox_new() when the recvmbox is created. + */ +#define DEFAULT_RAW_RECVMBOX_SIZE 4 + +/** + * DEFAULT_UDP_RECVMBOX_SIZE: The mailbox size for the incoming packets on a + * NETCONN_UDP. The queue size value itself is platform-dependent, but is passed + * to sys_mbox_new() when the recvmbox is created. + */ +#define DEFAULT_UDP_RECVMBOX_SIZE 8 + +/** + * DEFAULT_TCP_RECVMBOX_SIZE: The mailbox size for the incoming packets on a + * NETCONN_TCP. The queue size value itself is platform-dependent, but is passed + * to sys_mbox_new() when the recvmbox is created. + */ +#define DEFAULT_TCP_RECVMBOX_SIZE 8 + +/** + * DEFAULT_ACCEPTMBOX_SIZE: The mailbox size for the incoming connections. + * The queue size value itself is platform-dependent, but is passed to + * sys_mbox_new() when the acceptmbox is created. + */ +#define DEFAULT_ACCEPTMBOX_SIZE 4 +/** + * @} + */ + +/* + ---------------------------------------------- + ---------- Sequential layer options ---------- + ---------------------------------------------- +*/ +/** + * @defgroup lwip_opts_netconn Netconn + * @ingroup lwip_opts_threadsafe_apis + * @{ + */ +/** + * LWIP_NETCONN==1: Enable Netconn API (require to use api_lib.c) + */ +#define LWIP_NETCONN 1 + +/** LWIP_TCPIP_TIMEOUT==1: Enable tcpip_timeout/tcpip_untimeout to create + * timers running in tcpip_thread from another thread. + */ +#define LWIP_TCPIP_TIMEOUT 0 + +/** LWIP_NETCONN_SEM_PER_THREAD==1: Use one (thread-local) semaphore per + * thread calling socket/netconn functions instead of allocating one + * semaphore per netconn (and per select etc.) + * ATTENTION: a thread-local semaphore for API calls is needed: + * - LWIP_NETCONN_THREAD_SEM_GET() returning a sys_sem_t* + * - LWIP_NETCONN_THREAD_SEM_ALLOC() creating the semaphore + * - LWIP_NETCONN_THREAD_SEM_FREE() freeing the semaphore + * The latter 2 can be invoked up by calling netconn_thread_init()/netconn_thread_cleanup(). + * Ports may call these for threads created with sys_thread_new(). + */ +#define LWIP_NETCONN_SEM_PER_THREAD 0 // ???, set to 1 + +/** LWIP_NETCONN_FULLDUPLEX==1: Enable code that allows reading from one thread, + * writing from a 2nd thread and closing from a 3rd thread at the same time. + * ATTENTION: This is currently really alpha! Some requirements: + * - LWIP_NETCONN_SEM_PER_THREAD==1 is required to use one socket/netconn from + * multiple threads at once + * - sys_mbox_free() has to unblock receive tasks waiting on recvmbox/acceptmbox + * and prevent a task pending on this during/after deletion + */ +#define LWIP_NETCONN_FULLDUPLEX 0 +/** + * @} + */ + +/* + ------------------------------------ + ---------- Socket options ---------- + ------------------------------------ +*/ +/** + * @defgroup lwip_opts_socket Sockets + * @ingroup lwip_opts_threadsafe_apis + * @{ + */ +/** + * LWIP_SOCKET==1: Enable Socket API (require to use sockets.c) + */ +#define LWIP_SOCKET 1 + +/* LWIP_SOCKET_SET_ERRNO==1: Set errno when socket functions cannot complete + * successfully, as required by POSIX. Default is POSIX-compliant. + */ +#define LWIP_SOCKET_SET_ERRNO 1 // ??? + +/** + * LWIP_COMPAT_SOCKETS==1: Enable BSD-style sockets functions names through defines. + * LWIP_COMPAT_SOCKETS==2: Same as ==1 but correctly named functions are created. + * While this helps code completion, it might conflict with existing libraries. + * (only used if you use sockets.c) + */ +#define LWIP_COMPAT_SOCKETS 1 + +/** + * LWIP_POSIX_SOCKETS_IO_NAMES==1: Enable POSIX-style sockets functions names. + * Disable this option if you use a POSIX operating system that uses the same + * names (read, write & close). (only used if you use sockets.c) + */ +#define LWIP_POSIX_SOCKETS_IO_NAMES 1 + +/** + * LWIP_SOCKET_OFFSET==n: Increases the file descriptor number created by LwIP with n. + * This can be useful when there are multiple APIs which create file descriptors. + * When they all start with a different offset and you won't make them overlap you can + * re implement read/write/close/ioctl/fnctl to send the requested action to the right + * library (sharing select will need more work though). + */ +#define LWIP_SOCKET_OFFSET 0 // ??? + +/** + * LWIP_TCP_KEEPALIVE==1: Enable TCP_KEEPIDLE, TCP_KEEPINTVL and TCP_KEEPCNT + * options processing. Note that TCP_KEEPIDLE and TCP_KEEPINTVL have to be set + * in seconds. (does not require sockets.c, and will affect tcp.c) + */ +#define LWIP_TCP_KEEPALIVE 1 + +/** + * LWIP_SO_SNDTIMEO==1: Enable send timeout for sockets/netconns and + * SO_SNDTIMEO processing. + */ +#define LWIP_SO_SNDTIMEO 1 + +/** + * LWIP_SO_RCVTIMEO==1: Enable receive timeout for sockets/netconns and + * SO_RCVTIMEO processing. + */ +#define LWIP_SO_RCVTIMEO 1 + +/** + * LWIP_SO_SNDRCVTIMEO_NONSTANDARD==1: SO_RCVTIMEO/SO_SNDTIMEO take an int + * (milliseconds, much like winsock does) instead of a struct timeval (default). + */ +#define LWIP_SO_SNDRCVTIMEO_NONSTANDARD 0 + +/** + * LWIP_SO_RCVBUF==1: Enable SO_RCVBUF processing. + */ +#define LWIP_SO_RCVBUF 0 + +/** + * LWIP_SO_LINGER==1: Enable SO_LINGER processing. + */ +#define LWIP_SO_LINGER 0 // ??? + +/** + * If LWIP_SO_RCVBUF is used, this is the default value for recv_bufsize. + */ +#define RECV_BUFSIZE_DEFAULT INT_MAX // ??? + +/** + * By default, TCP socket/netconn close waits 20 seconds max to send the FIN + */ +#define LWIP_TCP_CLOSE_TIMEOUT_MS_DEFAULT 20000 + +/** + * SO_REUSE==1: Enable SO_REUSEADDR option. + */ +#define SO_REUSE 1 + +/** + * SO_REUSE_RXTOALL==1: Pass a copy of incoming broadcast/multicast packets + * to all local matches if SO_REUSEADDR is turned on. + * WARNING: Adds a memcpy for every packet if passing to more than one pcb! + */ +#define SO_REUSE_RXTOALL 0 + +/** + * LWIP_FIONREAD_LINUXMODE==0 (default): ioctl/FIONREAD returns the amount of + * pending data in the network buffer. This is the way windows does it. It's + * the default for lwIP since it is smaller. + * LWIP_FIONREAD_LINUXMODE==1: ioctl/FIONREAD returns the size of the next + * pending datagram in bytes. This is the way linux does it. This code is only + * here for compatibility. + */ +#define LWIP_FIONREAD_LINUXMODE 0 // ??? +/** + * @} + */ + +/* + ---------------------------------------- + ---------- Statistics options ---------- + ---------------------------------------- +*/ +/** + * @defgroup lwip_opts_stats Statistics + * @ingroup lwip_opts_debug + * @{ + */ +/** + * LWIP_STATS==1: Enable statistics collection in lwip_stats. + */ +#define LWIP_STATS 1 // ??? + +#if LWIP_STATS + +/** + * LWIP_STATS_DISPLAY==1: Compile in the statistics output functions. + */ +#define LWIP_STATS_DISPLAY 0 + +/** + * LINK_STATS==1: Enable link stats. + */ +#define LINK_STATS 1 + +/** + * ETHARP_STATS==1: Enable etharp stats. + */ +#define ETHARP_STATS (LWIP_ARP) + +/** + * IP_STATS==1: Enable IP stats. + */ +#define IP_STATS 1 + +/** + * IPFRAG_STATS==1: Enable IP fragmentation stats. Default is + * on if using either frag or reass. + */ +#define IPFRAG_STATS (IP_REASSEMBLY || IP_FRAG) + +/** + * ICMP_STATS==1: Enable ICMP stats. + */ +#define ICMP_STATS 1 + +/** + * IGMP_STATS==1: Enable IGMP stats. + */ +#define IGMP_STATS (LWIP_IGMP) + +/** + * UDP_STATS==1: Enable UDP stats. Default is on if + * UDP enabled, otherwise off. + */ +#define UDP_STATS (LWIP_UDP) + +/** + * TCP_STATS==1: Enable TCP stats. Default is on if TCP + * enabled, otherwise off. + */ +#define TCP_STATS (LWIP_TCP) + +/** + * MEM_STATS==1: Enable mem.c stats. + */ +#define MEM_STATS ((MEM_LIBC_MALLOC == 0) && (MEM_USE_POOLS == 0)) + +/** + * MEMP_STATS==1: Enable memp.c pool stats. + */ +#define MEMP_STATS (MEMP_MEM_MALLOC == 0) + +/** + * SYS_STATS==1: Enable system stats (sem and mbox counts, etc). + */ +#define SYS_STATS (NO_SYS == 0) + +/** + * IP6_STATS==1: Enable IPv6 stats. + */ +#define IP6_STATS (LWIP_IPV6) + +/** + * ICMP6_STATS==1: Enable ICMP for IPv6 stats. + */ +#define ICMP6_STATS (LWIP_IPV6 && LWIP_ICMP6) + +/** + * IP6_FRAG_STATS==1: Enable IPv6 fragmentation stats. + */ +#define IP6_FRAG_STATS (LWIP_IPV6 && (LWIP_IPV6_FRAG || LWIP_IPV6_REASS)) + +/** + * MLD6_STATS==1: Enable MLD for IPv6 stats. + */ +#define MLD6_STATS (LWIP_IPV6 && LWIP_IPV6_MLD) + +/** + * ND6_STATS==1: Enable Neighbor discovery for IPv6 stats. + */ +#define ND6_STATS (LWIP_IPV6) + +/** + * MIB2_STATS==1: Stats for SNMP MIB2. + */ +#define MIB2_STATS 1 + +#else /* LWIP_STATS */ + +#define LINK_STATS 0 +#define ETHARP_STATS 0 +#define IP_STATS 0 +#define IPFRAG_STATS 0 +#define ICMP_STATS 0 +#define IGMP_STATS 0 +#define UDP_STATS 0 +#define TCP_STATS 0 +#define MEM_STATS 0 +#define MEMP_STATS 0 +#define SYS_STATS 0 +#define LWIP_STATS_DISPLAY 0 +#define IP6_STATS 0 +#define ICMP6_STATS 0 +#define IP6_FRAG_STATS 0 +#define MLD6_STATS 0 +#define ND6_STATS 0 +#define MIB2_STATS 0 + +#endif /* LWIP_STATS */ +/** + * @} + */ + +/* + -------------------------------------- + ---------- Checksum options ---------- + -------------------------------------- +*/ +/** + * @defgroup lwip_opts_checksum Checksum + * @ingroup lwip_opts_infrastructure + * @{ + */ +/** + * LWIP_CHECKSUM_CTRL_PER_NETIF==1: Checksum generation/check can be enabled/disabled + * per netif. + * ATTENTION: if enabled, the CHECKSUM_GEN_* and CHECKSUM_CHECK_* defines must be enabled! + */ +#define LWIP_CHECKSUM_CTRL_PER_NETIF 0 // ??? + +/** + * CHECKSUM_GEN_IP==1: Generate checksums in software for outgoing IP packets. + */ +#define CHECKSUM_GEN_IP 1 + +/** + * CHECKSUM_GEN_UDP==1: Generate checksums in software for outgoing UDP packets. + */ +#define CHECKSUM_GEN_UDP 1 + +/** + * CHECKSUM_GEN_TCP==1: Generate checksums in software for outgoing TCP packets. + */ +#define CHECKSUM_GEN_TCP 1 + +/** + * CHECKSUM_GEN_ICMP==1: Generate checksums in software for outgoing ICMP packets. + */ +#define CHECKSUM_GEN_ICMP 1 + +/** + * CHECKSUM_GEN_ICMP6==1: Generate checksums in software for outgoing ICMP6 packets. + */ +#define CHECKSUM_GEN_ICMP6 1 + +/** + * CHECKSUM_CHECK_IP==1: Check checksums in software for incoming IP packets. + */ +#define CHECKSUM_CHECK_IP 1 + +/** + * CHECKSUM_CHECK_UDP==1: Check checksums in software for incoming UDP packets. + */ +#define CHECKSUM_CHECK_UDP 1 + +/** + * CHECKSUM_CHECK_TCP==1: Check checksums in software for incoming TCP packets. + */ +#define CHECKSUM_CHECK_TCP 1 + +/** + * CHECKSUM_CHECK_ICMP==1: Check checksums in software for incoming ICMP packets. + */ +#define CHECKSUM_CHECK_ICMP 1 + +/** + * CHECKSUM_CHECK_ICMP6==1: Check checksums in software for incoming ICMPv6 packets + */ +#define CHECKSUM_CHECK_ICMP6 1 + +/** + * LWIP_CHECKSUM_ON_COPY==1: Calculate checksum when copying data from + * application buffers to pbufs. + */ +#define LWIP_CHECKSUM_ON_COPY 0 +/** + * @} + */ + +/* + --------------------------------------- + ---------- IPv6 options --------------- + --------------------------------------- +*/ +/** + * @defgroup lwip_opts_ipv6 IPv6 + * @ingroup lwip_opts + * @{ + */ +/** + * LWIP_IPV6==1: Enable IPv6 + */ +#define LWIP_IPV6 0 + +/** + * LWIP_IPV6_NUM_ADDRESSES: Number of IPv6 addresses per netif. + */ +#define LWIP_IPV6_NUM_ADDRESSES 3 + +/** + * LWIP_IPV6_FORWARD==1: Forward IPv6 packets across netifs + */ +#define LWIP_IPV6_FORWARD 0 + +/** + * LWIP_IPV6_FRAG==1: Fragment outgoing IPv6 packets that are too big. + */ +#define LWIP_IPV6_FRAG 0 + +/** + * LWIP_IPV6_REASS==1: reassemble incoming IPv6 packets that fragmented + */ +#define LWIP_IPV6_REASS (LWIP_IPV6) + +/** + * LWIP_IPV6_SEND_ROUTER_SOLICIT==1: Send router solicitation messages during + * network startup. + */ +#define LWIP_IPV6_SEND_ROUTER_SOLICIT 1 + +/** + * LWIP_IPV6_AUTOCONFIG==1: Enable stateless address autoconfiguration as per RFC 4862. + */ +#define LWIP_IPV6_AUTOCONFIG (LWIP_IPV6) + +/** + * LWIP_IPV6_DUP_DETECT_ATTEMPTS=[0..7]: Number of duplicate address detection attempts. + */ +#define LWIP_IPV6_DUP_DETECT_ATTEMPTS 1 +/** + * @} + */ + +/** + * @defgroup lwip_opts_icmp6 ICMP6 + * @ingroup lwip_opts_ipv6 + * @{ + */ +/** + * LWIP_ICMP6==1: Enable ICMPv6 (mandatory per RFC) + */ +#define LWIP_ICMP6 (LWIP_IPV6) + +/** + * LWIP_ICMP6_DATASIZE: bytes from original packet to send back in + * ICMPv6 error messages. + */ +#define LWIP_ICMP6_DATASIZE 8 + +/** + * LWIP_ICMP6_HL: default hop limit for ICMPv6 messages + */ +#define LWIP_ICMP6_HL 255 +/** + * @} + */ + +/** + * @defgroup lwip_opts_mld6 Multicast listener discovery + * @ingroup lwip_opts_ipv6 + * @{ + */ +/** + * LWIP_IPV6_MLD==1: Enable multicast listener discovery protocol. + * If LWIP_IPV6 is enabled but this setting is disabled, the MAC layer must + * indiscriminately pass all inbound IPv6 multicast traffic to lwIP. + */ +#define LWIP_IPV6_MLD (LWIP_IPV6) + +/** + * MEMP_NUM_MLD6_GROUP: Max number of IPv6 multicast groups that can be joined. + * There must be enough groups so that each netif can join the solicited-node + * multicast group for each of its local addresses, plus one for MDNS if + * applicable, plus any number of groups to be joined on UDP sockets. + */ +#define MEMP_NUM_MLD6_GROUP 4 +/** + * @} + */ + +/** + * @defgroup lwip_opts_nd6 Neighbor discovery + * @ingroup lwip_opts_ipv6 + * @{ + */ +/** + * LWIP_ND6_QUEUEING==1: queue outgoing IPv6 packets while MAC address + * is being resolved. + */ +#define LWIP_ND6_QUEUEING (LWIP_IPV6) + +/** + * MEMP_NUM_ND6_QUEUE: Max number of IPv6 packets to queue during MAC resolution. + */ +#define MEMP_NUM_ND6_QUEUE 20 + +/** + * LWIP_ND6_NUM_NEIGHBORS: Number of entries in IPv6 neighbor cache + */ +#define LWIP_ND6_NUM_NEIGHBORS 10 + +/** + * LWIP_ND6_NUM_DESTINATIONS: number of entries in IPv6 destination cache + */ +#define LWIP_ND6_NUM_DESTINATIONS 10 + +/** + * LWIP_ND6_NUM_PREFIXES: number of entries in IPv6 on-link prefixes cache + */ +#define LWIP_ND6_NUM_PREFIXES 5 + +/** + * LWIP_ND6_NUM_ROUTERS: number of entries in IPv6 default router cache + */ +#define LWIP_ND6_NUM_ROUTERS 3 + +/** + * LWIP_ND6_MAX_MULTICAST_SOLICIT: max number of multicast solicit messages to send + * (neighbor solicit and router solicit) + */ +#define LWIP_ND6_MAX_MULTICAST_SOLICIT 3 + +/** + * LWIP_ND6_MAX_UNICAST_SOLICIT: max number of unicast neighbor solicitation messages + * to send during neighbor reachability detection. + */ +#define LWIP_ND6_MAX_UNICAST_SOLICIT 3 + +/** + * Unused: See ND RFC (time in milliseconds). + */ +#define LWIP_ND6_MAX_ANYCAST_DELAY_TIME 1000 + +/** + * Unused: See ND RFC + */ +#define LWIP_ND6_MAX_NEIGHBOR_ADVERTISEMENT 3 + +/** + * LWIP_ND6_REACHABLE_TIME: default neighbor reachable time (in milliseconds). + * May be updated by router advertisement messages. + */ +#define LWIP_ND6_REACHABLE_TIME 30000 + +/** + * LWIP_ND6_RETRANS_TIMER: default retransmission timer for solicitation messages + */ +#define LWIP_ND6_RETRANS_TIMER 1000 + +/** + * LWIP_ND6_DELAY_FIRST_PROBE_TIME: Delay before first unicast neighbor solicitation + * message is sent, during neighbor reachability detection. + */ +#define LWIP_ND6_DELAY_FIRST_PROBE_TIME 5000 + +/** + * LWIP_ND6_ALLOW_RA_UPDATES==1: Allow Router Advertisement messages to update + * Reachable time and retransmission timers, and netif MTU. + */ +#define LWIP_ND6_ALLOW_RA_UPDATES 1 + +/** + * LWIP_ND6_TCP_REACHABILITY_HINTS==1: Allow TCP to provide Neighbor Discovery + * with reachability hints for connected destinations. This helps avoid sending + * unicast neighbor solicitation messages. + */ +#define LWIP_ND6_TCP_REACHABILITY_HINTS 1 + +/** + * LWIP_ND6_RDNSS_MAX_DNS_SERVERS > 0: Use IPv6 Router Advertisement Recursive + * DNS Server Option (as per RFC 6106) to copy a defined maximum number of DNS + * servers to the DNS module. + */ +#define LWIP_ND6_RDNSS_MAX_DNS_SERVERS 0 +/** + * @} + */ + +/** + * LWIP_IPV6_DHCP6==1: enable DHCPv6 stateful address autoconfiguration. + */ +#define LWIP_IPV6_DHCP6 0 // ??? + +/* + --------------------------------------- + ---------- Hook options --------------- + --------------------------------------- +*/ + +/** + * @defgroup lwip_opts_hooks Hooks + * @ingroup lwip_opts_infrastructure + * Hooks are undefined by default, define them to a function if you need them. + * @{ + */ + +/** + * LWIP_HOOK_FILENAME: Custom filename to #include in files that provide hooks. + * Declare your hook function prototypes in there, you may also #include all headers + * providing data types that are need in this file. + */ +//#define LWIP_HOOK_FILENAME "path/to/my/lwip_hooks.h" + +/** + * LWIP_HOOK_TCP_ISN: + * Hook for generation of the Initial Sequence Number (ISN) for a new TCP + * connection. The default lwIP ISN generation algorithm is very basic and may + * allow for TCP spoofing attacks. This hook provides the means to implement + * the standardized ISN generation algorithm from RFC 6528 (see contrib/adons/tcp_isn), + * or any other desired algorithm as a replacement. + * Called from tcp_connect() and tcp_listen_input() when an ISN is needed for + * a new TCP connection, if TCP support (@ref LWIP_TCP) is enabled.\n + * Signature: u32_t my_hook_tcp_isn(const ip_addr_t* local_ip, u16_t local_port, const ip_addr_t* remote_ip, u16_t remote_port); + * - it may be necessary to use "struct ip_addr" (ip4_addr, ip6_addr) instead of "ip_addr_t" in function declarations\n + * Arguments: + * - local_ip: pointer to the local IP address of the connection + * - local_port: local port number of the connection (host-byte order) + * - remote_ip: pointer to the remote IP address of the connection + * - remote_port: remote port number of the connection (host-byte order)\n + * Return value: + * - the 32-bit Initial Sequence Number to use for the new TCP connection. + */ +//#define LWIP_HOOK_TCP_ISN(local_ip, local_port, remote_ip, remote_port) + +/** + * LWIP_HOOK_IP4_INPUT(pbuf, input_netif): + * - called from ip_input() (IPv4) + * - pbuf: received struct pbuf passed to ip_input() + * - input_netif: struct netif on which the packet has been received + * Return values: + * - 0: Hook has not consumed the packet, packet is processed as normal + * - != 0: Hook has consumed the packet. + * If the hook consumed the packet, 'pbuf' is in the responsibility of the hook + * (i.e. free it when done). + */ +//#define LWIP_HOOK_IP4_INPUT(pbuf, input_netif) + +/** + * LWIP_HOOK_IP4_ROUTE(dest): + * - called from ip_route() (IPv4) + * - dest: destination IPv4 address + * Returns the destination netif or NULL if no destination netif is found. In + * that case, ip_route() continues as normal. + */ +//#define LWIP_HOOK_IP4_ROUTE() + +/** + * LWIP_HOOK_IP4_ROUTE_SRC(dest, src): + * - source-based routing for IPv4 (see LWIP_HOOK_IP4_ROUTE(), src may be NULL) + */ +//#define LWIP_HOOK_IP4_ROUTE_SRC(dest, src) + +/** + * LWIP_HOOK_ETHARP_GET_GW(netif, dest): + * - called from etharp_output() (IPv4) + * - netif: the netif used for sending + * - dest: the destination IPv4 address + * Returns the IPv4 address of the gateway to handle the specified destination + * IPv4 address. If NULL is returned, the netif's default gateway is used. + * The returned address MUST be directly reachable on the specified netif! + * This function is meant to implement advanced IPv4 routing together with + * LWIP_HOOK_IP4_ROUTE(). The actual routing/gateway table implementation is + * not part of lwIP but can e.g. be hidden in the netif's state argument. +*/ +//#define LWIP_HOOK_ETHARP_GET_GW(netif, dest) + +/** + * LWIP_HOOK_IP6_INPUT(pbuf, input_netif): + * - called from ip6_input() (IPv6) + * - pbuf: received struct pbuf passed to ip6_input() + * - input_netif: struct netif on which the packet has been received + * Return values: + * - 0: Hook has not consumed the packet, packet is processed as normal + * - != 0: Hook has consumed the packet. + * If the hook consumed the packet, 'pbuf' is in the responsibility of the hook + * (i.e. free it when done). + */ +//#define LWIP_HOOK_IP6_INPUT(pbuf, input_netif) + +/** + * LWIP_HOOK_IP6_ROUTE(src, dest): + * - called from ip6_route() (IPv6) + * - src: sourc IPv6 address + * - dest: destination IPv6 address + * Returns the destination netif or NULL if no destination netif is found. In + * that case, ip6_route() continues as normal. + */ +//#define LWIP_HOOK_IP6_ROUTE(src, dest) + +/** + * LWIP_HOOK_ND6_GET_GW(netif, dest): + * - called from nd6_get_next_hop_entry() (IPv6) + * - netif: the netif used for sending + * - dest: the destination IPv6 address + * Returns the IPv6 address of the next hop to handle the specified destination + * IPv6 address. If NULL is returned, a NDP-discovered router is used instead. + * The returned address MUST be directly reachable on the specified netif! + * This function is meant to implement advanced IPv6 routing together with + * LWIP_HOOK_IP6_ROUTE(). The actual routing/gateway table implementation is + * not part of lwIP but can e.g. be hidden in the netif's state argument. +*/ +//#define LWIP_HOOK_ND6_GET_GW(netif, dest) + +/** + * LWIP_HOOK_VLAN_CHECK(netif, eth_hdr, vlan_hdr): + * - called from ethernet_input() if VLAN support is enabled + * - netif: struct netif on which the packet has been received + * - eth_hdr: struct eth_hdr of the packet + * - vlan_hdr: struct eth_vlan_hdr of the packet + * Return values: + * - 0: Packet must be dropped. + * - != 0: Packet must be accepted. + */ +//#define LWIP_HOOK_VLAN_CHECK(netif, eth_hdr, vlan_hdr) + +/** + * LWIP_HOOK_VLAN_SET: + * Hook can be used to set prio_vid field of vlan_hdr. If you need to store data + * on per-netif basis to implement this callback, see @ref netif_cd. + * Called from ethernet_output() if VLAN support (@ref ETHARP_SUPPORT_VLAN) is enabled.\n + * Signature: s32_t my_hook_vlan_set(struct netif* netif, struct pbuf* pbuf, const struct eth_addr* src, const struct eth_addr* dst, u16_t eth_type);\n + * Arguments: + * - netif: struct netif that the packet will be sent through + * - p: struct pbuf packet to be sent + * - src: source eth address + * - dst: destination eth address + * - eth_type: ethernet type to packet to be sent\n + * + * + * Return values: + * - <0: Packet shall not contain VLAN header. + * - 0 <= return value <= 0xFFFF: Packet shall contain VLAN header. Return value is prio_vid in host byte order. + */ +//#define LWIP_HOOK_VLAN_SET(netif, p, src, dst, eth_type) + +/** + * LWIP_HOOK_MEMP_AVAILABLE(memp_t_type): + * - called from memp_free() when a memp pool was empty and an item is now available + */ +//#define LWIP_HOOK_MEMP_AVAILABLE(memp_t_type) + +/** + * LWIP_HOOK_UNKNOWN_ETH_PROTOCOL(pbuf, netif): + * Called from ethernet_input() when an unknown eth type is encountered. + * Return ERR_OK if packet is accepted, any error code otherwise. + * Payload points to ethernet header! + */ +//#define LWIP_HOOK_UNKNOWN_ETH_PROTOCOL(pbuf, netif) +/** + * @} + */ + +/* + --------------------------------------- + ---------- Debugging options ---------- + --------------------------------------- +*/ +//#define LWIP_DEBUG +/** + * @defgroup lwip_opts_debugmsg Debug messages + * @ingroup lwip_opts_debug + * @{ + */ +/** + * LWIP_DBG_MIN_LEVEL: After masking, the value of the debug is + * compared against this value. If it is smaller, then debugging + * messages are written. + * @see debugging_levels + */ +#define LWIP_DBG_MIN_LEVEL LWIP_DBG_LEVEL_ALL + +/** + * LWIP_DBG_TYPES_ON: A mask that can be used to globally enable/disable + * debug messages of certain types. + * @see debugging_levels + */ +#define LWIP_DBG_TYPES_ON LWIP_DBG_ON + +/** + * ETHARP_DEBUG: Enable debugging in etharp.c. + */ +#define ETHARP_DEBUG LWIP_DBG_OFF + +/** + * NETIF_DEBUG: Enable debugging in netif.c. + */ +#define NETIF_DEBUG LWIP_DBG_OFF + +/** + * PBUF_DEBUG: Enable debugging in pbuf.c. + */ +#define PBUF_DEBUG LWIP_DBG_OFF + +/** + * API_LIB_DEBUG: Enable debugging in api_lib.c. + */ +#define API_LIB_DEBUG LWIP_DBG_OFF + +/** + * API_MSG_DEBUG: Enable debugging in api_msg.c. + */ +#define API_MSG_DEBUG LWIP_DBG_OFF + +/** + * SOCKETS_DEBUG: Enable debugging in sockets.c. + */ +#define SOCKETS_DEBUG LWIP_DBG_OFF + +/** + * ICMP_DEBUG: Enable debugging in icmp.c. + */ +#define ICMP_DEBUG LWIP_DBG_OFF + +/** + * IGMP_DEBUG: Enable debugging in igmp.c. + */ +#define IGMP_DEBUG LWIP_DBG_OFF + +/** + * INET_DEBUG: Enable debugging in inet.c. + */ +#define INET_DEBUG LWIP_DBG_OFF + +/** + * IP_DEBUG: Enable debugging for IP. + */ +#define IP_DEBUG LWIP_DBG_OFF + +/** + * IP_REASS_DEBUG: Enable debugging in ip_frag.c for both frag & reass. + */ +#define IP_REASS_DEBUG LWIP_DBG_OFF + +/** + * RAW_DEBUG: Enable debugging in raw.c. + */ +#define RAW_DEBUG LWIP_DBG_OFF + +/** + * MEM_DEBUG: Enable debugging in mem.c. + */ +#define MEM_DEBUG LWIP_DBG_OFF + +/** + * MEMP_DEBUG: Enable debugging in memp.c. + */ +#define MEMP_DEBUG LWIP_DBG_OFF + +/** + * SYS_DEBUG: Enable debugging in sys.c. + */ +#define SYS_DEBUG LWIP_DBG_OFF + +/** + * TIMERS_DEBUG: Enable debugging in timers.c. + */ +#define TIMERS_DEBUG LWIP_DBG_OFF + +/** + * TCP_DEBUG: Enable debugging for TCP. + */ +#define TCP_DEBUG LWIP_DBG_OFF + +/** + * TCP_INPUT_DEBUG: Enable debugging in tcp_in.c for incoming debug. + */ +#define TCP_INPUT_DEBUG LWIP_DBG_OFF + +/** + * TCP_FR_DEBUG: Enable debugging in tcp_in.c for fast retransmit. + */ +#define TCP_FR_DEBUG LWIP_DBG_OFF + +/** + * TCP_RTO_DEBUG: Enable debugging in TCP for retransmit + * timeout. + */ +#define TCP_RTO_DEBUG LWIP_DBG_OFF + +/** + * TCP_CWND_DEBUG: Enable debugging for TCP congestion window. + */ +#define TCP_CWND_DEBUG LWIP_DBG_OFF + +/** + * TCP_WND_DEBUG: Enable debugging in tcp_in.c for window updating. + */ +#define TCP_WND_DEBUG LWIP_DBG_OFF + +/** + * TCP_OUTPUT_DEBUG: Enable debugging in tcp_out.c output functions. + */ +#define TCP_OUTPUT_DEBUG LWIP_DBG_OFF + +/** + * TCP_RST_DEBUG: Enable debugging for TCP with the RST message. + */ +#define TCP_RST_DEBUG LWIP_DBG_OFF + +/** + * TCP_QLEN_DEBUG: Enable debugging for TCP queue lengths. + */ +#define TCP_QLEN_DEBUG LWIP_DBG_OFF + +/** + * UDP_DEBUG: Enable debugging in UDP. + */ +#define UDP_DEBUG LWIP_DBG_OFF + +/** + * TCPIP_DEBUG: Enable debugging in tcpip.c. + */ +#define TCPIP_DEBUG LWIP_DBG_OFF + +/** + * SLIP_DEBUG: Enable debugging in slipif.c. + */ +#define SLIP_DEBUG LWIP_DBG_OFF + +/** + * DHCP_DEBUG: Enable debugging in dhcp.c. + */ +#define DHCP_DEBUG LWIP_DBG_OFF + +/** + * AUTOIP_DEBUG: Enable debugging in autoip.c. + */ +#define AUTOIP_DEBUG LWIP_DBG_OFF + +/** + * DNS_DEBUG: Enable debugging for DNS. + */ +#define DNS_DEBUG LWIP_DBG_OFF + +/** + * IP6_DEBUG: Enable debugging for IPv6. + */ +#define IP6_DEBUG LWIP_DBG_OFF + +#if LWIP_XR_IMPL +/** + * LWIP_XR_DEBUG: Enable debugging for XRadio. + */ +#define LWIP_XR_DEBUG LWIP_DBG_OFF +#endif /* LWIP_XR_IMPL */ +/** + * @} + */ + +/* + -------------------------------------------------- + ---------- Performance tracking options ---------- + -------------------------------------------------- +*/ +/** + * @defgroup lwip_opts_perf Performance + * @ingroup lwip_opts_debug + * @{ + */ +/** + * LWIP_PERF: Enable performance testing for lwIP + * (if enabled, arch/perf.h is included) + */ +#define LWIP_PERF 0 +/** + * @} + */ + +#endif /* LWIP_HDR_LWIPOPTS_H */ diff --git a/platform/mcu/xr871/include/net/lwip/lwippools.h b/platform/mcu/xr871/include/net/lwip/lwippools.h new file mode 100644 index 0000000000..8c30970e32 --- /dev/null +++ b/platform/mcu/xr871/include/net/lwip/lwippools.h @@ -0,0 +1,12 @@ +/** + * Define pools used by mbuf. + * + * LWIP_MEMPOOL(pool_name, number_elements, element_size, pool_description) + * creates a pool name MEMP_pool_name. description is used in stats.c + */ + +#define SIZEOF_MBUF 48 /* MUST be equal to MSIZE defined in mbuf.h */ +#define MEMP_NUM_MBUF 64 + +//LWIP_MEMPOOL(MBUF, MEMP_NUM_MBUF, sizeof(struct mbuf), "MBUF") +LWIP_MEMPOOL(MBUF, MEMP_NUM_MBUF, SIZEOF_MBUF, "MBUF") diff --git a/platform/mcu/xr871/include/net/udhcp/usr_dhcpd.h b/platform/mcu/xr871/include/net/udhcp/usr_dhcpd.h new file mode 100644 index 0000000000..c5e9542415 --- /dev/null +++ b/platform/mcu/xr871/include/net/udhcp/usr_dhcpd.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __USR_DHCPD_H_H +#define __USR_DHCPD_H_H + +#ifdef __cplusplus +extern "C" { +#endif + +struct dhcp_server_info { + unsigned int addr_start; + unsigned int addr_end; + unsigned int lease_time; + unsigned int max_leases; +}; + +void dhcp_server_start(const struct dhcp_server_info *arg); +void dhcp_server_stop(void); + +#ifdef __cplusplus +} +#endif + +#endif /* __USR_DHCPD_H_H */ diff --git a/platform/mcu/xr871/include/net/wlan/ethernetif.h b/platform/mcu/xr871/include/net/wlan/ethernetif.h new file mode 100644 index 0000000000..aaf6b4dbc4 --- /dev/null +++ b/platform/mcu/xr871/include/net/wlan/ethernetif.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _NET_WLAN_ETHERNETIF_H_ +#define _NET_WLAN_ETHERNETIF_H_ + +#include "lwip/netif.h" + +#ifdef __cplusplus +extern "C" { +#endif + +enum wlan_mode { + WLAN_MODE_STA = 0, /* Infrastructure station */ + WLAN_MODE_HOSTAP, /* Software Access Point */ + WLAN_MODE_MONITOR, /* Monitor mode */ + WLAN_MODE_NUM, + WLAN_MODE_INVALID = WLAN_MODE_NUM +}; + +struct netif *ethernetif_create(enum wlan_mode mode); +void ethernetif_delete(struct netif *nif); +err_t ethernetif_input(struct netif *nif, struct pbuf *p); +#if (LWIP_MBUF_SUPPORT == 0) +err_t ethernetif_raw_input(struct netif *nif, uint8_t *data, u16_t len); +#endif +enum wlan_mode ethernetif_get_mode(struct netif *nif); + +#ifdef __cplusplus +} +#endif + +#endif /* _NET_WLAN_ETHERNETIF_H_ */ diff --git a/platform/mcu/xr871/include/net/wlan/wlan.h b/platform/mcu/xr871/include/net/wlan/wlan.h new file mode 100644 index 0000000000..10edcccc7f --- /dev/null +++ b/platform/mcu/xr871/include/net/wlan/wlan.h @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _NET_WLAN_WLAN_H_ +#define _NET_WLAN_WLAN_H_ + +#if (defined(__CONFIG_ARCH_DUAL_CORE) && defined(__CONFIG_ARCH_APP_CORE)) + +#include +#include "lwip/netif.h" +#include "sys/ducc/ducc_net.h" +#include "sys/ducc/ducc_app.h" +#include "net/wlan/wlan_defs.h" +#include "net/wlan/ethernetif.h" +#include "net/wlan/wlan_smart_config.h" +#include "net/wlan/wlan_airkiss.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* wlan sys */ +int wlan_sys_init(enum wlan_mode mode, ducc_cb_func cb); +int wlan_sys_deinit(void); + +int wlan_start(struct netif *nif); +int wlan_stop(void); /* Note: make sure wlan is disconnect before calling wlan_stop() */ + +static __inline int wlan_attach(void) +{ + return ducc_app_ioctl(DUCC_APP_CMD_WLAN_ATTACH, NULL); +} + +static __inline int wlan_detach(void) +{ + return ducc_app_ioctl(DUCC_APP_CMD_WLAN_DETACH, NULL); +} + +static __inline struct netif *wlan_if_create(enum wlan_mode mode) +{ + return ethernetif_create(mode); +} + +static __inline int wlan_if_delete(struct netif *nif) +{ + ethernetif_delete(nif); + return 0; +} + +static __inline enum wlan_mode wlan_if_get_mode(struct netif *nif) +{ + return ethernetif_get_mode(nif); +} + +int wlan_set_mac_addr(uint8_t *mac_addr, int mac_len); +int wlan_set_ip_addr(struct netif *nif, uint8_t *ip_addr, int ip_len); +int wlan_set_appie(struct netif *nif, uint8_t type, uint8_t *ie, uint16_t ie_len); + +/* STA */ +int wlan_sta_set(uint8_t *ssid, uint8_t ssid_len, uint8_t *psk); +int wlan_sta_set_config(wlan_sta_config_t *config); +int wlan_sta_get_config(wlan_sta_config_t *config); + +int wlan_sta_enable(void); +int wlan_sta_disable(void); + +int wlan_sta_scan_once(void); +int wlan_sta_scan_result(wlan_sta_scan_results_t *results); +int wlan_sta_scan_interval(int sec); +int wlan_sta_bss_flush(int age); + +int wlan_sta_connect(void); +int wlan_sta_disconnect(void); + +int wlan_sta_state(wlan_sta_states_t *state); +int wlan_sta_ap_info(wlan_sta_ap_t *ap); + +int wlan_sta_wps_pbc(void); +int wlan_sta_wps_pin_get(wlan_sta_wps_pin_t *wps); +int wlan_sta_wps_pin_set(wlan_sta_wps_pin_t *wps); + +/* softAP */ +int wlan_ap_set(uint8_t *ssid, uint8_t ssid_len, uint8_t *psk); +int wlan_ap_set_config(wlan_ap_config_t *config); +int wlan_ap_get_config(wlan_ap_config_t *config); + +int wlan_ap_enable(void); +int wlan_ap_reload(void); +int wlan_ap_disable(void); + +int wlan_ap_sta_num(int *num); +int wlan_ap_sta_info(wlan_ap_stas_t *stas); + +/* monitor */ +typedef void (*wlan_monitor_rx_cb)(uint8_t *data, uint32_t len, void *info); +int wlan_monitor_set_rx_cb(struct netif *nif, wlan_monitor_rx_cb cb); +int wlan_monitor_set_channel(struct netif *nif, int16_t channel); +void wlan_monitor_input(struct netif *nif, uint8_t *data, uint32_t len, void *info); + +#ifdef __cplusplus +} +#endif + +#endif /* (defined(__CONFIG_ARCH_DUAL_CORE) && defined(__CONFIG_ARCH_APP_CORE)) */ + +#endif /* _NET_WLAN_WLAN_H_ */ diff --git a/platform/mcu/xr871/include/net/wlan/wlan_airkiss.h b/platform/mcu/xr871/include/net/wlan/wlan_airkiss.h new file mode 100644 index 0000000000..f0565c97df --- /dev/null +++ b/platform/mcu/xr871/include/net/wlan/wlan_airkiss.h @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _NET_WLAN_WLAN_AIRKISS_H_ +#define _NET_WLAN_WLAN_AIRKISS_H_ + +#if (defined(__CONFIG_ARCH_DUAL_CORE) && defined(__CONFIG_ARCH_APP_CORE)) + +#include +#include "net/wlan/wlan_defs.h" +#include "lwip/netif.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + WLAN_AIRKISS_SUCCESS = 0, /* success */ + WLAN_AIRKISS_FAIL = -1, /* general error */ + WLAN_AIRKISS_TIMEOUT = -2, /* wait timeout */ + WLAN_AIRKISS_INVALID = -3, /* invalid argument */ +} wlan_airkiss_status_t; + +typedef struct wlan_airkiss_result { + uint8_t ssid[WLAN_SSID_MAX_LEN]; + uint8_t ssid_len; + uint8_t passphrase[WLAN_PASSPHRASE_MAX_LEN + 1]; /* ASCII string ending with '\0' */ + uint8_t random_num; +} wlan_airkiss_result_t; + +#define WLAN_AIRKISS_KEY_LEN 16 + +int wlan_airkiss_set_key(const char *key, uint32_t len); + +wlan_airkiss_status_t wlan_airkiss_start(struct netif *nif, uint32_t timeout_ms, + wlan_airkiss_result_t *result); +int wlan_airkiss_stop(void); + +int wlan_airkiss_ack_start(struct netif *nif, uint32_t random_num, uint32_t timeout_ms); +int wlan_airkiss_ack_stop(void); + +/* + * The wechat_public_id and devic_id should be global variables. + * In this mode, the driver will be send online data by cycle + */ +int wlan_airkiss_online_cycle_ack_start(char *app_id, char *drv_id, uint32_t period_ms); +int wlan_airkiss_online_cycle_ack_stop(void); + +/* + * The wechat_public_id and devic_id should be global variables. + * In this mode, the drivers will be listen to server's request and send ack for server, then the driver will + * be send online data by cycle + */ +int wlan_airkiss_online_dialog_mode_start(char *app_id, char *drv_id, uint32_t period_ms); +int wlan_airkiss_online_dialog_mode_stop(void); + +#ifdef __cplusplus +} +#endif + +#endif /* (defined(__CONFIG_ARCH_DUAL_CORE) && defined(__CONFIG_ARCH_APP_CORE)) */ + +#endif /* _NET_WLAN_WLAN_AIRKISS_H_ */ diff --git a/platform/mcu/xr871/include/net/wlan/wlan_defs.h b/platform/mcu/xr871/include/net/wlan/wlan_defs.h new file mode 100644 index 0000000000..406efffe82 --- /dev/null +++ b/platform/mcu/xr871/include/net/wlan/wlan_defs.h @@ -0,0 +1,488 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _NET_WLAN_WLAN_DEFS_H_ +#define _NET_WLAN_WLAN_DEFS_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef WPA_BIT +#define WPA_BIT(x) (1 << (x)) +#endif + +/** + * @brief WPA cipher definition + */ +#define WPA_CIPHER_NONE WPA_BIT(0) +#define WPA_CIPHER_WEP40 WPA_BIT(1) +#define WPA_CIPHER_WEP104 WPA_BIT(2) +#define WPA_CIPHER_TKIP WPA_BIT(3) +#define WPA_CIPHER_CCMP WPA_BIT(4) +#define WPA_CIPHER_AES_128_CMAC WPA_BIT(5) +#define WPA_CIPHER_GCMP WPA_BIT(6) +#define WPA_CIPHER_SMS4 WPA_BIT(7) +#define WPA_CIPHER_GCMP_256 WPA_BIT(8) +#define WPA_CIPHER_CCMP_256 WPA_BIT(9) +#define WPA_CIPHER_BIP_GMAC_128 WPA_BIT(11) +#define WPA_CIPHER_BIP_GMAC_256 WPA_BIT(12) +#define WPA_CIPHER_BIP_CMAC_256 WPA_BIT(13) +#define WPA_CIPHER_GTK_NOT_USED WPA_BIT(14) +#define WPA_CIPHER_MASK 0x7fffUL + +/** + * @brief WPA key management definition + */ +#define WPA_KEY_MGMT_IEEE8021X WPA_BIT(0) +#define WPA_KEY_MGMT_PSK WPA_BIT(1) +#define WPA_KEY_MGMT_NONE WPA_BIT(2) +#define WPA_KEY_MGMT_IEEE8021X_NO_WPA WPA_BIT(3) +#define WPA_KEY_MGMT_WPA_NONE WPA_BIT(4) +#define WPA_KEY_MGMT_FT_IEEE8021X WPA_BIT(5) +#define WPA_KEY_MGMT_FT_PSK WPA_BIT(6) +#define WPA_KEY_MGMT_IEEE8021X_SHA256 WPA_BIT(7) +#define WPA_KEY_MGMT_PSK_SHA256 WPA_BIT(8) +#define WPA_KEY_MGMT_WPS WPA_BIT(9) +#define WPA_KEY_MGMT_SAE WPA_BIT(10) +#define WPA_KEY_MGMT_FT_SAE WPA_BIT(11) +#define WPA_KEY_MGMT_WAPI_PSK WPA_BIT(12) +#define WPA_KEY_MGMT_WAPI_CERT WPA_BIT(13) +#define WPA_KEY_MGMT_CCKM WPA_BIT(14) +#define WPA_KEY_MGMT_OSEN WPA_BIT(15) +#define WPA_KEY_MGMT_IEEE8021X_SUITE_B WPA_BIT(16) +#define WPA_KEY_MGMT_IEEE8021X_SUITE_B_192 WPA_BIT(17) +#define WPA_KEY_MGMT_MASK 0x3ffffUL + +/** + * @brief WPA protocol definition + */ +#define WPA_PROTO_WPA WPA_BIT(0) +#define WPA_PROTO_RSN WPA_BIT(1) +#define WPA_PROTO_WAPI WPA_BIT(2) +#define WPA_PROTO_OSEN WPA_BIT(3) +#define WPA_PROTO_MASK 0xfUL + +/** + * @brief WPA authentication algorithm definition + */ +#define WPA_AUTH_ALG_OPEN WPA_BIT(0) +#define WPA_AUTH_ALG_SHARED WPA_BIT(1) +#define WPA_AUTH_ALG_LEAP WPA_BIT(2) +#define WPA_AUTH_ALG_FT WPA_BIT(3) +#define WPA_AUTH_ALG_SAE WPA_BIT(4) +#define WPA_AUTH_ALG_MASK 0x1fUL + +/** + * @brief WPA scan result flags definition + */ +#define WPA_FLAGS_WPA WPA_BIT(0) +#define WPA_FLAGS_WPA2 WPA_BIT(1) +#define WPA_FLAGS_WEP WPA_BIT(2) +#define WPA_FLAGS_WPS_PBC WPA_BIT(3) +#define WPA_FLAGS_WPS_AUTH WPA_BIT(4) +#define WPA_FLAGS_WPS_PIN WPA_BIT(5) +#define WPA_FLAGS_WPS WPA_BIT(6) +#define WPA_FLAGS_IBSS WPA_BIT(7) +#define WPA_FLAGS_ESS WPA_BIT(8) + +/** + * @brief WLAN SSID and passphrase definition + */ +#define WLAN_SSID_MAX_LEN 32 +#define WLAN_PASSPHRASE_MIN_LEN 8 +#define WLAN_PASSPHRASE_MAX_LEN 63 + +/** + * @brief IEEE 802.11 frame type definition + */ +#define IEEE80211_FC_TYPE_SHIFT 2 +#define IEEE80211_FC_TYPE_MASK (0x3 << IEEE80211_FC_TYPE_SHIFT) +#define IEEE80211_FC_TYPE_MGMT (0x0 << IEEE80211_FC_TYPE_SHIFT) +#define IEEE80211_FC_TYPE_CTRL (0x1 << IEEE80211_FC_TYPE_SHIFT) +#define IEEE80211_FC_TYPE_DATA (0x2 << IEEE80211_FC_TYPE_SHIFT) + +/** + * @brief IEEE 802.11 management frame subtype definition + */ +#define IEEE80211_FC_STYPE_SHIFT 4 +#define IEEE80211_FC_STYPE_MASK (0xf << IEEE80211_FC_STYPE_SHIFT) +#define IEEE80211_FC_STYPE_ASSOC_REQ (0x0 << IEEE80211_FC_STYPE_SHIFT) +#define IEEE80211_FC_STYPE_ASSOC_RESP (0x1 << IEEE80211_FC_STYPE_SHIFT) +#define IEEE80211_FC_STYPE_REASSOC_REQ (0x2 << IEEE80211_FC_STYPE_SHIFT) +#define IEEE80211_FC_STYPE_REASSOC_RESP (0x3 << IEEE80211_FC_STYPE_SHIFT) +#define IEEE80211_FC_STYPE_PROBE_REQ (0x4 << IEEE80211_FC_STYPE_SHIFT) +#define IEEE80211_FC_STYPE_PROBE_RESP (0x5 << IEEE80211_FC_STYPE_SHIFT) +#define IEEE80211_FC_STYPE_BEACON (0x8 << IEEE80211_FC_STYPE_SHIFT) +#define IEEE80211_FC_STYPE_ATIM (0x9 << IEEE80211_FC_STYPE_SHIFT) +#define IEEE80211_FC_STYPE_DISASSOC (0xa << IEEE80211_FC_STYPE_SHIFT) +#define IEEE80211_FC_STYPE_AUTH (0xb << IEEE80211_FC_STYPE_SHIFT) +#define IEEE80211_FC_STYPE_DEAUTH (0xc << IEEE80211_FC_STYPE_SHIFT) +#define IEEE80211_FC_STYPE_ACTION (0xd << IEEE80211_FC_STYPE_SHIFT) + +/** + * @brief Wlan event definition + */ +typedef enum wlan_event { + WLAN_EVENT_CONNECTED, + WLAN_EVENT_DISCONNECTED, + WLAN_EVENT_SCAN_SUCCESS, + WLAN_EVENT_SCAN_FAILED, + WLAN_EVENT_4WAY_HANDSHAKE_FAILED, + WLAN_EVENT_CONNECT_FAILED, + + WLAN_EVENT_DEV_HANG, +} wlan_event_t; + +/** + * @brief Wlan ssid definition + */ +typedef struct wlan_ssid { + uint8_t ssid[WLAN_SSID_MAX_LEN]; + uint8_t ssid_len; +} wlan_ssid_t; + +/** + * @brief Wlan station configuration field definition + */ +typedef enum wlan_sta_field { + WLAN_STA_FIELD_SSID = 0, + WLAN_STA_FIELD_PSK, + WLAN_STA_FIELD_WEP_KEY0, + WLAN_STA_FIELD_WEP_KEY1, + WLAN_STA_FIELD_WEP_KEY2, + WLAN_STA_FIELD_WEP_KEY3, + WLAN_STA_FIELD_WEP_KEY_INDEX, + WLAN_STA_FIELD_KEY_MGMT, + WLAN_STA_FIELD_PAIRWISE_CIPHER, + WLAN_STA_FIELD_GROUP_CIPHER, + WLAN_STA_FIELD_PROTO, + WLAN_STA_FIELD_AUTH_ALG, + WLAN_STA_FIELD_WPA_PTK_REKEY, + WLAN_STA_FIELD_SCAN_SSID, + + WLAN_STA_FIELD_NUM, +} wlan_sta_field_t; + +/** + * @brief Wlan station configuration definition + */ +typedef struct wlan_sta_config { + wlan_sta_field_t field; + + union { + /** + * Network name + */ + wlan_ssid_t ssid; + + /** + * WPA preshared key in one of the optional formats: + * - an ASCII string of passphrase, length is [8, 63] + * - a hex string of PSK (two characters per octet of PSK), length is 64 + */ + uint8_t psk[65]; + + /** + * WEP key in one of the optional formats: + * - an ASCII string with double quotation, length is [5, 13] + 2 + * - a hex string (two characters per octet of PSK), length is [10, 26] + */ + uint8_t wep_key[27]; + + /** + * Default key index for TX frames using WEP + */ + int wep_tx_keyidx; + + /** + * Bitfield of allowed key management protocols + * + * WPA_KEY_MGMT_* + */ + int key_mgmt; + + /** + * Bitfield of allowed pairwise ciphers + * + * WPA_CIPHER_* + */ + int pairwise_cipher; + + /** + * Bitfield of allowed group ciphers + * + * WPA_CIPHER_* + */ + int group_cipher; + + /** + * Bitfield of allowed protocols + * + * WPA_PROTO_* + */ + int proto; + + /** + * Bitfield of allowed authentication algorithms + * + * WPA_AUTH_ALG_* + */ + int auth_alg; + + /** + * Maximum lifetime for PTK in seconds + * + * This value can be used to enforce rekeying of PTK to + * mitigate some attacks against TKIP deficiencies. + */ + int wpa_ptk_rekey; + + /** + * Scan this SSID with Probe Requests + * + * scan_ssid can be used to scan for APs using hidden SSIDs. + */ + int scan_ssid; + } u; +} wlan_sta_config_t; + +/** + * @brief Wlan station connection state definition + */ +typedef enum wlan_sta_states { + WLAN_STA_STATE_DISCONNECTED = 0, + WLAN_STA_STATE_CONNECTED = 1, +} wlan_sta_states_t; + +/** + * @brief Wlan AP information definition + */ +typedef struct wlan_sta_ap { + wlan_ssid_t ssid; + uint8_t bssid[6]; + uint8_t channel; + uint16_t beacon_int; + int freq; + int rssi; + int level; + int wpa_flags; + int wpa_cipher; + int wpa_key_mgmt; + int wpa2_cipher; + int wpa2_key_mgmt; +}wlan_sta_ap_t; + +/** + * @brief Wlan station scan results definition + */ +typedef struct wlan_sta_scan_results { + wlan_sta_ap_t *ap; + int size; + int num; +} wlan_sta_scan_results_t; + +/** + * @brief Wlan WPS pin definition + */ +typedef struct wlan_sta_wps_pin { + uint8_t pin[9]; +} wlan_sta_wps_pin_t; + +/** + * @brief Wlan AP configuration field definition + */ +typedef enum wlan_ap_field { + WLAN_AP_FIELD_SSID = 0, + WLAN_AP_FIELD_PSK, + WLAN_AP_FIELD_KEY_MGMT, + WLAN_AP_FIELD_WPA_CIPHER, + WLAN_AP_FIELD_RSN_CIPHER, + WLAN_AP_FIELD_PROTO, + WLAN_AP_FIELD_AUTH_ALG, + WLAN_AP_FIELD_GROUP_REKEY, + WLAN_AP_FIELD_STRICT_REKEY, + WLAN_AP_FIELD_GMK_REKEY, + WLAN_AP_FIELD_PTK_REKEY, + WLAN_AP_FIELD_HW_MODE, + WLAN_AP_FIELD_IEEE80211N, + WLAN_AP_FIELD_CHANNEL, + WLAN_AP_FIELD_BEACON_INT, + WLAN_AP_FIELD_DTIM, + WLAN_AP_FIELD_MAX_NUM_STA, + + WLAN_AP_FIELD_NUM, +} wlan_ap_field_t; + +/** + * @brief Wlan AP hardware mode definition + */ +typedef enum wlan_ap_hw_mode { + WLAN_AP_HW_MODE_IEEE80211B = 0, + WLAN_AP_HW_MODE_IEEE80211G, + WLAN_AP_HW_MODE_IEEE80211A, + WLAN_AP_HW_MODE_IEEE80211AD, + + WLAN_AP_HW_MODE_NUM, +} wlan_ap_hw_mode_t; + +/** + * @brief Wlan AP configuration definition + */ +typedef struct wlan_ap_config { + wlan_ap_field_t field; + + union { + /** + * Network name + */ + wlan_ssid_t ssid; + + /** + * WPA preshared key in one of the optional formats: + * - an ASCII string of passphrase, length is [8, 63] + * - a hex string of PSK (two characters per octet of PSK), length is 64 + */ + uint8_t psk[65]; + + /** + * Bitfield of allowed key management protocols + * + * WPA_KEY_MGMT_* + */ + int key_mgmt; + + /** + * Bitfield of allowed WPA pairwise ciphers + * + * WPA_CIPHER_* + */ + int wpa_cipher; + + /** + * Bitfield of allowed RSN pairwise ciphers + * + * WPA_CIPHER_* + */ + int rsn_cipher; + + /** + * Bitfield of allowed protocols + * + * WPA_PROTO_* + */ + int proto; + + /** + * Bitfield of allowed authentication algorithms + * + * WPA_AUTH_ALG_* + */ + int auth_alg; + + /** + * Maximum lifetime for GTK in seconds + */ + int group_rekey; + + /** + * Rekey GTK when any STA that possesses the current GTK is + * leaving the BSS + */ + int strict_rekey; + + /** + * Maximum lifetime for GMK in seconds + */ + int gmk_rekey; + + /** + * Maximum lifetime for PTK in seconds + */ + int ptk_rekey; + + /** + * Hardware mode + */ + wlan_ap_hw_mode_t hw_mode; + + /** + * IEEE802.11n mode + */ + int ieee80211n; + + /** + * RF channel + */ + uint8_t channel; + + /** + * MIB defines range as 1..65535, but very small values + * cause problems with the current implementation. + * Since it is unlikely that this small numbers are + * useful in real life scenarios, do not allow beacon + * period to be set below 15 TU. + */ + uint16_t beacon_int; + + /** + * Delivery traffic indication message + */ + int dtim; + + /** + * Maximum number of STAs in station table + */ + int max_num_sta; + } u; +} wlan_ap_config_t; + +/** + * @brief Wlan station information definition + */ +typedef struct wlan_ap_sta { + uint8_t addr[6]; +} wlan_ap_sta_t; + +/** + * @brief Wlan connected stations information definition + */ +typedef struct wlan_ap_stas { + wlan_ap_sta_t *sta; + int size; + int num; +} wlan_ap_stas_t; + +#ifdef __cplusplus +} +#endif + +#endif /* _NET_WLAN_WLAN_DEFS_H_ */ diff --git a/platform/mcu/xr871/include/net/wlan/wlan_smart_config.h b/platform/mcu/xr871/include/net/wlan/wlan_smart_config.h new file mode 100644 index 0000000000..e1f1332df9 --- /dev/null +++ b/platform/mcu/xr871/include/net/wlan/wlan_smart_config.h @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _NET_WLAN_WLAN_SMART_CONFIG_H_ +#define _NET_WLAN_WLAN_SMART_CONFIG_H_ + +#if (defined(__CONFIG_ARCH_DUAL_CORE) && defined(__CONFIG_ARCH_APP_CORE)) + +#include +#include "net/wlan/wlan_defs.h" +#include "lwip/netif.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + WLAN_SMART_CONFIG_SUCCESS = 0, /* success */ + WLAN_SMART_CONFIG_FAIL = -1, /* general error */ + WLAN_SMART_CONFIG_TIMEOUT = -2, /* wait timeout */ + WLAN_SMART_CONFIG_INVALID = -3, /* invalid argument */ +} wlan_smart_config_status_t; + +typedef struct wlan_smart_config_result { + uint8_t ssid[WLAN_SSID_MAX_LEN]; + uint8_t ssid_len; + uint8_t passphrase[WLAN_PASSPHRASE_MAX_LEN + 1]; /* ASCII string ending with '\0' */ + uint8_t random_num; +} wlan_smart_config_result_t; + +#define WLAN_SMART_CONFIG_KEY_LEN 16 + +int wlan_smart_config_set_key(const char *key, uint32_t len); + +wlan_smart_config_status_t wlan_smart_config_start(struct netif *nif, + uint32_t timeout_ms, + wlan_smart_config_result_t *result); +int wlan_smart_config_stop(void); + +int wlan_smart_config_ack_start(struct netif *nif, uint32_t random_num, uint32_t timeout_ms); +int wlan_smart_config_ack_stop(void); + +#ifdef __cplusplus +} +#endif + +#endif /* (defined(__CONFIG_ARCH_DUAL_CORE) && defined(__CONFIG_ARCH_APP_CORE)) */ + +#endif /* _NET_WLAN_WLAN_SMART_CONFIG_H_ */ diff --git a/platform/mcu/xr871/include/pm/pm.h b/platform/mcu/xr871/include/pm/pm.h new file mode 100644 index 0000000000..841d4df6b6 --- /dev/null +++ b/platform/mcu/xr871/include/pm/pm.h @@ -0,0 +1,226 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __XRADIO_PM_H +#define __XRADIO_PM_H + +#include +#include +#include "sys/list.h" + +#ifdef __CONFIG_BOOTLOADER +#else +#define CONFIG_PM +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Defined all supported low power state. + * @note PM_MODE_HIBERNATION is not used. + */ +enum suspend_state_t { + PM_MODE_ON = 0, + PM_MODE_SLEEP = 1, + PM_MODE_STANDBY = 2, + PM_MODE_HIBERNATION = 3, + PM_MODE_POWEROFF = 4, + PM_MODE_MAX = 5, +}; + +/** @brief Platform pm mode support. */ +#define PM_SUPPORT_SLEEP (1<Device will be registered. + * @retval 0 if success or other if failed. + */ +extern int pm_register_ops(struct soc_device *dev); + +/** + * @brief Unregister a set of system core operations. + * @param dev: + * @arg dev->Device will be unregistered. + * @retval 0 if success or other if failed. + */ +extern int pm_unregister_ops(struct soc_device *dev); + +/** + * @brief Set a magin to synchronize with net. + */ +extern void pm_set_sync_magic(void); + +/** + * @brief Set system to a lowpower mode. + * @param state: + * @arg state->The lowpower mode will enter. + * @retval 0 if success or other if failed. + */ +extern int pm_enter_mode(enum suspend_state_t state); + +/** + * @brief Initialize the PM-related part of a device object. + * @note not use printf for this fun is called very earlier. + * @retval 0 if success or other if failed. + */ +extern int pm_init(void); + +/** + * @brief Set suspend test level. + * @param level: + * @arg level->Suspend will exit when run up to setted level. + */ +extern void pm_set_test_level(enum suspend_test_level_t level); + +/** + * @brief Set delay ms in debug mode. + * @note To prevent mutual interference between devices. + * @param ms: + * @arg ms->The delayed ms between two devices. + */ +extern void pm_set_debug_delay_ms(unsigned int ms); + +/** @brief Show suspend statistic info. */ +extern void pm_stats_show(void); + +/** + * @brief Select pm modes used on this platform. + * @note Select modes at init for some modes are not used on some platforms. + * This will prevent enter unselected modes. + * @param select: + * @arg select->The selected modes set. + */ +extern void pm_mode_platform_select(unsigned int select); + +/** + * @brief register wlan power on/off callback. + * @note Wlan power on/off calback will be called when pm enter select modes. + * @param wlan_power_cb: + * @arg wlan_power_cb->Wlan power on/off calback. + * @param select: + * @arg select->The selected modes set. + * retval 0 if success or other if failed. + */ +extern int pm_register_wlan_power_onoff(pm_wlan_power_onoff wlan_power_cb, + unsigned int select); + +/** @brief unregister wlan power on/off callback. */ +extern void pm_unregister_wlan_power_onoff(void); + +extern int pm_test(void); +#else /* CONFIG_PM */ +static inline int pm_register_ops(struct soc_device *dev) { return 0; } +static inline int pm_unregister_ops(struct soc_device *dev) { return 0; } +static inline void pm_set_sync_magic(void) { ; } +static inline int pm_enter_mode(enum suspend_state_t state) { return 0; } +static inline int pm_init(void) { return 0;} +static inline void pm_set_test_level(enum suspend_test_level_t level) {;} +static inline void pm_set_debug_delay_ms(unsigned int ms) { ; } +static inline void pm_stats_show(void) {;} +static inline void pm_mode_platform_select(unsigned int select) {;} +static inline int pm_register_wlan_power_onoff(pm_wlan_power_onoff wlan_power_cb, + unsigned int select) { return 0; } +static inline int pm_test(void) { return 0; } +#endif /* CONFIG_PM */ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/platform/mcu/xr871/include/sys/defs.h b/platform/mcu/xr871/include/sys/defs.h new file mode 100644 index 0000000000..4e72cab74f --- /dev/null +++ b/platform/mcu/xr871/include/sys/defs.h @@ -0,0 +1,82 @@ +/* libc/sys/linux/sys/cdefs.h - Helper macros for K&R vs. ANSI C compat. */ + +/* Written 2000 by Werner Almesberger */ + +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Berkeley Software Design, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)cdefs.h 8.8 (Berkeley) 1/9/95 + * $FreeBSD$ + */ + +#ifndef _SYS_DEFS_H_ +#define _SYS_DEFS_H_ + +#include "types.h" +#include "compiler.h" + +#ifndef __DEQUALIFY +#define __DEQUALIFY(type, var) ((type)(uintptr_t)(const volatile void *)(var)) +#endif + +#ifndef offsetof +#define offsetof(type, field) \ + ((size_t)(uintptr_t)((const volatile void *)&((type *)0)->field)) +#endif + +#ifndef __offsetof +#define __offsetof(type, field) offsetof(type, field) +#endif + +#ifndef __containerof +#define __containerof(ptr, type, field) \ + __DEQUALIFY(type *, (const volatile char *)(ptr) - offsetof(type, field)) +#endif + +#ifndef container_of +#define container_of(ptr, type, field) __containerof(ptr, type, field) +#endif + + +/* + * Definitions for byte order, according to byte significance from low + * address to high. + */ +#define _LITTLE_ENDIAN 1234 /* LSB first: i386, vax */ +#define _BIG_ENDIAN 4321 /* MSB first: 68000, ibm, net */ + +#define LITTLE_ENDIAN _LITTLE_ENDIAN +#define BIG_ENDIAN _BIG_ENDIAN + +#define _BYTE_ORDER _LITTLE_ENDIAN +#define BYTE_ORDER _BYTE_ORDER + +#endif /* _SYS_DEFS_H_ */ diff --git a/platform/mcu/xr871/include/sys/ducc/ducc_addr.h b/platform/mcu/xr871/include/sys/ducc/ducc_addr.h new file mode 100644 index 0000000000..a7496d075b --- /dev/null +++ b/platform/mcu/xr871/include/sys/ducc/ducc_addr.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _SYS_DUCC_DUCC_ADDR_H_ +#define _SYS_DUCC_DUCC_ADDR_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* There are 4 types of memory address value for app core and net core. + * - APP_MEM_APP_ADDR: memory allocated from app, used by app + * - APP_MEM_NET_ADDR: memory allocated from app, used by net + * - NET_MEM_APP_ADDR: memory allocated from net, used by app + * - NET_MEM_NET_ADDR: memory allocated from net, used by net + */ + +/* convert from APP_MEM_APP_ADDR to APP_MEM_NET_ADDR */ +#define DUCC_APPMEM_APP2NET(addr) ((uint32_t)(addr) | 0x20000000) + +/* convert from APP_MEM_NET_ADDR to APP_MEM_APP_ADDR */ +#define DUCC_APPMEM_NET2APP(addr) ((uint32_t)(addr) & ~0xf0000000) + +/* convert from NET_MEM_NET_ADDR to NET_MEM_APP_ADDR */ +#define DUCC_NETMEM_NET2APP(addr) ((uint32_t)(addr) | 0x60000000) + +/* convert from NET_MEM_APP_ADDR to NET_MEM_NET_ADDR */ +#define DUCC_NETMEM_APP2NET(addr) ((uint32_t)(addr) & ~0xf0000000) + +#ifdef __cplusplus +} +#endif + +#endif /* _SYS_DUCC_DUCC_ADDR_H_ */ diff --git a/platform/mcu/xr871/include/sys/ducc/ducc_app.h b/platform/mcu/xr871/include/sys/ducc/ducc_app.h new file mode 100644 index 0000000000..5518d43a9b --- /dev/null +++ b/platform/mcu/xr871/include/sys/ducc/ducc_app.h @@ -0,0 +1,146 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _SYS_DUCC_DUCC_APP_H_ +#define _SYS_DUCC_DUCC_APP_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +enum ducc_app_cmd { + /* data command */ + DUCC_APP_CMD_WLAN_LINKOUTPUT, + + /* normal command */ +#if (__CONFIG_MBUF_IMPL_MODE == 0) + DUCC_APP_CMD_MBUF_GET, + DUCC_APP_CMD_MBUF_FREE, +#endif + DUCC_APP_CMD_CONSOLE_EXEC, + DUCC_APP_CMD_UART_CONFIG, + DUCC_APP_CMD_POWER_NOTIFY, + + DUCC_APP_CMD_WLAN_ATTACH, + DUCC_APP_CMD_WLAN_DETACH, + DUCC_APP_CMD_WLAN_IF_CREATE, + DUCC_APP_CMD_WLAN_IF_DELETE, + DUCC_APP_CMD_WLAN_START, + DUCC_APP_CMD_WLAN_STOP, + DUCC_APP_CMD_WLAN_GET_MAC_ADDR, + DUCC_APP_CMD_WLAN_SET_MAC_ADDR, + DUCC_APP_CMD_WLAN_SET_IP_ADDR, + DUCC_APP_CMD_WLAN_SET_APPIE, + + DUCC_APP_CMD_WLAN_MONITOR_ENABLE_RX, + DUCC_APP_CMD_WLAN_MONITOR_SET_CHAN, + + DUCC_APP_CMD_WLAN_WPA_CTRL_OPEN, + DUCC_APP_CMD_WLAN_WPA_CTRL_CLOSE, + DUCC_APP_CMD_WLAN_WPA_CTRL_REQUEST, +}; + +#define DUCC_APP_IS_DATA_CMD(c) \ + ((c) == DUCC_APP_CMD_WLAN_LINKOUTPUT) + +#if (__CONFIG_MBUF_IMPL_MODE == 0) +struct ducc_param_mbuf_get { + int len; + int tx; + void *mbuf; +}; +#endif + +struct ducc_param_wlan_create { + uint32_t mode; + void *nif; + const char *name; + void *ifp; /* @out */ +}; + +struct ducc_param_wlan_get_mac_addr { + void *ifp; + uint8_t *buf; + int buf_len; +}; + +struct ducc_param_wlan_set_mac_addr { + uint8_t *mac_addr; + int mac_len; +}; + +struct ducc_param_wlan_linkoutput { + void *ifp; + void *mbuf; +}; + +struct ducc_param_wlan_set_ip_addr { + void *ifp; + uint8_t *ip_addr; + int ip_len; +}; + +struct ducc_param_wlan_appie { + void *ifp; + uint8_t type; + uint16_t ie_len; + uint8_t *ie; +}; + +struct ducc_param_wlan_mon_set_chan { + void *ifp; + int16_t channel; +}; + +struct ducc_param_wlan_wpa_ctrl_req { + uint32_t cmd; + void *data; +}; + +#if (defined(__CONFIG_ARCH_DUAL_CORE) && defined(__CONFIG_ARCH_APP_CORE)) + +typedef int (*ducc_cb_func)(uint32_t param0, uint32_t param1); + +struct ducc_app_param { + ducc_cb_func cb; +}; + +int ducc_app_start(struct ducc_app_param *param); +int ducc_app_ioctl(enum ducc_app_cmd cmd, void *param); +int ducc_app_stop(void); + +#endif /* (defined(__CONFIG_ARCH_DUAL_CORE) && defined(__CONFIG_ARCH_APP_CORE)) */ + +#ifdef __cplusplus +} +#endif + +#endif /* _SYS_DUCC_DUCC_APP_H_ */ diff --git a/platform/mcu/xr871/include/sys/ducc/ducc_net.h b/platform/mcu/xr871/include/sys/ducc/ducc_net.h new file mode 100644 index 0000000000..2e7e343d13 --- /dev/null +++ b/platform/mcu/xr871/include/sys/ducc/ducc_net.h @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _SYS_DUCC_DUCC_NET_H_ +#define _SYS_DUCC_DUCC_NET_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +enum ducc_net_cmd { + /* data command */ + DUCC_NET_CMD_WLAN_INPUT, + DUCC_NET_CMD_WLAN_MONITOR_INPUT, /* data input for monitor mode */ + + /* normal command */ +#if (__CONFIG_MBUF_IMPL_MODE == 1) + DUCC_NET_CMD_MBUF_GET, + DUCC_NET_CMD_MBUF_FREE, +#endif + DUCC_NET_CMD_POWER_NOTIFY, + DUCC_NET_CMD_BIN_READ, + DUCC_NET_CMD_EFUSE_READ, + + DUCC_NET_CMD_SYS_EVENT, /* refer to enum ducc_net_sys_event */ + DUCC_NET_CMD_WLAN_EVENT, /* refer to enum wlan_event */ +}; + +#define DUCC_NET_IS_DATA_CMD(c) \ + ((c) <= DUCC_NET_CMD_WLAN_MONITOR_INPUT) + +enum ducc_net_sys_event { + DUCC_NET_SYS_READY, +}; + +struct ducc_param_wlan_input { + void *nif; +#if (__CONFIG_MBUF_IMPL_MODE == 0) + uint8_t *data; + int len; +#elif (__CONFIG_MBUF_IMPL_MODE == 1) + void *mbuf; +#endif +}; + +struct ducc_param_wlan_mon_input { + void *nif; + uint8_t *data; + uint32_t len; + void *info; +}; + +#if (__CONFIG_MBUF_IMPL_MODE == 1) +struct ducc_param_mbuf_get { + int len; + int tx; + void *mbuf; +}; +#endif + +#define DUCC_WLAN_BIN_TYPE_BL (0) +#define DUCC_WLAN_BIN_TYPE_FW (1) +#define DUCC_WLAN_BIN_TYPE_SDD (2) + +struct ducc_param_wlan_bin { + int type; + int index; + int len; + void *buf; +}; + +struct ducc_param_efuse { + uint8_t *data; + uint32_t start_bit; + uint32_t bit_num; +}; + +#if (defined(__CONFIG_ARCH_DUAL_CORE) && defined(__CONFIG_ARCH_NET_CORE)) + +typedef int (*ducc_cb_func)(uint32_t param0, uint32_t param1); + +struct ducc_net_param { + ducc_cb_func cb; +}; + +int ducc_net_start(struct ducc_net_param *param); +int ducc_net_ioctl(enum ducc_net_cmd cmd, void *param); + +#endif /* (defined(__CONFIG_ARCH_DUAL_CORE) && defined(__CONFIG_ARCH_NET_CORE)) */ + +#ifdef __cplusplus +} +#endif + +#endif /* _SYS_DUCC_DUCC_NET_H_ */ diff --git a/platform/mcu/xr871/include/sys/endian.h b/platform/mcu/xr871/include/sys/endian.h new file mode 100644 index 0000000000..505d496c2c --- /dev/null +++ b/platform/mcu/xr871/include/sys/endian.h @@ -0,0 +1,234 @@ +/*- + * Copyright (c) 2002 Thomas Moestl + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD: releng/10.1/sys/sys/endian.h 208331 2010-05-20 06:16:13Z phk $ + */ + +#ifndef _SYS_ENDIAN_H_ +#define _SYS_ENDIAN_H_ + +#include "types.h" +#include "sys/defs.h" + +/* TODO: Optimize it */ +#define __bswap16(x) ((uint16_t)( \ + (((uint16_t)(x) & (uint16_t)0x00ffU) << 8) | \ + (((uint16_t)(x) & (uint16_t)0xff00U) >> 8))) + +#define __bswap32(x) ((uint32_t)( \ + (((uint32_t)(x) & (uint32_t)0x000000ffUL) << 24) | \ + (((uint32_t)(x) & (uint32_t)0x0000ff00UL) << 8) | \ + (((uint32_t)(x) & (uint32_t)0x00ff0000UL) >> 8) | \ + (((uint32_t)(x) & (uint32_t)0xff000000UL) >> 24))) + +#define __bswap64(x) ((uint64_t)( \ + (((uint64_t)(x) & (uint64_t)0x00000000000000ffULL) << 56) | \ + (((uint64_t)(x) & (uint64_t)0x000000000000ff00ULL) << 40) | \ + (((uint64_t)(x) & (uint64_t)0x0000000000ff0000ULL) << 24) | \ + (((uint64_t)(x) & (uint64_t)0x00000000ff000000ULL) << 8) | \ + (((uint64_t)(x) & (uint64_t)0x000000ff00000000ULL) >> 8) | \ + (((uint64_t)(x) & (uint64_t)0x0000ff0000000000ULL) >> 24) | \ + (((uint64_t)(x) & (uint64_t)0x00ff000000000000ULL) >> 40) | \ + (((uint64_t)(x) & (uint64_t)0xff00000000000000ULL) >> 56))) + +/* + * General byte order swapping functions. + */ +#define bswap16(x) __bswap16(x) +#define bswap32(x) __bswap32(x) +#define bswap64(x) __bswap64(x) + +/* + * Host to big endian, host to little endian, big endian to host, and little + * endian to host byte order functions as detailed in byteorder(9). + */ +#if _BYTE_ORDER == _LITTLE_ENDIAN +#define htobe16(x) bswap16((x)) +#define htobe32(x) bswap32((x)) +#define htobe64(x) bswap64((x)) +#define htole16(x) ((uint16_t)(x)) +#define htole32(x) ((uint32_t)(x)) +#define htole64(x) ((uint64_t)(x)) + +#define be16toh(x) bswap16((x)) +#define be32toh(x) bswap32((x)) +#define be64toh(x) bswap64((x)) +#define le16toh(x) ((uint16_t)(x)) +#define le32toh(x) ((uint32_t)(x)) +#define le64toh(x) ((uint64_t)(x)) +#elif (_BYTE_ORDER == _BIG_ENDIAN) +#define htobe16(x) ((uint16_t)(x)) +#define htobe32(x) ((uint32_t)(x)) +#define htobe64(x) ((uint64_t)(x)) +#define htole16(x) bswap16((x)) +#define htole32(x) bswap32((x)) +#define htole64(x) bswap64((x)) + +#define be16toh(x) ((uint16_t)(x)) +#define be32toh(x) ((uint32_t)(x)) +#define be64toh(x) ((uint64_t)(x)) +#define le16toh(x) bswap16((x)) +#define le32toh(x) bswap32((x)) +#define le64toh(x) bswap64((x)) +#else +#error "Endian not defined!" +#endif /* _BYTE_ORDER == _LITTLE_ENDIAN */ + +#define __cpu_to_le64(x) htole64(x) +#define __le64_to_cpu(x) le64toh(x) +#define __cpu_to_le32(x) htole32(x) +#define __le32_to_cpu(x) le32toh(x) +#define __cpu_to_le16(x) htole16(x) +#define __le16_to_cpu(x) le16toh(x) +#define __cpu_to_be64(x) htobe64(x) +#define __be64_to_cpu(x) be64toh(x) +#define __cpu_to_be32(x) htobe32(x) +#define __be32_to_cpu(x) be32toh(x) +#define __cpu_to_be16(x) htobe16(x) +#define __be16_to_cpu(x) be16toh(x) + +#define cpu_to_le64 __cpu_to_le64 +#define le64_to_cpu __le64_to_cpu +#define cpu_to_le32 __cpu_to_le32 +#define le32_to_cpu __le32_to_cpu +#define cpu_to_le16 __cpu_to_le16 +#define le16_to_cpu __le16_to_cpu +#define cpu_to_be64 __cpu_to_be64 +#define be64_to_cpu __be64_to_cpu +#define cpu_to_be32 __cpu_to_be32 +#define be32_to_cpu __be32_to_cpu +#define cpu_to_be16 __cpu_to_be16 +#define be16_to_cpu __be16_to_cpu + + +/* Alignment-agnostic encode/decode bytestream to/from little/big endian. */ + +static __inline uint16_t +be16dec(const void *pp) +{ + uint8_t const *p = (uint8_t const *)pp; + + return ((p[0] << 8) | p[1]); +} + +static __inline uint32_t +be32dec(const void *pp) +{ + uint8_t const *p = (uint8_t const *)pp; + + return (((unsigned)p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]); +} + +static __inline uint64_t +be64dec(const void *pp) +{ + uint8_t const *p = (uint8_t const *)pp; + + return (((uint64_t)be32dec(p) << 32) | be32dec(p + 4)); +} + +static __inline uint16_t +le16dec(const void *pp) +{ + uint8_t const *p = (uint8_t const *)pp; + + return ((p[1] << 8) | p[0]); +} + +static __inline uint32_t +le32dec(const void *pp) +{ + uint8_t const *p = (uint8_t const *)pp; + + return (((unsigned)p[3] << 24) | (p[2] << 16) | (p[1] << 8) | p[0]); +} + +static __inline uint64_t +le64dec(const void *pp) +{ + uint8_t const *p = (uint8_t const *)pp; + + return (((uint64_t)le32dec(p + 4) << 32) | le32dec(p)); +} + +static __inline void +be16enc(void *pp, uint16_t u) +{ + uint8_t *p = (uint8_t *)pp; + + p[0] = (u >> 8) & 0xff; + p[1] = u & 0xff; +} + +static __inline void +be32enc(void *pp, uint32_t u) +{ + uint8_t *p = (uint8_t *)pp; + + p[0] = (u >> 24) & 0xff; + p[1] = (u >> 16) & 0xff; + p[2] = (u >> 8) & 0xff; + p[3] = u & 0xff; +} + +static __inline void +be64enc(void *pp, uint64_t u) +{ + uint8_t *p = (uint8_t *)pp; + + be32enc(p, (uint32_t)(u >> 32)); + be32enc(p + 4, (uint32_t)(u & 0xffffffffU)); +} + +static __inline void +le16enc(void *pp, uint16_t u) +{ + uint8_t *p = (uint8_t *)pp; + + p[0] = u & 0xff; + p[1] = (u >> 8) & 0xff; +} + +static __inline void +le32enc(void *pp, uint32_t u) +{ + uint8_t *p = (uint8_t *)pp; + + p[0] = u & 0xff; + p[1] = (u >> 8) & 0xff; + p[2] = (u >> 16) & 0xff; + p[3] = (u >> 24) & 0xff; +} + +static __inline void +le64enc(void *pp, uint64_t u) +{ + uint8_t *p = (uint8_t *)pp; + + le32enc(p, (uint32_t)(u & 0xffffffffU)); + le32enc(p + 4, (uint32_t)(u >> 32)); +} + +#endif /* _SYS_ENDIAN_H_ */ diff --git a/platform/mcu/xr871/include/sys/fdcm.h b/platform/mcu/xr871/include/sys/fdcm.h new file mode 100644 index 0000000000..859a039cc2 --- /dev/null +++ b/platform/mcu/xr871/include/sys/fdcm.h @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _SYS_FDCM_H_ +#define _SYS_FDCM_H_ + +#include "types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief FDCM handle definition + */ +typedef struct fdcm_handle { + uint32_t flash; + uint32_t addr; + uint32_t size; +} fdcm_handle_t; + +fdcm_handle_t *fdcm_open(uint32_t flash, uint32_t addr, uint32_t size); +uint32_t fdcm_read(fdcm_handle_t *hdl, void *data, uint16_t data_size); +uint32_t fdcm_write(fdcm_handle_t *hdl, void *data, uint16_t data_size); +void fdcm_close(fdcm_handle_t *hdl); + +#ifdef __cplusplus +} +#endif + +#endif /* _SYS_FDCM_H_ */ diff --git a/platform/mcu/xr871/include/sys/image.h b/platform/mcu/xr871/include/sys/image.h new file mode 100644 index 0000000000..87811dba58 --- /dev/null +++ b/platform/mcu/xr871/include/sys/image.h @@ -0,0 +1,141 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _SYS_IMAGE_H_ +#define _SYS_IMAGE_H_ + +#include "types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define IMAGE_INVALID_FLASH (0xFFFFFFFF) +#define IMAGE_INVALID_ADDR (0xFFFFFFFF) + +/** + * @brief Image sequence definition + */ +typedef enum image_sequence { + IMAGE_SEQ_1ST = 0, + IMAGE_SEQ_2ND = 1, + IMAGE_SEQ_NUM = 2, +} image_seq_t; + +/** + * @brief Image segment definition + */ +typedef enum image_segment { + IMAGE_SEG_HEADER = 0, + IMAGE_SEG_BODY = 1, + IMAGE_SEG_TAILER = 2, +} image_seg_t; + +/** + * @brief Image validity definition + */ +typedef enum image_validity { + IMAGE_INVALID = 0, + IMAGE_VALID = 1, +} image_val_t; + +/** + * @brief Section header magic number definition (AWIH) + */ +#define IMAGE_MAGIC_NUMBER (0x48495741) + +/** + * @brief Section ID definition + */ +#define IMAGE_BOOT_ID (0xA5FF5A00) +#define IMAGE_APP_ID (0xA5FE5A01) +#define IMAGE_APP_XIP_ID (0xA5FD5A02) +#define IMAGE_NET_ID (0xA5FC5A03) +#define IMAGE_NET_AP_ID (0xA5FB5A04) +#define IMAGE_WLAN_BL_ID (0xA5FA5A05) +#define IMAGE_WLAN_FW_ID (0xA5F95A06) +#define IMAGE_WLAN_SDD_ID (0xA5F85A07) + +/** + * @brief Section header definition (64 Bytes) + */ +typedef struct section_header { + uint32_t magic_number; /* magic number */ + uint32_t version; /* version */ + uint16_t header_chksum; /* header checksum */ + uint16_t data_chksum; /* data checksum */ + uint32_t data_size; /* data size */ + uint32_t load_addr; /* load address */ + uint32_t entry; /* entry point */ + uint32_t body_len; /* body length */ + uint32_t attribute; /* attribute */ + uint32_t next_addr; /* next section address */ + uint32_t id; /* section ID */ + uint32_t priv[6]; /* private data */ +} section_header_t; + +#define IMAGE_HEADER_SIZE sizeof(section_header_t) + +/** + * @brief OTA parameter definition + */ +typedef struct image_ota_param { + uint32_t flash[IMAGE_SEQ_NUM]; + uint32_t addr[IMAGE_SEQ_NUM]; + uint32_t image_size; + uint32_t boot_size; +} image_ota_param_t; + +void image_init(uint32_t flash, uint32_t addr, uint32_t size); +void image_deinit(void); + +void image_get_ota_param(image_ota_param_t *param); + +void image_set_running_seq(image_seq_t seq); +void image_get_running_seq(image_seq_t *seq); + +uint32_t image_get_section_addr(uint32_t id); + +uint32_t image_read(uint32_t id, image_seg_t seg, uint32_t offset, void *buf, uint32_t size); +uint32_t image_write(uint32_t id, image_seg_t seg, uint32_t offset, void *buf, uint32_t size); + +uint16_t image_get_checksum(void *buf, uint32_t len); + +image_val_t image_check_header(section_header_t *sh); +image_val_t image_check_data(section_header_t *sh, void *body, uint32_t body_len, + void *tailer, uint32_t tailer_len); + +image_val_t image_check_section(image_seq_t seq, uint32_t id); +image_val_t image_check_sections(image_seq_t seq); + +#ifdef __cplusplus +} +#endif + +#endif /* _SYS_IMAGE_H_ */ diff --git a/platform/mcu/xr871/include/sys/interrupt.h b/platform/mcu/xr871/include/sys/interrupt.h new file mode 100644 index 0000000000..6d64e1c60d --- /dev/null +++ b/platform/mcu/xr871/include/sys/interrupt.h @@ -0,0 +1,159 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _SYS_INTERRUPT_H_ +#define _SYS_INTERRUPT_H_ + +#include "compiler.h" + +#if defined(__CC_ARM) +/* ARM Compiler */ + +/* + * CPU interrupt mask handling. + */ +#define IRQMASK_REG_NAME_R primask +#define IRQMASK_REG_NAME_W primask + +/* + * Save the current interrupt enable state & disable IRQs + */ +static __always_inline unsigned long arch_irq_save(void) +{ + unsigned long flags; + + __asm { + mrs flags, IRQMASK_REG_NAME_R + cpsid i + } + return flags; +} + +/* + * restore saved IRQ state + */ +static __always_inline void arch_irq_restore(unsigned long flags) +{ + __asm { msr IRQMASK_REG_NAME_W, flags } +} + +/* + * Enable IRQs + */ +#define arch_irq_enable() __enable_irq() + +/* + * Disable IRQs + */ +#define arch_irq_disable() __disable_irq() + +/* + * Enable FIQs + */ +#define arch_fiq_enable() __enable_fiq() + +/* + * Disable FIQs + */ +#define arch_fiq_disable() __disable_fiq() + +#elif defined(__GNUC__) +/* GNU Compiler */ + +/* + * CPU interrupt mask handling. + */ +#define IRQMASK_REG_NAME_R "primask" +#define IRQMASK_REG_NAME_W "primask" + +/* + * Save the current interrupt enable state & disable IRQs + */ +static __always_inline unsigned long arch_irq_save(void) +{ + unsigned long flags; + + __asm volatile( + "mrs %0, " IRQMASK_REG_NAME_R "\n" + "cpsid i" + : "=r" (flags) : : "memory", "cc"); + return flags; +} + +/* + * restore saved IRQ state + */ +static __always_inline void arch_irq_restore(unsigned long flags) +{ + __asm volatile( + "msr " IRQMASK_REG_NAME_W ", %0" + : + : "r" (flags) + : "memory", "cc"); +} + +/* + * Save the current interrupt enable state. + */ +static __always_inline unsigned long arch_irq_get_flags(void) +{ + unsigned long flags; + + __asm volatile( + "mrs %0, " IRQMASK_REG_NAME_R "\n" + : "=r" (flags) : : "memory", "cc"); + return flags; + +} + +/* + * Enable IRQs + */ +#define arch_irq_enable() __asm volatile("cpsie i" : : : "memory", "cc") + +/* + * Disable IRQs + */ +#define arch_irq_disable() __asm volatile("cpsid i" : : : "memory", "cc") + +/* + * Enable FIQs + */ +#define arch_fiq_enable() __asm volatile("cpsie f" : : : "memory", "cc") + +/* + * Disable FIQs + */ +#define arch_fiq_disable() __asm volatile("cpsid f" : : : "memory", "cc") + +#else +#error "Compiler not supported." +#endif + +#endif /* _SYS_INTERRUPT_H_ */ diff --git a/platform/mcu/xr871/include/sys/io.h b/platform/mcu/xr871/include/sys/io.h new file mode 100644 index 0000000000..84aa5e01e6 --- /dev/null +++ b/platform/mcu/xr871/include/sys/io.h @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _IO_H +#define _IO_H + +#include +#include + +#define BIT(nr) (1UL << (nr)) + +#define get_bvalue(addr) (*((volatile unsigned char *)(addr))) +#define put_bvalue(addr, v) (*((volatile unsigned char *)(addr)) = (unsigned char)(v)) +#define get_hvalue(addr) (*((volatile unsigned short *)(addr))) +#define put_hvalue(addr, v) (*((volatile unsigned short *)(addr)) = (unsigned short)(v)) +#define get_wvalue(addr) (*((volatile unsigned long *)(addr))) +#define put_wvalue(addr, v) (*((volatile unsigned long *)(addr)) = (unsigned long)(v)) + +#define set_bit(addr, v) (*((volatile unsigned char *)(addr)) |= (unsigned char)(v)) +#define clr_bit(addr, v) (*((volatile unsigned char *)(addr)) &= ~(unsigned char)(v)) +#define set_bbit(addr, v) (*((volatile unsigned char *)(addr)) |= (unsigned char)(v)) +#define clr_bbit(addr, v) (*((volatile unsigned char *)(addr)) &= ~(unsigned char)(v)) +#define set_hbit(addr, v) (*((volatile unsigned short *)(addr)) |= (unsigned short)(v)) +#define clr_hbit(addr, v) (*((volatile unsigned short *)(addr)) &= ~(unsigned short)(v)) +#define set_wbit(addr, v) (*((volatile unsigned long *)(addr)) |= (unsigned long)(v)) +#define clr_wbit(addr, v) (*((volatile unsigned long *)(addr)) &= ~(unsigned long)(v)) + +#define readb(addr) (*((volatile unsigned char *)(addr))) +#define readw(addr) (*((volatile unsigned short *)(addr))) +#define readl(addr) (*((volatile unsigned long *)(addr))) +#define writeb(v, addr) (*((volatile unsigned char *)(addr)) = (unsigned char)(v)) +#define writew(v, addr) (*((volatile unsigned short *)(addr)) = (unsigned short)(v)) +#define writel(v, addr) (*((volatile unsigned long *)(addr)) = (unsigned long)(v)) + +#define ARRAY_SIZE(n) (sizeof(n)/sizeof(n[0])) + +/** + * sr32 - clear & set a value in a bit range for a 32 bit address + */ +static __inline void sr32(unsigned int addr, unsigned int start_bit, unsigned int num_bits, unsigned int value) +{ + unsigned int tmp, msk = (1 << num_bits) - 1; + tmp = readl(addr) & ~(msk << start_bit); + tmp |= value << start_bit; + writel(tmp, addr); +} + +#endif /* _IO_H */ diff --git a/platform/mcu/xr871/include/sys/list.h b/platform/mcu/xr871/include/sys/list.h new file mode 100644 index 0000000000..daa43e9781 --- /dev/null +++ b/platform/mcu/xr871/include/sys/list.h @@ -0,0 +1,584 @@ +#ifndef _SYS_LIST_H +#define _SYS_LIST_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "compiler.h" + +/* + * Simple doubly linked list implementation. + * + * Some of the internal functions ("__xxx") are useful when + * manipulating whole lists rather than single entries, as + * sometimes we already know the next/prev entries and we can + * generate better code by using them directly rather than + * using the generic single-entry routines. + */ + +struct list_head { + struct list_head *next, *prev; +}; + +#define LIST_HEAD_INIT(name) { &(name), &(name) } + +#define LIST_HEAD_DEF(name) \ + struct list_head name = LIST_HEAD_INIT(name) + +static inline void INIT_LIST_HEAD(struct list_head *list) +{ + list->next = list; + list->prev = list; +} + +/* + * Insert a new entry between two known consecutive entries. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + */ +static inline void __list_add(struct list_head *newl, + struct list_head *prev, + struct list_head *next) +{ + next->prev = newl; + newl->next = next; + newl->prev = prev; + prev->next = newl; +} + +/** + * list_add - add a new entry + * @new: new entry to be added + * @head: list head to add it after + * + * Insert a new entry after the specified head. + * This is good for implementing stacks. + */ +static inline void list_add(struct list_head *newl, struct list_head *head) +{ + __list_add(newl, head, head->next); +} + + +/** + * list_add_tail - add a new entry + * @new: new entry to be added + * @head: list head to add it before + * + * Insert a new entry before the specified head. + * This is useful for implementing queues. + */ +static inline void list_add_tail(struct list_head *newl, struct list_head *head) +{ + __list_add(newl, head->prev, head); +} + +/* + * Delete a list entry by making the prev/next entries + * point to each other. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + */ +static inline void __list_del(struct list_head * prev, struct list_head * next) +{ + next->prev = prev; + prev->next = next; +} + +/** + * list_del - deletes entry from list. + * @entry: the element to delete from the list. + * Note: list_empty() on entry does not return true after this, the entry is + * in an undefined state. + */ +static inline void __list_del_entry(struct list_head *entry) +{ + __list_del(entry->prev, entry->next); +} + +static inline void list_del(struct list_head *entry) +{ + __list_del(entry->prev, entry->next); + entry->next = entry; + entry->prev = entry; +} + +/** + * list_replace - replace old entry by new one + * @old : the element to be replaced + * @new : the new element to insert + * + * If @old was empty, it will be overwritten. + */ +static inline void list_replace(struct list_head *old, struct list_head *newl) +{ + newl->next = old->next; + newl->next->prev = newl; + newl->prev = old->prev; + newl->prev->next = newl; +} + +static inline void list_replace_init(struct list_head *old, + struct list_head *newl) +{ + list_replace(old, newl); + INIT_LIST_HEAD(old); +} + +/** + * list_del_init - deletes entry from list and reinitialize it. + * @entry: the element to delete from the list. + */ +static inline void list_del_init(struct list_head *entry) +{ + __list_del_entry(entry); + INIT_LIST_HEAD(entry); +} + +/** + * list_move - delete from one list and add as another's head + * @list: the entry to move + * @head: the head that will precede our entry + */ +static inline void list_move(struct list_head *list, struct list_head *head) +{ + __list_del_entry(list); + list_add(list, head); +} + +/** + * list_move_tail - delete from one list and add as another's tail + * @list: the entry to move + * @head: the head that will follow our entry + */ +static inline void list_move_tail(struct list_head *list, + struct list_head *head) +{ + __list_del_entry(list); + list_add_tail(list, head); +} + +/** + * list_is_last - tests whether @list is the last entry in list @head + * @list: the entry to test + * @head: the head of the list + */ +static inline int list_is_last(const struct list_head *list, + const struct list_head *head) +{ + return list->next == head; +} + +/** + * list_empty - tests whether a list is empty + * @head: the list to test. + */ +static inline int list_empty(const struct list_head *head) +{ + return head->next == head; +} + +/** + * list_empty_careful - tests whether a list is empty and not being modified + * @head: the list to test + * + * Description: + * tests whether a list is empty _and_ checks that no other CPU might be + * in the process of modifying either member (next or prev) + * + * NOTE: using list_empty_careful() without synchronization + * can only be safe if the only activity that can happen + * to the list entry is list_del_init(). Eg. it cannot be used + * if another CPU could re-list_add() it. + */ +static inline int list_empty_careful(const struct list_head *head) +{ + struct list_head *next = head->next; + return (next == head) && (next == head->prev); +} + +/** + * list_rotate_left - rotate the list to the left + * @head: the head of the list + */ +static inline void list_rotate_left(struct list_head *head) +{ + struct list_head *first; + + if (!list_empty(head)) { + first = head->next; + list_move_tail(first, head); + } +} + +/** + * list_is_singular - tests whether a list has just one entry. + * @head: the list to test. + */ +static inline int list_is_singular(const struct list_head *head) +{ + return !list_empty(head) && (head->next == head->prev); +} + +static inline void __list_cut_position(struct list_head *list, + struct list_head *head, struct list_head *entry) +{ + struct list_head *new_first = entry->next; + list->next = head->next; + list->next->prev = list; + list->prev = entry; + entry->next = list; + head->next = new_first; + new_first->prev = head; +} + +/** + * list_cut_position - cut a list into two + * @list: a new list to add all removed entries + * @head: a list with entries + * @entry: an entry within head, could be the head itself + * and if so we won't cut the list + * + * This helper moves the initial part of @head, up to and + * including @entry, from @head to @list. You should + * pass on @entry an element you know is on @head. @list + * should be an empty list or a list you do not care about + * losing its data. + * + */ +static inline void list_cut_position(struct list_head *list, + struct list_head *head, struct list_head *entry) +{ + if (list_empty(head)) + return; + if (list_is_singular(head) && + (head->next != entry && head != entry)) + return; + if (entry == head) + INIT_LIST_HEAD(list); + else + __list_cut_position(list, head, entry); +} + +static inline void __list_splice(const struct list_head *list, + struct list_head *prev, + struct list_head *next) +{ + struct list_head *first = list->next; + struct list_head *last = list->prev; + + first->prev = prev; + prev->next = first; + + last->next = next; + next->prev = last; +} + +/** + * list_splice - join two lists, this is designed for stacks + * @list: the new list to add. + * @head: the place to add it in the first list. + */ +static inline void list_splice(const struct list_head *list, + struct list_head *head) +{ + if (!list_empty(list)) + __list_splice(list, head, head->next); +} + +/** + * list_splice_tail - join two lists, each list being a queue + * @list: the new list to add. + * @head: the place to add it in the first list. + */ +static inline void list_splice_tail(struct list_head *list, + struct list_head *head) +{ + if (!list_empty(list)) + __list_splice(list, head->prev, head); +} + +/** + * list_splice_init - join two lists and reinitialise the emptied list. + * @list: the new list to add. + * @head: the place to add it in the first list. + * + * The list at @list is reinitialised + */ +static inline void list_splice_init(struct list_head *list, + struct list_head *head) +{ + if (!list_empty(list)) { + __list_splice(list, head, head->next); + INIT_LIST_HEAD(list); + } +} + +/** + * list_splice_tail_init - join two lists and reinitialise the emptied list + * @list: the new list to add. + * @head: the place to add it in the first list. + * + * Each of the lists is a queue. + * The list at @list is reinitialised + */ +static inline void list_splice_tail_init(struct list_head *list, + struct list_head *head) +{ + if (!list_empty(list)) { + __list_splice(list, head->prev, head); + INIT_LIST_HEAD(list); + } +} + +/** + * list_entry - get the struct for this entry + * @ptr: the &struct list_head pointer. + * @type: the type of the struct this is embedded in. + * @member: the name of the list_head within the struct. + */ +#define list_entry(ptr, type, member) \ + container_of(ptr, type, member) + +/** + * list_first_entry - get the first element from a list + * @ptr: the list head to take the element from. + * @type: the type of the struct this is embedded in. + * @member: the name of the list_head within the struct. + * + * Note, that list is expected to be not empty. + */ +#define list_first_entry(ptr, type, member) \ + list_entry((ptr)->next, type, member) + +/** + * list_last_entry - get the last element from a list + * @ptr: the list head to take the element from. + * @type: the type of the struct this is embedded in. + * @member: the name of the list_head within the struct. + * + * Note, that list is expected to be not empty. + */ +#define list_last_entry(ptr, type, member) \ + list_entry((ptr)->prev, type, member) + +/** + * list_first_entry_or_null - get the first element from a list + * @ptr: the list head to take the element from. + * @type: the type of the struct this is embedded in. + * @member: the name of the list_head within the struct. + * + * Note that if the list is empty, it returns NULL. + */ +#define list_first_entry_or_null(ptr, type, member) \ + (!list_empty(ptr) ? list_first_entry(ptr, type, member) : NULL) + +/** + * list_next_entry - get the next element in list + * @pos: the type * to cursor + * @member: the name of the list_head within the struct. + */ +#define list_next_entry(pos, member) \ + list_entry((pos)->member.next, typeof(*(pos)), member) + +/** + * list_prev_entry - get the prev element in list + * @pos: the type * to cursor + * @member: the name of the list_head within the struct. + */ +#define list_prev_entry(pos, member) \ + list_entry((pos)->member.prev, typeof(*(pos)), member) + +/** + * list_for_each - iterate over a list + * @pos: the &struct list_head to use as a loop cursor. + * @head: the head for your list. + */ +#define list_for_each(pos, head) \ + for (pos = (head)->next; pos != (head); pos = pos->next) + +/** + * list_for_each_prev - iterate over a list backwards + * @pos: the &struct list_head to use as a loop cursor. + * @head: the head for your list. + */ +#define list_for_each_prev(pos, head) \ + for (pos = (head)->prev; pos != (head); pos = pos->prev) + +/** + * list_for_each_safe - iterate over a list safe against removal of list entry + * @pos: the &struct list_head to use as a loop cursor. + * @n: another &struct list_head to use as temporary storage + * @head: the head for your list. + */ +#define list_for_each_safe(pos, n, head) \ + for (pos = (head)->next, n = pos->next; pos != (head); \ + pos = n, n = pos->next) + +/** + * list_for_each_prev_safe - iterate over a list backwards safe against removal of list entry + * @pos: the &struct list_head to use as a loop cursor. + * @n: another &struct list_head to use as temporary storage + * @head: the head for your list. + */ +#define list_for_each_prev_safe(pos, n, head) \ + for (pos = (head)->prev, n = pos->prev; \ + pos != (head); \ + pos = n, n = pos->prev) + +/** + * list_for_each_entry - iterate over list of given type + * @pos: the type * to use as a loop cursor. + * @head: the head for your list. + * @member: the name of the list_head within the struct. + */ +#define list_for_each_entry(pos, head, member) \ + for (pos = list_first_entry(head, typeof(*pos), member); \ + &pos->member != (head); \ + pos = list_next_entry(pos, member)) + +/** + * list_for_each_entry_reverse - iterate backwards over list of given type. + * @pos: the type * to use as a loop cursor. + * @head: the head for your list. + * @member: the name of the list_head within the struct. + */ +#define list_for_each_entry_reverse(pos, head, member) \ + for (pos = list_last_entry(head, typeof(*pos), member); \ + &pos->member != (head); \ + pos = list_prev_entry(pos, member)) + +/** + * list_prepare_entry - prepare a pos entry for use in list_for_each_entry_continue() + * @pos: the type * to use as a start point + * @head: the head of the list + * @member: the name of the list_head within the struct. + * + * Prepares a pos entry for use as a start point in list_for_each_entry_continue(). + */ +#define list_prepare_entry(pos, head, member) \ + ((pos) ? : list_entry(head, typeof(*pos), member)) + +/** + * list_for_each_entry_continue - continue iteration over list of given type + * @pos: the type * to use as a loop cursor. + * @head: the head for your list. + * @member: the name of the list_head within the struct. + * + * Continue to iterate over list of given type, continuing after + * the current position. + */ +#define list_for_each_entry_continue(pos, head, member) \ + for (pos = list_next_entry(pos, member); \ + &pos->member != (head); \ + pos = list_next_entry(pos, member)) + +/** + * list_for_each_entry_continue_reverse - iterate backwards from the given point + * @pos: the type * to use as a loop cursor. + * @head: the head for your list. + * @member: the name of the list_head within the struct. + * + * Start to iterate over list of given type backwards, continuing after + * the current position. + */ +#define list_for_each_entry_continue_reverse(pos, head, member) \ + for (pos = list_prev_entry(pos, member); \ + &pos->member != (head); \ + pos = list_prev_entry(pos, member)) + +/** + * list_for_each_entry_from - iterate over list of given type from the current point + * @pos: the type * to use as a loop cursor. + * @head: the head for your list. + * @member: the name of the list_head within the struct. + * + * Iterate over list of given type, continuing from current position. + */ +#define list_for_each_entry_from(pos, head, member) \ + for (; &pos->member != (head); \ + pos = list_next_entry(pos, member)) + +/** + * list_for_each_entry_safe - iterate over list of given type safe against removal of list entry + * @pos: the type * to use as a loop cursor. + * @n: another type * to use as temporary storage + * @head: the head for your list. + * @member: the name of the list_head within the struct. + */ +#define list_for_each_entry_safe(pos, n, head, member) \ + for (pos = list_first_entry(head, typeof(*pos), member), \ + n = list_next_entry(pos, member); \ + &pos->member != (head); \ + pos = n, n = list_next_entry(n, member)) + +/** + * list_for_each_entry_safe_continue - continue list iteration safe against removal + * @pos: the type * to use as a loop cursor. + * @n: another type * to use as temporary storage + * @head: the head for your list. + * @member: the name of the list_head within the struct. + * + * Iterate over list of given type, continuing after current point, + * safe against removal of list entry. + */ +#define list_for_each_entry_safe_continue(pos, n, head, member) \ + for (pos = list_next_entry(pos, member), \ + n = list_next_entry(pos, member); \ + &pos->member != (head); \ + pos = n, n = list_next_entry(n, member)) + +/** + * list_for_each_entry_safe_from - iterate over list from current point safe against removal + * @pos: the type * to use as a loop cursor. + * @n: another type * to use as temporary storage + * @head: the head for your list. + * @member: the name of the list_head within the struct. + * + * Iterate over list of given type from current point, safe against + * removal of list entry. + */ +#define list_for_each_entry_safe_from(pos, n, head, member) \ + for (n = list_next_entry(pos, member); \ + &pos->member != (head); \ + pos = n, n = list_next_entry(n, member)) + +/** + * list_for_each_entry_safe_reverse - iterate backwards over list safe against removal + * @pos: the type * to use as a loop cursor. + * @n: another type * to use as temporary storage + * @head: the head for your list. + * @member: the name of the list_head within the struct. + * + * Iterate backwards over list of given type, safe against removal + * of list entry. + */ +#define list_for_each_entry_safe_reverse(pos, n, head, member) \ + for (pos = list_last_entry(head, typeof(*pos), member), \ + n = list_prev_entry(pos, member); \ + &pos->member != (head); \ + pos = n, n = list_prev_entry(n, member)) + +/** + * list_safe_reset_next - reset a stale list_for_each_entry_safe loop + * @pos: the loop cursor used in the list_for_each_entry_safe loop + * @n: temporary storage used in list_for_each_entry_safe + * @member: the name of the list_head within the struct. + * + * list_safe_reset_next is not safe to use in general if the list may be + * modified concurrently (eg. the lock is dropped in the loop body). An + * exception to this is if the cursor element (pos) is pinned in the list, + * and list_safe_reset_next is called after re-taking the lock and before + * completing the current iteration of the loop body. + */ +#define list_safe_reset_next(pos, n, member) \ + n = list_next_entry(pos, member) + +#ifdef __cplusplus +} +#endif + +#endif /* _SYS_LIST_H */ diff --git a/platform/mcu/xr871/include/sys/mbuf.h b/platform/mcu/xr871/include/sys/mbuf.h new file mode 100644 index 0000000000..1c292a4eb9 --- /dev/null +++ b/platform/mcu/xr871/include/sys/mbuf.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _SYS_MBUF_H_ +#define _SYS_MBUF_H_ + +#if (__CONFIG_MBUF_IMPL_MODE == 0) +#include "sys/mbuf_0.h" +#elif (__CONFIG_MBUF_IMPL_MODE == 1) +#include "sys/mbuf_1.h" +#else +#error "Invalid __CONFIG_MBUF_IMPL_MODE!" +#endif + +#endif /* !_SYS_MBUF_H_ */ diff --git a/platform/mcu/xr871/include/sys/mbuf_0.h b/platform/mcu/xr871/include/sys/mbuf_0.h new file mode 100644 index 0000000000..d239987574 --- /dev/null +++ b/platform/mcu/xr871/include/sys/mbuf_0.h @@ -0,0 +1,157 @@ +/*- + * Copyright (c) 1982, 1986, 1988, 1993 + * The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)mbuf.h 8.5 (Berkeley) 2/19/95 + * $FreeBSD: releng/10.1/sys/sys/mbuf.h 269047 2014-07-24 06:03:45Z kevlo $ + */ + +#ifndef _SYS_MBUF_0_H_ +#define _SYS_MBUF_0_H_ + +#if (__CONFIG_MBUF_IMPL_MODE == 0) + +#include +#ifdef __CONFIG_ARCH_DUAL_CORE +#include "sys/ducc/ducc_addr.h" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Callback structure + */ +struct m_cb { + void *func; + void *arg; +}; + +/* + * Record/packet header in first mbuf of chain; always valid and M_PKTHDR is set. + * Size : 24 + */ +struct pkthdr { + void *rcvif; /* rcv interface */ + int32_t len; /* total packet length */ + + union { + uint8_t eigth[8]; + uint16_t sixteen[4]; + uint32_t thirtytwo[2]; + uint64_t sixtyfour[1]; + uintptr_t unintptr[1]; + void *ptr; + } PH_per; + + struct m_cb cb; +}; +#define ether_vtag PH_per.sixteen[0] +#define PH_vt PH_per +#define vt_nrecs sixteen[0] +#define tso_segsz PH_per.sixteen[1] +#define csum_phsum PH_per.sixteen[2] +#define csum_data PH_per.thirtytwo[1] + +/* + * The core of the mbuf object along with some shortcut defines for practical + * purposes. + */ +struct mbuf { // Size : 48 + /* + * Header present at the beginning of every mbuf. + * Size : 24 + */ + uint8_t *m_buf; /* a continuous buffer */ // useless now + struct mbuf *m_nextpkt; /* next chain in queue/record */ + uint8_t *m_data; /* location of data */ + int32_t m_len; /* amount of data in this mbuf */ + uint16_t m_headspace; /* empty space available at the head */ + uint16_t m_tailspace; /* empty space available at the tail */ + uint32_t m_type :8, /* type of data in this mbuf */ // useless now + m_flags :24; /* flags; see below */ + /*** End of the mbuf header ***/ + + struct pkthdr m_pkthdr; /* M_PKTHDR always set */ +}; + +#ifdef __CONFIG_ARCH_DUAL_CORE +/* + * NB: mbuf is allocated from net core, pointer conversion MUST be done when + * transfer mbuf from app core to net core, or vice versa. + */ + +#ifdef __CONFIG_ARCH_NET_CORE +/* convert mbuf's pointers in net core, the new mbuf is used by net core */ +#define MBUF_APP2NET(m) \ + do { \ + (m) = (struct mbuf *)DUCC_NETMEM_APP2NET(m); \ + (m)->m_data = (uint8_t *)DUCC_NETMEM_APP2NET((m)->m_data); \ + } while (0) + +/* convert mbuf's pointers in net core, the new mbuf is used by app core */ +#define MBUF_NET2APP(m) \ + do { \ + (m)->m_data = (uint8_t *)DUCC_NETMEM_NET2APP((m)->m_data); \ + (m) = (struct mbuf *)DUCC_NETMEM_NET2APP(m); \ + } while (0) +#endif /* __CONFIG_ARCH_NET_CORE */ + +#ifdef __CONFIG_ARCH_APP_CORE +/* convert mbuf's pointers in app core, the new mbuf is used by net core */ +#define MBUF_APP2NET(m) \ + do { \ + (m)->m_data = (uint8_t *)DUCC_NETMEM_APP2NET((m)->m_data); \ + (m) = (struct mbuf *)DUCC_NETMEM_APP2NET(m); \ + } while (0) + +/* convert mbuf's pointers in app core, the new mbuf is used by app core */ +#define MBUF_NET2APP(m) \ + do { \ + (m) = (struct mbuf *)DUCC_NETMEM_NET2APP(m); \ + (m)->m_data = (uint8_t *)DUCC_NETMEM_NET2APP((m)->m_data); \ + } while (0) +#endif /* __CONFIG_ARCH_APP_CORE */ + +#endif /* __CONFIG_ARCH_DUAL_CORE */ + +/* + * Macro for type conversion: convert mbuf pointer to data pointer of correct + * type: + * + * mtod(m, t) -- Convert mbuf pointer to data pointer of correct type. + */ +#define mtod(m, t) ((t)((m)->m_data)) + +#ifdef __cplusplus +} +#endif + +#endif /* (__CONFIG_MBUF_IMPL_MODE == 0) */ +#endif /* !_SYS_MBUF_0_H_ */ diff --git a/platform/mcu/xr871/include/sys/mbuf_1.h b/platform/mcu/xr871/include/sys/mbuf_1.h new file mode 100644 index 0000000000..8c1fcba81b --- /dev/null +++ b/platform/mcu/xr871/include/sys/mbuf_1.h @@ -0,0 +1,244 @@ +/*- + * Copyright (c) 1982, 1986, 1988, 1993 + * The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)mbuf.h 8.5 (Berkeley) 2/19/95 + * $FreeBSD: releng/10.1/sys/sys/mbuf.h 269047 2014-07-24 06:03:45Z kevlo $ + */ + +#ifndef _SYS_MBUF_1_H_ +#define _SYS_MBUF_1_H_ + +#if (__CONFIG_MBUF_IMPL_MODE == 1) + +#include +#ifdef __CONFIG_ARCH_DUAL_CORE +#include "sys/ducc/ducc_addr.h" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Callback structure + */ +struct m_cb { + void *func; + void *arg; +}; + +/* + * Record/packet header in first mbuf of chain; always valid and M_PKTHDR is set. + * Size : 24 + */ +struct pkthdr { + void *rcvif; /* rcv interface */ + int32_t len; /* total packet length */ + + union { + uint8_t eigth[8]; + uint16_t sixteen[4]; + uint32_t thirtytwo[2]; + uint64_t sixtyfour[1]; + uintptr_t unintptr[1]; + void *ptr; + } PH_per; + + struct m_cb cb; +}; +#define ether_vtag PH_per.sixteen[0] +#define PH_vt PH_per +#define vt_nrecs sixteen[0] +#define tso_segsz PH_per.sixteen[1] +#define csum_phsum PH_per.sixteen[2] +#define csum_data PH_per.thirtytwo[1] + +/* + * The core of the mbuf object along with some shortcut defines for practical + * purposes. + */ +struct mbuf { // Size : 48 + /* + * Header present at the beginning of every mbuf. + * Size : 24 + */ + void *m_pbuf; /* only a single pbuf, not in chain */ + /* NB: All mbuf operations do not change the struct of pbuf. */ + struct mbuf *m_nextpkt; /* next chain in queue/record */ + uint8_t *m_data; /* location of data */ + int32_t m_len; /* amount of data in this mbuf */ + uint16_t m_headspace; /* empty space available at the head */ + uint16_t m_tailspace; /* empty space available at the tail */ + uint32_t m_type :8, /* type of data in this mbuf */ // useless now + m_flags :24; /* flags; see below */ + /*** End of the mbuf header ***/ + + struct pkthdr m_pkthdr; /* M_PKTHDR always set */ +}; + +#define MBUF_HEAD_SPACE 68 /* reserved head space in mbuf::m_buf, align to 4 */ +#define MBUF_TAIL_SPACE 16 /* reserved tail space in mbuf::m_buf, align to 4 */ + +/* + * mbuf flags of global significance and layer crossing. + * Those of only protocol/layer specific significance are to be mapped + * to M_PROTO[1-12] and cleared at layer handoff boundaries. + * NB: Limited to the lower 24 bits. + */ +#define M_EXT 0x00000001 /* has associated external storage */ +#define M_PKTHDR 0x00000002 /* start of record */ // always set +#define M_EOR 0x00000004 /* end of record */ +#define M_RDONLY 0x00000008 /* associated data is marked read-only */ +#define M_BCAST 0x00000010 /* send/received as link-level broadcast */ +#define M_MCAST 0x00000020 /* send/received as link-level multicast */ +#define M_PROMISC 0x00000040 /* packet was not for us */ +#define M_VLANTAG 0x00000080 /* ether_vtag is valid */ +#define M_FLOWID 0x00000100 /* deprecated: flowid is valid */ +#define M_NOFREE 0x00000200 /* do not free mbuf, embedded in cluster */ + +#define M_PROTO1 0x00001000 /* protocol-specific */ +#define M_PROTO2 0x00002000 /* protocol-specific */ +#define M_PROTO3 0x00004000 /* protocol-specific */ +#define M_PROTO4 0x00008000 /* protocol-specific */ +#define M_PROTO5 0x00010000 /* protocol-specific */ +#define M_PROTO6 0x00020000 /* protocol-specific */ +#define M_PROTO7 0x00040000 /* protocol-specific */ +#define M_PROTO8 0x00080000 /* protocol-specific */ +#define M_PROTO9 0x00100000 /* protocol-specific */ +#define M_PROTO10 0x00200000 /* protocol-specific */ +#define M_PROTO11 0x00400000 /* protocol-specific */ +#define M_PROTO12 0x00800000 /* protocol-specific */ + +/* + * Flags to purge when crossing layers. + */ +#define M_PROTOFLAGS \ + (M_PROTO1|M_PROTO2|M_PROTO3|M_PROTO4|M_PROTO5|M_PROTO6|M_PROTO7|M_PROTO8|\ + M_PROTO9|M_PROTO10|M_PROTO11|M_PROTO12) + +/* + * Flags preserved when copying m_pkthdr. + */ +#define M_COPYFLAGS \ + (M_PKTHDR|M_EOR|M_RDONLY|M_BCAST|M_MCAST|M_VLANTAG|M_PROMISC| \ + M_PROTOFLAGS) + +/* + * Mbuf flag description for use with printf(9) %b identifier. + */ +#define M_FLAG_BITS \ + "\20\1M_EXT\2M_PKTHDR\3M_EOR\4M_RDONLY\5M_BCAST\6M_MCAST" \ + "\7M_PROMISC\10M_VLANTAG\11M_FLOWID" +#define M_FLAG_PROTOBITS \ + "\15M_PROTO1\16M_PROTO2\17M_PROTO3\20M_PROTO4\21M_PROTO5" \ + "\22M_PROTO6\23M_PROTO7\24M_PROTO8\25M_PROTO9\26M_PROTO10" \ + "\27M_PROTO11\30M_PROTO12" +#define M_FLAG_PRINTF (M_FLAG_BITS M_FLAG_PROTOBITS) + + +/* + * mbuf types describing the content of the mbuf (including external storage). + */ +#define MT_NOTMBUF 0 /* USED INTERNALLY ONLY! Object is not mbuf */ +#define MT_DATA 1 /* dynamic (data) allocation */ +#define MT_HEADER MT_DATA /* packet header, use M_PKTHDR instead */ + +#define MT_VENDOR1 4 /* for vendor-internal use */ +#define MT_VENDOR2 5 /* for vendor-internal use */ +#define MT_VENDOR3 6 /* for vendor-internal use */ +#define MT_VENDOR4 7 /* for vendor-internal use */ + +#define MT_SONAME 8 /* socket name */ + +#define MT_EXP1 9 /* for experimental use */ +#define MT_EXP2 10 /* for experimental use */ +#define MT_EXP3 11 /* for experimental use */ +#define MT_EXP4 12 /* for experimental use */ + +#define MT_CONTROL 14 /* extra-data protocol message */ +#define MT_OOBDATA 15 /* expedited data */ +#define MT_NTYPES 16 /* number of mbuf types for mbtypes[] */ + +#define MT_NOINIT 255 /* Not a type but a flag to allocate + a non-initialized mbuf */ + +#ifdef __CONFIG_ARCH_DUAL_CORE +/* + * NB: mbuf is allocated from app core, pointer conversion MUST be done when + * transfer mbuf from app core to net core, or vice versa. + */ + +#ifdef __CONFIG_ARCH_NET_CORE +/* convert mbuf's pointers in net core, the new mbuf is used by net core */ +#define MBUF_APP2NET(m) \ + do { \ + (m) = (struct mbuf *)DUCC_APPMEM_APP2NET(m); \ + (m)->m_data = (uint8_t *)DUCC_APPMEM_APP2NET((m)->m_data); \ + } while (0) + +/* convert mbuf's pointers in net core, the new mbuf is used by app core */ +#define MBUF_NET2APP(m) \ + do { \ + (m)->m_data = (uint8_t *)DUCC_APPMEM_NET2APP((m)->m_data); \ + (m) = (struct mbuf *)DUCC_APPMEM_NET2APP(m); \ + } while (0) +#endif /* __CONFIG_ARCH_NET_CORE */ + +#ifdef __CONFIG_ARCH_APP_CORE +/* convert mbuf's pointers in app core, the new mbuf is used by net core */ +#define MBUF_APP2NET(m) \ + do { \ + (m)->m_data = (uint8_t *)DUCC_APPMEM_APP2NET((m)->m_data); \ + (m) = (struct mbuf *)DUCC_APPMEM_APP2NET(m); \ + } while (0) + +/* convert mbuf's pointers in app core, the new mbuf is used by app core */ +#define MBUF_NET2APP(m) \ + do { \ + (m) = (struct mbuf *)DUCC_APPMEM_NET2APP(m); \ + (m)->m_data = (uint8_t *)DUCC_APPMEM_NET2APP((m)->m_data); \ + } while (0) +#endif /* __CONFIG_ARCH_APP_CORE */ + +#endif /* __CONFIG_ARCH_DUAL_CORE */ + +struct mbuf *mb_get(int len, int tx); +void mb_free(struct mbuf *m); + +#if (defined(__CONFIG_ARCH_APP_CORE) || !defined(__CONFIG_ARCH_DUAL_CORE)) +struct mbuf *mb_pbuf2mbuf(void *p); +void *mb_mbuf2pbuf(struct mbuf *m); +#endif /* (defined(__CONFIG_ARCH_APP_CORE) || !defined(__CONFIG_ARCH_DUAL_CORE)) */ + +#ifdef __cplusplus +} +#endif + +#endif /* (__CONFIG_MBUF_IMPL_MODE == 1) */ +#endif /* !_SYS_MBUF_1_H_ */ diff --git a/platform/mcu/xr871/include/sys/ota.h b/platform/mcu/xr871/include/sys/ota.h new file mode 100644 index 0000000000..f7421137ae --- /dev/null +++ b/platform/mcu/xr871/include/sys/ota.h @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _SYS_OTA_H_ +#define _SYS_OTA_H_ + +#include "types.h" +#include "sys/image.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief OTA status definition + */ +typedef enum ota_status { + OTA_STATUS_OK = 0, + OTA_STATUS_ERROR = -1, +} ota_status_t; + +/** + * @brief OTA image sequence definition + */ +typedef enum ota_image { + OTA_IMAGE_1ST = 1, + OTA_IMAGE_2ND = 2, +} ota_image_t; + +/** + * @brief OTA image verification state definition + */ +typedef enum ota_state { + OTA_STATE_UNVERIFIED = 0, + OTA_STATE_VERIFIED = 1, +} ota_state_t; + +/** + * @brief OTA configuration definition + */ +typedef struct ota_cfg { + ota_image_t image; + ota_state_t state; +} ota_cfg_t; + +/** + * @brief OTA protocol definition + */ +typedef enum ota_protocol { + OTA_PROTOCOL_FILE = 0, + OTA_PROTOCOL_HTTP = 1, +} ota_protocol_t; + +/** + * @brief OTA image verification algorithm definition + */ +typedef enum ota_verify { + OTA_VERIFY_NONE = 0, + OTA_VERIFY_CRC32 = 1, + OTA_VERIFY_MD5 = 2, + OTA_VERIFY_SHA1 = 3, + OTA_VERIFY_SHA256 = 4, +} ota_verify_t; + +ota_status_t ota_init(image_ota_param_t *param); +void ota_deinit(void); + +ota_status_t ota_read_cfg(ota_cfg_t *cfg); +ota_status_t ota_write_cfg(ota_cfg_t *cfg); + +ota_status_t ota_get_image(ota_protocol_t protocol, void *url); +ota_status_t ota_verify_image(ota_verify_t verify, uint32_t *value); +void ota_reboot(void); + +#ifdef __cplusplus +} +#endif + +#endif /* _SYS_OTA_H_ */ diff --git a/platform/mcu/xr871/include/sys/param.h b/platform/mcu/xr871/include/sys/param.h new file mode 100644 index 0000000000..1ce5f943a1 --- /dev/null +++ b/platform/mcu/xr871/include/sys/param.h @@ -0,0 +1,68 @@ +/*- + * Copyright (c) 1982, 1986, 1989, 1993 + * The Regents of the University of California. All rights reserved. + * (c) UNIX System Laboratories, Inc. + * All or some portions of this file are derived from material licensed + * to the University of California by American Telephone and Telegraph + * Co. or Unix System Laboratories, Inc. and are reproduced herein with + * the permission of UNIX System Laboratories, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)param.h 8.3 (Berkeley) 4/4/95 + * $FreeBSD: releng/10.1/sys/sys/param.h 272463 2014-10-03 00:58:34Z gjb $ + */ + +#ifndef _SYS_PARAM_H_ +#define _SYS_PARAM_H_ + +#define NBBY 8 /* number of bits in a byte */ +#define NBPW sizeof(int) /* number of bytes per word (integer) */ + +/* Bit map related macros. */ +#define setbit(a,i) (((unsigned char *)(a))[(i)/NBBY] |= 1<<((i)%NBBY)) +#define clrbit(a,i) (((unsigned char *)(a))[(i)/NBBY] &= ~(1<<((i)%NBBY))) +#define isset(a,i) \ + (((const unsigned char *)(a))[(i)/NBBY] & (1<<((i)%NBBY))) +#define isclr(a,i) \ + ((((const unsigned char *)(a))[(i)/NBBY] & (1<<((i)%NBBY))) == 0) + +/* Macros for counting and rounding. */ +#ifndef howmany +#define howmany(x, y) (((x)+((y)-1))/(y)) +#endif +#define nitems(x) (sizeof((x)) / sizeof((x)[0])) +#define rounddown(x, y) (((x)/(y))*(y)) +#define rounddown2(x, y) ((x)&(~((y)-1))) /* if y is power of two */ +#define roundup(x, y) ((((x)+((y)-1))/(y))*(y)) /* to any y */ +#define roundup2(x, y) (((x)+((y)-1))&(~((y)-1))) /* if y is powers of two */ +#define powerof2(x) ((((x)-1)&(x))==0) + +/* Macros for min/max. */ +#define MIN(a,b) (((a)<(b))?(a):(b)) +#define MAX(a,b) (((a)>(b))?(a):(b)) +#define DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d)) + +#endif /* _SYS_PARAM_H_ */ diff --git a/platform/mcu/xr871/include/sys/queue.h b/platform/mcu/xr871/include/sys/queue.h new file mode 100644 index 0000000000..1f6ab00fd1 --- /dev/null +++ b/platform/mcu/xr871/include/sys/queue.h @@ -0,0 +1,694 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)queue.h 8.5 (Berkeley) 8/20/94 + * $FreeBSD: releng/10.1/sys/sys/queue.h 251887 2013-06-18 02:57:56Z lstewart $ + */ + +#ifndef _SYS_QUEUE_H_ +#define _SYS_QUEUE_H_ + +#include "sys/defs.h" + +/* + * This file defines four types of data structures: singly-linked lists, + * singly-linked tail queues, lists and tail queues. + * + * A singly-linked list is headed by a single forward pointer. The elements + * are singly linked for minimum space and pointer manipulation overhead at + * the expense of O(n) removal for arbitrary elements. New elements can be + * added to the list after an existing element or at the head of the list. + * Elements being removed from the head of the list should use the explicit + * macro for this purpose for optimum efficiency. A singly-linked list may + * only be traversed in the forward direction. Singly-linked lists are ideal + * for applications with large datasets and few or no removals or for + * implementing a LIFO queue. + * + * A singly-linked tail queue is headed by a pair of pointers, one to the + * head of the list and the other to the tail of the list. The elements are + * singly linked for minimum space and pointer manipulation overhead at the + * expense of O(n) removal for arbitrary elements. New elements can be added + * to the list after an existing element, at the head of the list, or at the + * end of the list. Elements being removed from the head of the tail queue + * should use the explicit macro for this purpose for optimum efficiency. + * A singly-linked tail queue may only be traversed in the forward direction. + * Singly-linked tail queues are ideal for applications with large datasets + * and few or no removals or for implementing a FIFO queue. + * + * A list is headed by a single forward pointer (or an array of forward + * pointers for a hash table header). The elements are doubly linked + * so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list before + * or after an existing element or at the head of the list. A list + * may be traversed in either direction. + * + * A tail queue is headed by a pair of pointers, one to the head of the + * list and the other to the tail of the list. The elements are doubly + * linked so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list before or + * after an existing element, at the head of the list, or at the end of + * the list. A tail queue may be traversed in either direction. + * + * For details on the use of these macros, see the queue(3) manual page. + * + * + * SLIST LIST STAILQ TAILQ + * _HEAD + + + + + * _HEAD_INITIALIZER + + + + + * _ENTRY + + + + + * _INIT + + + + + * _EMPTY + + + + + * _FIRST + + + + + * _NEXT + + + + + * _PREV - + - + + * _LAST - - + + + * _FOREACH + + + + + * _FOREACH_FROM + + + + + * _FOREACH_SAFE + + + + + * _FOREACH_FROM_SAFE + + + + + * _FOREACH_REVERSE - - - + + * _FOREACH_REVERSE_FROM - - - + + * _FOREACH_REVERSE_SAFE - - - + + * _FOREACH_REVERSE_FROM_SAFE - - - + + * _INSERT_HEAD + + + + + * _INSERT_BEFORE - + - + + * _INSERT_AFTER + + + + + * _INSERT_TAIL - - + + + * _CONCAT - - + + + * _REMOVE_AFTER + - + - + * _REMOVE_HEAD + - + - + * _REMOVE + + + + + * _SWAP + + + + + * + */ +#ifdef QUEUE_MACRO_DEBUG +/* Store the last 2 places the queue element or head was altered */ +struct qm_trace { + unsigned long lastline; + unsigned long prevline; + const char *lastfile; + const char *prevfile; +}; + +#define TRACEBUF struct qm_trace trace; +#define TRACEBUF_INITIALIZER { __FILE__, __LINE__, NULL, 0 } , +#define TRASHIT(x) do {(x) = (void *)-1;} while (0) +#define QMD_SAVELINK(name, link) void **name = (void *)&(link) + +#define QMD_TRACE_HEAD(head) do { \ + (head)->trace.prevline = (head)->trace.lastline; \ + (head)->trace.prevfile = (head)->trace.lastfile; \ + (head)->trace.lastline = __LINE__; \ + (head)->trace.lastfile = __FILE__; \ +} while (0) + +#define QMD_TRACE_ELEM(elem) do { \ + (elem)->trace.prevline = (elem)->trace.lastline; \ + (elem)->trace.prevfile = (elem)->trace.lastfile; \ + (elem)->trace.lastline = __LINE__; \ + (elem)->trace.lastfile = __FILE__; \ +} while (0) + +#else +#define QMD_TRACE_ELEM(elem) +#define QMD_TRACE_HEAD(head) +#define QMD_SAVELINK(name, link) +#define TRACEBUF +#define TRACEBUF_INITIALIZER +#define TRASHIT(x) +#endif /* QUEUE_MACRO_DEBUG */ + +/* + * Singly-linked List declarations. + */ +#define SLIST_HEAD(name, type) \ +struct name { \ + struct type *slh_first; /* first element */ \ +} + +#define SLIST_HEAD_INITIALIZER(head) \ + { NULL } + +#define SLIST_ENTRY(type) \ +struct { \ + struct type *sle_next; /* next element */ \ +} + +/* + * Singly-linked List functions. + */ +#define SLIST_EMPTY(head) ((head)->slh_first == NULL) + +#define SLIST_FIRST(head) ((head)->slh_first) + +#define SLIST_FOREACH(var, head, field) \ + for ((var) = SLIST_FIRST((head)); \ + (var); \ + (var) = SLIST_NEXT((var), field)) + +#define SLIST_FOREACH_FROM(var, head, field) \ + for ((var) = ((var) ? (var) : SLIST_FIRST((head))); \ + (var); \ + (var) = SLIST_NEXT((var), field)) + +#define SLIST_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = SLIST_FIRST((head)); \ + (var) && ((tvar) = SLIST_NEXT((var), field), 1); \ + (var) = (tvar)) + +#define SLIST_FOREACH_FROM_SAFE(var, head, field, tvar) \ + for ((var) = ((var) ? (var) : SLIST_FIRST((head))); \ + (var) && ((tvar) = SLIST_NEXT((var), field), 1); \ + (var) = (tvar)) + +#define SLIST_FOREACH_PREVPTR(var, varp, head, field) \ + for ((varp) = &SLIST_FIRST((head)); \ + ((var) = *(varp)) != NULL; \ + (varp) = &SLIST_NEXT((var), field)) + +#define SLIST_INIT(head) do { \ + SLIST_FIRST((head)) = NULL; \ +} while (0) + +#define SLIST_INSERT_AFTER(slistelm, elm, field) do { \ + SLIST_NEXT((elm), field) = SLIST_NEXT((slistelm), field); \ + SLIST_NEXT((slistelm), field) = (elm); \ +} while (0) + +#define SLIST_INSERT_HEAD(head, elm, field) do { \ + SLIST_NEXT((elm), field) = SLIST_FIRST((head)); \ + SLIST_FIRST((head)) = (elm); \ +} while (0) + +#define SLIST_NEXT(elm, field) ((elm)->field.sle_next) + +#define SLIST_REMOVE(head, elm, type, field) do { \ + QMD_SAVELINK(oldnext, (elm)->field.sle_next); \ + if (SLIST_FIRST((head)) == (elm)) { \ + SLIST_REMOVE_HEAD((head), field); \ + } \ + else { \ + struct type *curelm = SLIST_FIRST((head)); \ + while (SLIST_NEXT(curelm, field) != (elm)) \ + curelm = SLIST_NEXT(curelm, field); \ + SLIST_REMOVE_AFTER(curelm, field); \ + } \ + TRASHIT(*oldnext); \ +} while (0) + +#define SLIST_REMOVE_AFTER(elm, field) do { \ + SLIST_NEXT(elm, field) = \ + SLIST_NEXT(SLIST_NEXT(elm, field), field); \ +} while (0) + +#define SLIST_REMOVE_HEAD(head, field) do { \ + SLIST_FIRST((head)) = SLIST_NEXT(SLIST_FIRST((head)), field); \ +} while (0) + +#define SLIST_SWAP(head1, head2, type) do { \ + struct type *swap_first = SLIST_FIRST(head1); \ + SLIST_FIRST(head1) = SLIST_FIRST(head2); \ + SLIST_FIRST(head2) = swap_first; \ +} while (0) + +/* + * Singly-linked Tail queue declarations. + */ +#define STAILQ_HEAD(name, type) \ +struct name { \ + struct type *stqh_first;/* first element */ \ + struct type **stqh_last;/* addr of last next element */ \ +} + +#define STAILQ_HEAD_INITIALIZER(head) \ + { NULL, &(head).stqh_first } + +#define STAILQ_ENTRY(type) \ +struct { \ + struct type *stqe_next; /* next element */ \ +} + +/* + * Singly-linked Tail queue functions. + */ +#define STAILQ_CONCAT(head1, head2) do { \ + if (!STAILQ_EMPTY((head2))) { \ + *(head1)->stqh_last = (head2)->stqh_first; \ + (head1)->stqh_last = (head2)->stqh_last; \ + STAILQ_INIT((head2)); \ + } \ +} while (0) + +#define STAILQ_EMPTY(head) ((head)->stqh_first == NULL) + +#define STAILQ_FIRST(head) ((head)->stqh_first) + +#define STAILQ_FOREACH(var, head, field) \ + for((var) = STAILQ_FIRST((head)); \ + (var); \ + (var) = STAILQ_NEXT((var), field)) + +#define STAILQ_FOREACH_FROM(var, head, field) \ + for ((var) = ((var) ? (var) : STAILQ_FIRST((head))); \ + (var); \ + (var) = STAILQ_NEXT((var), field)) + +#define STAILQ_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = STAILQ_FIRST((head)); \ + (var) && ((tvar) = STAILQ_NEXT((var), field), 1); \ + (var) = (tvar)) + +#define STAILQ_FOREACH_FROM_SAFE(var, head, field, tvar) \ + for ((var) = ((var) ? (var) : STAILQ_FIRST((head))); \ + (var) && ((tvar) = STAILQ_NEXT((var), field), 1); \ + (var) = (tvar)) + +#define STAILQ_INIT(head) do { \ + STAILQ_FIRST((head)) = NULL; \ + (head)->stqh_last = &STAILQ_FIRST((head)); \ +} while (0) + +#define STAILQ_INSERT_AFTER(head, tqelm, elm, field) do { \ + if ((STAILQ_NEXT((elm), field) = STAILQ_NEXT((tqelm), field)) == NULL)\ + (head)->stqh_last = &STAILQ_NEXT((elm), field); \ + STAILQ_NEXT((tqelm), field) = (elm); \ +} while (0) + +#define STAILQ_INSERT_HEAD(head, elm, field) do { \ + if ((STAILQ_NEXT((elm), field) = STAILQ_FIRST((head))) == NULL) \ + (head)->stqh_last = &STAILQ_NEXT((elm), field); \ + STAILQ_FIRST((head)) = (elm); \ +} while (0) + +#define STAILQ_INSERT_TAIL(head, elm, field) do { \ + STAILQ_NEXT((elm), field) = NULL; \ + *(head)->stqh_last = (elm); \ + (head)->stqh_last = &STAILQ_NEXT((elm), field); \ +} while (0) + +#define STAILQ_LAST(head, type, field) \ + (STAILQ_EMPTY((head)) ? NULL : \ + __containerof((head)->stqh_last, struct type, field.stqe_next)) + +#define STAILQ_NEXT(elm, field) ((elm)->field.stqe_next) + +#define STAILQ_REMOVE(head, elm, type, field) do { \ + QMD_SAVELINK(oldnext, (elm)->field.stqe_next); \ + if (STAILQ_FIRST((head)) == (elm)) { \ + STAILQ_REMOVE_HEAD((head), field); \ + } \ + else { \ + struct type *curelm = STAILQ_FIRST((head)); \ + while (STAILQ_NEXT(curelm, field) != (elm)) \ + curelm = STAILQ_NEXT(curelm, field); \ + STAILQ_REMOVE_AFTER(head, curelm, field); \ + } \ + TRASHIT(*oldnext); \ +} while (0) + +#define STAILQ_REMOVE_AFTER(head, elm, field) do { \ + if ((STAILQ_NEXT(elm, field) = \ + STAILQ_NEXT(STAILQ_NEXT(elm, field), field)) == NULL) \ + (head)->stqh_last = &STAILQ_NEXT((elm), field); \ +} while (0) + +#define STAILQ_REMOVE_HEAD(head, field) do { \ + if ((STAILQ_FIRST((head)) = \ + STAILQ_NEXT(STAILQ_FIRST((head)), field)) == NULL) \ + (head)->stqh_last = &STAILQ_FIRST((head)); \ +} while (0) + +#define STAILQ_SWAP(head1, head2, type) do { \ + struct type *swap_first = STAILQ_FIRST(head1); \ + struct type **swap_last = (head1)->stqh_last; \ + STAILQ_FIRST(head1) = STAILQ_FIRST(head2); \ + (head1)->stqh_last = (head2)->stqh_last; \ + STAILQ_FIRST(head2) = swap_first; \ + (head2)->stqh_last = swap_last; \ + if (STAILQ_EMPTY(head1)) \ + (head1)->stqh_last = &STAILQ_FIRST(head1); \ + if (STAILQ_EMPTY(head2)) \ + (head2)->stqh_last = &STAILQ_FIRST(head2); \ +} while (0) + + +/* + * List declarations. + */ +#define LIST_HEAD(name, type) \ +struct name { \ + struct type *lh_first; /* first element */ \ +} + +#define LIST_HEAD_INITIALIZER(head) \ + { NULL } + +#define LIST_ENTRY(type) \ +struct { \ + struct type *le_next; /* next element */ \ + struct type **le_prev; /* address of previous next element */ \ +} + +/* + * List functions. + */ + +#if (defined(_KERNEL) && defined(INVARIANTS)) +#define QMD_LIST_CHECK_HEAD(head, field) do { \ + if (LIST_FIRST((head)) != NULL && \ + LIST_FIRST((head))->field.le_prev != \ + &LIST_FIRST((head))) \ + panic("Bad list head %p first->prev != head", (head)); \ +} while (0) + +#define QMD_LIST_CHECK_NEXT(elm, field) do { \ + if (LIST_NEXT((elm), field) != NULL && \ + LIST_NEXT((elm), field)->field.le_prev != \ + &((elm)->field.le_next)) \ + panic("Bad link elm %p next->prev != elm", (elm)); \ +} while (0) + +#define QMD_LIST_CHECK_PREV(elm, field) do { \ + if (*(elm)->field.le_prev != (elm)) \ + panic("Bad link elm %p prev->next != elm", (elm)); \ +} while (0) +#else +#define QMD_LIST_CHECK_HEAD(head, field) +#define QMD_LIST_CHECK_NEXT(elm, field) +#define QMD_LIST_CHECK_PREV(elm, field) +#endif /* (_KERNEL && INVARIANTS) */ + +#define LIST_EMPTY(head) ((head)->lh_first == NULL) + +#define LIST_FIRST(head) ((head)->lh_first) + +#define LIST_FOREACH(var, head, field) \ + for ((var) = LIST_FIRST((head)); \ + (var); \ + (var) = LIST_NEXT((var), field)) + +#define LIST_FOREACH_FROM(var, head, field) \ + for ((var) = ((var) ? (var) : LIST_FIRST((head))); \ + (var); \ + (var) = LIST_NEXT((var), field)) + +#define LIST_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = LIST_FIRST((head)); \ + (var) && ((tvar) = LIST_NEXT((var), field), 1); \ + (var) = (tvar)) + +#define LIST_FOREACH_FROM_SAFE(var, head, field, tvar) \ + for ((var) = ((var) ? (var) : LIST_FIRST((head))); \ + (var) && ((tvar) = LIST_NEXT((var), field), 1); \ + (var) = (tvar)) + +#define LIST_INIT(head) do { \ + LIST_FIRST((head)) = NULL; \ +} while (0) + +#define LIST_INSERT_AFTER(listelm, elm, field) do { \ + QMD_LIST_CHECK_NEXT(listelm, field); \ + if ((LIST_NEXT((elm), field) = LIST_NEXT((listelm), field)) != NULL)\ + LIST_NEXT((listelm), field)->field.le_prev = \ + &LIST_NEXT((elm), field); \ + LIST_NEXT((listelm), field) = (elm); \ + (elm)->field.le_prev = &LIST_NEXT((listelm), field); \ +} while (0) + +#define LIST_INSERT_BEFORE(listelm, elm, field) do { \ + QMD_LIST_CHECK_PREV(listelm, field); \ + (elm)->field.le_prev = (listelm)->field.le_prev; \ + LIST_NEXT((elm), field) = (listelm); \ + *(listelm)->field.le_prev = (elm); \ + (listelm)->field.le_prev = &LIST_NEXT((elm), field); \ +} while (0) + +#define LIST_INSERT_HEAD(head, elm, field) do { \ + QMD_LIST_CHECK_HEAD((head), field); \ + if ((LIST_NEXT((elm), field) = LIST_FIRST((head))) != NULL) \ + LIST_FIRST((head))->field.le_prev = &LIST_NEXT((elm), field);\ + LIST_FIRST((head)) = (elm); \ + (elm)->field.le_prev = &LIST_FIRST((head)); \ +} while (0) + +#define LIST_NEXT(elm, field) ((elm)->field.le_next) + +#define LIST_PREV(elm, head, type, field) \ + ((elm)->field.le_prev == &LIST_FIRST((head)) ? NULL : \ + __containerof((elm)->field.le_prev, struct type, field.le_next)) + +#define LIST_REMOVE(elm, field) do { \ + QMD_SAVELINK(oldnext, (elm)->field.le_next); \ + QMD_SAVELINK(oldprev, (elm)->field.le_prev); \ + QMD_LIST_CHECK_NEXT(elm, field); \ + QMD_LIST_CHECK_PREV(elm, field); \ + if (LIST_NEXT((elm), field) != NULL) \ + LIST_NEXT((elm), field)->field.le_prev = \ + (elm)->field.le_prev; \ + *(elm)->field.le_prev = LIST_NEXT((elm), field); \ + TRASHIT(*oldnext); \ + TRASHIT(*oldprev); \ +} while (0) + +#define LIST_SWAP(head1, head2, type, field) do { \ + struct type *swap_tmp = LIST_FIRST((head1)); \ + LIST_FIRST((head1)) = LIST_FIRST((head2)); \ + LIST_FIRST((head2)) = swap_tmp; \ + if ((swap_tmp = LIST_FIRST((head1))) != NULL) \ + swap_tmp->field.le_prev = &LIST_FIRST((head1)); \ + if ((swap_tmp = LIST_FIRST((head2))) != NULL) \ + swap_tmp->field.le_prev = &LIST_FIRST((head2)); \ +} while (0) + +/* + * Tail queue declarations. + */ +#define TAILQ_HEAD(name, type) \ +struct name { \ + struct type *tqh_first; /* first element */ \ + struct type **tqh_last; /* addr of last next element */ \ + TRACEBUF \ +} + +#define TAILQ_HEAD_INITIALIZER(head) \ + { NULL, &(head).tqh_first, TRACEBUF_INITIALIZER } + +#define TAILQ_ENTRY(type) \ +struct { \ + struct type *tqe_next; /* next element */ \ + struct type **tqe_prev; /* address of previous next element */ \ + TRACEBUF \ +} + +/* + * Tail queue functions. + */ +#if (defined(_KERNEL) && defined(INVARIANTS)) +#define QMD_TAILQ_CHECK_HEAD(head, field) do { \ + if (!TAILQ_EMPTY(head) && \ + TAILQ_FIRST((head))->field.tqe_prev != \ + &TAILQ_FIRST((head))) \ + panic("Bad tailq head %p first->prev != head", (head)); \ +} while (0) + +#define QMD_TAILQ_CHECK_TAIL(head, field) do { \ + if (*(head)->tqh_last != NULL) \ + panic("Bad tailq NEXT(%p->tqh_last) != NULL", (head)); \ +} while (0) + +#define QMD_TAILQ_CHECK_NEXT(elm, field) do { \ + if (TAILQ_NEXT((elm), field) != NULL && \ + TAILQ_NEXT((elm), field)->field.tqe_prev != \ + &((elm)->field.tqe_next)) \ + panic("Bad link elm %p next->prev != elm", (elm)); \ +} while (0) + +#define QMD_TAILQ_CHECK_PREV(elm, field) do { \ + if (*(elm)->field.tqe_prev != (elm)) \ + panic("Bad link elm %p prev->next != elm", (elm)); \ +} while (0) +#else +#define QMD_TAILQ_CHECK_HEAD(head, field) +#define QMD_TAILQ_CHECK_TAIL(head, headname) +#define QMD_TAILQ_CHECK_NEXT(elm, field) +#define QMD_TAILQ_CHECK_PREV(elm, field) +#endif /* (_KERNEL && INVARIANTS) */ + +#define TAILQ_CONCAT(head1, head2, field) do { \ + if (!TAILQ_EMPTY(head2)) { \ + *(head1)->tqh_last = (head2)->tqh_first; \ + (head2)->tqh_first->field.tqe_prev = (head1)->tqh_last; \ + (head1)->tqh_last = (head2)->tqh_last; \ + TAILQ_INIT((head2)); \ + QMD_TRACE_HEAD(head1); \ + QMD_TRACE_HEAD(head2); \ + } \ +} while (0) + +#define TAILQ_EMPTY(head) ((head)->tqh_first == NULL) + +#define TAILQ_FIRST(head) ((head)->tqh_first) + +#define TAILQ_FOREACH(var, head, field) \ + for ((var) = TAILQ_FIRST((head)); \ + (var); \ + (var) = TAILQ_NEXT((var), field)) + +#define TAILQ_FOREACH_FROM(var, head, field) \ + for ((var) = ((var) ? (var) : TAILQ_FIRST((head))); \ + (var); \ + (var) = TAILQ_NEXT((var), field)) + +#define TAILQ_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = TAILQ_FIRST((head)); \ + (var) && ((tvar) = TAILQ_NEXT((var), field), 1); \ + (var) = (tvar)) + +#define TAILQ_FOREACH_FROM_SAFE(var, head, field, tvar) \ + for ((var) = ((var) ? (var) : TAILQ_FIRST((head))); \ + (var) && ((tvar) = TAILQ_NEXT((var), field), 1); \ + (var) = (tvar)) + +#define TAILQ_FOREACH_REVERSE(var, head, headname, field) \ + for ((var) = TAILQ_LAST((head), headname); \ + (var); \ + (var) = TAILQ_PREV((var), headname, field)) + +#define TAILQ_FOREACH_REVERSE_FROM(var, head, headname, field) \ + for ((var) = ((var) ? (var) : TAILQ_LAST((head), headname)); \ + (var); \ + (var) = TAILQ_PREV((var), headname, field)) + +#define TAILQ_FOREACH_REVERSE_SAFE(var, head, headname, field, tvar) \ + for ((var) = TAILQ_LAST((head), headname); \ + (var) && ((tvar) = TAILQ_PREV((var), headname, field), 1); \ + (var) = (tvar)) + +#define TAILQ_FOREACH_REVERSE_FROM_SAFE(var, head, headname, field, tvar) \ + for ((var) = ((var) ? (var) : TAILQ_LAST((head), headname)); \ + (var) && ((tvar) = TAILQ_PREV((var), headname, field), 1); \ + (var) = (tvar)) + +#define TAILQ_INIT(head) do { \ + TAILQ_FIRST((head)) = NULL; \ + (head)->tqh_last = &TAILQ_FIRST((head)); \ + QMD_TRACE_HEAD(head); \ +} while (0) + +#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \ + QMD_TAILQ_CHECK_NEXT(listelm, field); \ + if ((TAILQ_NEXT((elm), field) = TAILQ_NEXT((listelm), field)) != NULL)\ + TAILQ_NEXT((elm), field)->field.tqe_prev = \ + &TAILQ_NEXT((elm), field); \ + else { \ + (head)->tqh_last = &TAILQ_NEXT((elm), field); \ + QMD_TRACE_HEAD(head); \ + } \ + TAILQ_NEXT((listelm), field) = (elm); \ + (elm)->field.tqe_prev = &TAILQ_NEXT((listelm), field); \ + QMD_TRACE_ELEM(&(elm)->field); \ + QMD_TRACE_ELEM(&listelm->field); \ +} while (0) + +#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \ + QMD_TAILQ_CHECK_PREV(listelm, field); \ + (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \ + TAILQ_NEXT((elm), field) = (listelm); \ + *(listelm)->field.tqe_prev = (elm); \ + (listelm)->field.tqe_prev = &TAILQ_NEXT((elm), field); \ + QMD_TRACE_ELEM(&(elm)->field); \ + QMD_TRACE_ELEM(&listelm->field); \ +} while (0) + +#define TAILQ_INSERT_HEAD(head, elm, field) do { \ + QMD_TAILQ_CHECK_HEAD(head, field); \ + if ((TAILQ_NEXT((elm), field) = TAILQ_FIRST((head))) != NULL) \ + TAILQ_FIRST((head))->field.tqe_prev = \ + &TAILQ_NEXT((elm), field); \ + else \ + (head)->tqh_last = &TAILQ_NEXT((elm), field); \ + TAILQ_FIRST((head)) = (elm); \ + (elm)->field.tqe_prev = &TAILQ_FIRST((head)); \ + QMD_TRACE_HEAD(head); \ + QMD_TRACE_ELEM(&(elm)->field); \ +} while (0) + +#define TAILQ_INSERT_TAIL(head, elm, field) do { \ + QMD_TAILQ_CHECK_TAIL(head, field); \ + TAILQ_NEXT((elm), field) = NULL; \ + (elm)->field.tqe_prev = (head)->tqh_last; \ + *(head)->tqh_last = (elm); \ + (head)->tqh_last = &TAILQ_NEXT((elm), field); \ + QMD_TRACE_HEAD(head); \ + QMD_TRACE_ELEM(&(elm)->field); \ +} while (0) + +#define TAILQ_LAST(head, headname) \ + (*(((struct headname *)((head)->tqh_last))->tqh_last)) + +#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next) + +#define TAILQ_PREV(elm, headname, field) \ + (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last)) + +#define TAILQ_REMOVE(head, elm, field) do { \ + QMD_SAVELINK(oldnext, (elm)->field.tqe_next); \ + QMD_SAVELINK(oldprev, (elm)->field.tqe_prev); \ + QMD_TAILQ_CHECK_NEXT(elm, field); \ + QMD_TAILQ_CHECK_PREV(elm, field); \ + if ((TAILQ_NEXT((elm), field)) != NULL) \ + TAILQ_NEXT((elm), field)->field.tqe_prev = \ + (elm)->field.tqe_prev; \ + else { \ + (head)->tqh_last = (elm)->field.tqe_prev; \ + QMD_TRACE_HEAD(head); \ + } \ + *(elm)->field.tqe_prev = TAILQ_NEXT((elm), field); \ + TRASHIT(*oldnext); \ + TRASHIT(*oldprev); \ + QMD_TRACE_ELEM(&(elm)->field); \ +} while (0) + +#define TAILQ_SWAP(head1, head2, type, field) do { \ + struct type *swap_first = (head1)->tqh_first; \ + struct type **swap_last = (head1)->tqh_last; \ + (head1)->tqh_first = (head2)->tqh_first; \ + (head1)->tqh_last = (head2)->tqh_last; \ + (head2)->tqh_first = swap_first; \ + (head2)->tqh_last = swap_last; \ + if ((swap_first = (head1)->tqh_first) != NULL) \ + swap_first->field.tqe_prev = &(head1)->tqh_first; \ + else \ + (head1)->tqh_last = &(head1)->tqh_first; \ + if ((swap_first = (head2)->tqh_first) != NULL) \ + swap_first->field.tqe_prev = &(head2)->tqh_first; \ + else \ + (head2)->tqh_last = &(head2)->tqh_first; \ +} while (0) + +#endif /* !_SYS_QUEUE_H_ */ diff --git a/platform/mcu/xr871/include/sys/xr_debug.h b/platform/mcu/xr871/include/sys/xr_debug.h new file mode 100644 index 0000000000..fb9d17c10c --- /dev/null +++ b/platform/mcu/xr871/include/sys/xr_debug.h @@ -0,0 +1,355 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __XR_DEBUG_H__ +#define __XR_DEBUG_H__ + +#include "sys/xr_util.h" +#include +#include +#include +#include + +#define XR_DEBUG_ON + + +/* + * @brief Debug level + */ +#define DBG_LEVEL_MASK (0x0F) + + +#define XR_LEVEL_EMERG 0 + +#define XR_LEVEL_ALERT 1 + +#define XR_LEVEL_CRIT 2 + +#define XR_LEVEL_ERROR 3 + +#define XR_LEVEL_WARNING 4 + +#define XR_LEVEL_NOTICE 5 + +#define XR_LEVEL_INFO 6 + +#define XR_LEVEL_DEBUG 7 + +#define XR_LEVEL_ALL 0x0F + + +/* + * No expanded condition + */ +#define NOEXPAND 1 + + +/* + * module ON/OFF + */ +#define DBG_ON (1 << 4) + +#define DBG_OFF (0) + + +/* + * Always show message + */ +#define MOD_DBG_ALW_ON (DBG_ON | XR_LEVEL_ALL) + + +/************************************************************ + * XR_DEBUG INTERFACE + ************************************************************/ +#ifdef XR_DEBUG_ON + +#define XR_DEBUG_PRINT(msg, arg...) printf(msg, ##arg) + +#define XR_DEBUG_ABORT() do { \ + printf("system aborted!"); \ + sys_abort(); \ + } while (0) + + +/* + * @brief The realization of showing debug messages. + * @param module: Contained a module On/Off and a module debug level. + * @param dlevel: Debug message showed level for seal like MDEBUG. + * @param expand: Expanded condition if level param and module ON/OFF are not + * enough for developer. + * @param msg: The debug message. + * @param arg: Arguement shown in debug message which like printf arguement. + * @retval None + */ +#define _XR_DEBUG(module, dlevel, expand, msg, arg...) \ + do { \ + if ( \ + ((module) & DBG_ON) && \ + (((module) & DBG_LEVEL_MASK) >= dlevel) && \ + (expand)) { \ + XR_DEBUG_PRINT(msg, ##arg); \ + } \ + } while(0) + +/* + * @brief The realization of showing debug messages and it can't be turn off by + * module ON/OFF. + * @param module: Contained a module On/Off and a module debug level. + * @param dlevel: Debug message showed level for seal. + * @param expand: Expanded condition if level param is not enough for developer. + * @param msg: The debug message. + * @param arg: Arguement shown in debug message which like printf arguement. + * @retval None + */ +#define _XR_INFO(module, dlevel, expand, msg, arg...) \ + do { \ + if ( \ + (((int16_t)(module) & DBG_LEVEL_MASK) >= dlevel) && \ + (expand)) { \ + XR_DEBUG_PRINT(msg, ##arg); \ + } \ + } while(0) + +/* + * @brief The realization of assert debug messages shown the assert position, + * for example: "[Assert] At module_debug.h line 112 fun _MASSERT: **" + * @param module: Contained a module On/Off and a module debug level. + * @param dlevel: Debug message showed level for seal. + * @param msg: The debug message. + * @param arg: Arguement shown in debug message which like printf arguement. + * @retval None + */ +#define _XR_ASSERT(assert, module, dlevel, msg, arg...) \ + _XR_DEBUG(module, dlevel, !(assert), \ + "[Assert] At %s line %d fun %s: " msg, \ + __FILE__, __LINE__, __func__, ##arg) + +/* + * @brief The realization of assert debug messages shown the assert position, + * and abort. for example: "[Assert] At module_debug.h line 112 fun + * _MASSERT: ***" + * @param module: Contained a module On/Off and a module debug level. + * @param dlevel: Debug message showed level for seal. + * @param msg: The debug message. + * @param arg: Arguement shown in debug message which like printf arguement. + * @retval None + */ +#define _XR_ASSERT_ABORT(assert, module, dlevel, msg, arg...) \ + do { \ + if ((((int16_t)(module) & DBG_LEVEL_MASK) >= dlevel) && !(assert)) { \ + XR_DEBUG_PRINT("[Assert] At %s line %d fun %s: " msg, \ + __FILE__, __LINE__, __func__, ##arg); \ + XR_DEBUG_ABORT(); \ + } \ + } while(0) + + +/* + * @brief A level debug message + * @param module: Contained a module On/Off and a module debug level. + * @param expand: Expanded condition if level param and module ON/OFF are not + * enough for developer. + * @param msg: The debug message. + * @param arg: Arguement shown in debug message which like printf arguement. + * @retval None + */ +#define XR_ERROR(module, expand, msg, arg...) \ + _XR_DEBUG(module, XR_LEVEL_ERROR, expand, msg, ##arg) + +#define XR_ALERT(module, expand, msg, arg...) \ + _XR_DEBUG(module, XR_LEVEL_ALERT, expand, msg, ##arg) + +#define XR_CRIT(module, expand, msg, arg...) \ + _XR_DEBUG(module, XR_LEVEL_CRIT, expand, msg, ##arg) + +#define XR_EMERG(module, expand, msg, arg...) \ + _XR_DEBUG(module, XR_LEVEL_EMERG, XR_xpand, msg, ##arg) + +#define XR_WARN(module, expand, msg, arg...) \ + _XR_DEBUG(module, XR_LEVEL_WARNING, expand, msg, ##arg) + +#define XR_NOTICE(module, expand, msg, arg...) \ + _XR_DEBUG(module, XR_LEVEL_NOTICE, expand, msg, ##arg) + +#define XR_INFO(module, expand, msg, arg...) \ + _XR_DEBUG(module, XR_LEVEL_INFO, expand, msg, ##arg) + +#define XR_DEBUG(module, expand, msg, arg...) \ + _XR_DEBUG(module, XR_LEVEL_DEBUG, expand, msg, ##arg) + + +/* + * @brief Assert a full debug message with position(file, line, etc.) without level. + * for example: "[Assert] At module_debug.h line 112 fun _MASSERT: ***" + * @param assert: Debug condition + * @param module: Contained a module On/Off at least. + * @param msg: The debug message. + * @param arg: Arguement shown in debug message which like printf arguement. + * @retval None + */ +#define XR_ASSERT(assert, module, msg, arg...) \ + _XR_ASSERT(assert, module, XR_LEVEL_ALL, msg, ##arg) + +#define XR_ASSERT_ABORT(assert, module, msg, arg...) \ + _XR_ASSERT_ABORT(assert, module, XR_LEVEL_ALL, msg, ##arg) + +/* + * @brief Assert a full debug message with position(file, line, etc.) and + * error number without level. for example: + * "[Assert] At module_debug.h line 112 fun _MASSERT: condition p != NULL is fault. errno is 115." + * @param condition: It will assert a message if condition is fault. + * @retval Nuon + */ +#ifndef assert +#define assert(condition) XR_ASSERT(condition, MOD_DBG_ALW_ON, "condition %s is fault. errno is %d.\n", #condition, xr_thread_errno) +#endif + +/* +// THIS REALIZATION DO NOT SEAL +#define XR_ASSERT(assert, module, msg, arg...) \ + _XR_ASSERT(assert, module, XR_LEVEL_ALL, "[%s]" msg, #module, ##arg) + +#define XR_ASSERT_ABORT(assert, module, msg, arg...) \ + _XR_ASSERT_ABORT(assert, module, XR_LEVEL_ALL, "[%s]" msg, #module, ##arg) +*/ + + +/* + * @brief notify the function entry and exit/return in the debug level + * @param module: Contained a module On/Off at least. + * @param mname: module name in string + * @param ret: return value + * @retval None + */ +#define XR_ENTRY(module, mname) \ + XR_DEBUG(module, NOEXPAND, mname "entry %s().\n", __func__) + +#define XR_RET(module, mname, ret) \ + XR_DEBUG(module, NOEXPAND, mname "exit %s() with return %d.\n", __func__, ret) + +#define XR_RET_NOVAL(module, mname) \ + XR_DEBUG(module, NOEXPAND, mname "exit %s().\n", __func__) + + +#else /* MDEBUG_ON */ + +#define XR_DEBUG_PRINT(msg, arg...) + +#define XR_DEBUG_ABORT() + + +#define _XR_DEBUG(module, dlevel, expand, msg, arg...) + +#define _XR_INFO(module, dlevel, expand, msg, arg...) + +#define _XR_ASSERT(assert, module, dlevel, msg, arg...) + +#define _XR_ASSERT_ABORT(assert, module, dlevel, msg, arg...) + + +#define XR_ERROR(module, expand, msg, arg...) + +#define XR_ALERT(module, expand, msg, arg...) + +#define XR_CRIT(module, expand, msg, arg...) + +#define XR_EMERG(module, expand, msg, arg...) + +#define XR_WARN(module, expand, msg, arg...) + +#define XR_NOTICE(module, expand, msg, arg...) + +#define XR_INFO(module, expand, msg, arg...) + +#define XR_DEBUG(module, expand, msg, arg...) + + +#define XR_ASSERT(assert, module, msg, arg...) + +#define XR_ASSERT_ABORT(assert, module, msg, arg...) + +#ifndef assert +#define assert(condition) +#endif + + +#define XR_ENTRY(module, mname) + +#define XR_RET(module, mname, ret) + +#define XR_RET_NOVAL(module, mname) + +#endif /* XR_DEBUG_ON */ + +/* +//$ Example of xr_debug from mqtt $ + +#define MQTT_MODULE (DBG_ON | XR_LEVEL_DEBUG) + +#ifdef MOTT_ASSERT_ON +#define MQTT_ASSERT(assert, msg, arg...) XR_ALERT(MOD_DBG_ALW_ON, (assert), "[MQTT assert] "msg, ##arg) +#else +#define MQTT_ASSERT(assert, msg, arg...) +#endif + +#ifdef MQTT_DBG_ON + +#define MQTT_INFO(msg, arg...) XR_INFO(MQTT_MODULE, NOEXPAND, "[MQTT info] " msg, ##arg) + +#define MQTT_WARN(msg, arg...) XR_WARN(MQTT_MODULE, NOEXPAND, "[MQTT warning] " msg, ##arg) + +#define MQTT_DEBUG(msg, arg...) XR_DEBUG(MQTT_MODULE, NOEXPAND, "[MQTT debug] " msg, ##arg) + + + +#define MQTT_ENTRY() XR_ENTRY(MQTT_MODULE, "[MQTT entry] ") + +#define MQTT_EXIT(ret) XR_RET(MQTT_MODULE, "[MQTT return] ", ret) + +#else + +#define MQTT_INFO(msg, arg...) + +#define MQTT_WARN(msg, arg...) + +#define MQTT_DEBUG(msg, arg...) + + +#define MQTT_ENTRY() + +#define MQTT_EXIT(ret) + +#endif + +*/ + +extern void print_hex_dump_bytes(const void *addr, unsigned int len); +extern void hex_dump_bytes(const void *addr, unsigned int len); + +#endif /* __XR_DEBUG_H__ */ diff --git a/platform/mcu/xr871/include/sys/xr_util.h b/platform/mcu/xr871/include/sys/xr_util.h new file mode 100644 index 0000000000..bb3a3fd68e --- /dev/null +++ b/platform/mcu/xr871/include/sys/xr_util.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _SYS_XR_UTIL_H_ +#define _SYS_XR_UTIL_H_ + +#include "compiler.h" +#include "sys/interrupt.h" + +#if defined(__CC_ARM) + #define arch_breakpoint(value) __breakpoint(value) +#elif defined(__GNUC__) + #define arch_breakpoint(value) __asm volatile ("bkpt "#value) +#endif + +#define sys_abort() \ + do { \ + arch_fiq_disable(); \ + arch_breakpoint(0); \ + } while (0) + +#endif /* _SYS_XR_UTIL_H_ */ diff --git a/platform/mcu/xr871/include/types.h b/platform/mcu/xr871/include/types.h new file mode 100644 index 0000000000..2c09480add --- /dev/null +++ b/platform/mcu/xr871/include/types.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _TYPES_H_ +#define _TYPES_H_ + +#include +#include + +#endif /* _TYPES_H_ */ diff --git a/platform/mcu/xr871/include/version.h b/platform/mcu/xr871/include/version.h new file mode 100644 index 0000000000..ef2f91361c --- /dev/null +++ b/platform/mcu/xr871/include/version.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _VERSION_H_ +#define _VERSION_H_ + +#define SDK_VERSION_STR "1.0.1" + +#define SDK_VERSION_MAJOR 1 +#define SDK_VERSION_MINOR 0 +#define SDK_VERSION_PATCH 1 + +#define SDK_VERSION_NUM ((SDK_VERSION_MAJOR << 16) | \ + (SDK_VERSION_MINOR << 8) | \ + (SDK_VERSION_PATCH)) + +#endif /* _VERSION_H_ */ diff --git a/platform/mcu/xr871/include/xz/decompress.h b/platform/mcu/xr871/include/xz/decompress.h new file mode 100644 index 0000000000..90453518b0 --- /dev/null +++ b/platform/mcu/xr871/include/xz/decompress.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#ifndef __DECOMPRESS_H__ +#define __DECOMPRESS_H__ + +#include "xz.h" + +int xz_uncompress_init(struct xz_buf *stream); +int xz_uncompress_stream(struct xz_buf *stream, uint8_t *sbuf, uint32_t slen, + uint8_t *dbuf, uint32_t dlen, uint32_t *decomp_len); +void xz_uncompress_end(); + +uint32_t xz_index_len(uint8_t *stream_footer); +uint32_t xz_file_uncompress_size(uint8_t *index, uint32_t len); + + +#endif /* __DECOMPRESS_H__ */ diff --git a/platform/mcu/xr871/include/xz/xz.h b/platform/mcu/xr871/include/xz/xz.h new file mode 100644 index 0000000000..d94d68bc42 --- /dev/null +++ b/platform/mcu/xr871/include/xz/xz.h @@ -0,0 +1,286 @@ +/* + * XZ decompressor + * + * Authors: Lasse Collin + * Igor Pavlov + * + * This file has been put into the public domain. + * You can do whatever you want with this file. + */ + +#ifndef XZ_H +#define XZ_H + +#ifdef __KERNEL__ +# include +# include +# include +#else +# include +# include +# include +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* In Linux, this is used to make extern functions static when needed. */ +#ifndef XZ_EXTERN +# define XZ_EXTERN extern +#endif + +/** + * enum xz_mode - Operation mode + * + * @XZ_SINGLE: Single-call mode. This uses less RAM than + * than multi-call modes, because the LZMA2 + * dictionary doesn't need to be allocated as + * part of the decoder state. All required data + * structures are allocated at initialization, + * so xz_dec_run() cannot return XZ_MEM_ERROR. + * @XZ_PREALLOC: Multi-call mode with preallocated LZMA2 + * dictionary buffer. All data structures are + * allocated at initialization, so xz_dec_run() + * cannot return XZ_MEM_ERROR. + * @XZ_DYNALLOC: Multi-call mode. The LZMA2 dictionary is + * allocated once the required size has been + * parsed from the stream headers. If the + * allocation fails, xz_dec_run() will return + * XZ_MEM_ERROR. + * + * It is possible to enable support only for a subset of the above + * modes at compile time by defining XZ_DEC_SINGLE, XZ_DEC_PREALLOC, + * or XZ_DEC_DYNALLOC. The xz_dec kernel module is always compiled + * with support for all operation modes, but the preboot code may + * be built with fewer features to minimize code size. + */ +enum xz_mode { + XZ_SINGLE, + XZ_PREALLOC, + XZ_DYNALLOC +}; + +/** + * enum xz_ret - Return codes + * @XZ_OK: Everything is OK so far. More input or more + * output space is required to continue. This + * return code is possible only in multi-call mode + * (XZ_PREALLOC or XZ_DYNALLOC). + * @XZ_STREAM_END: Operation finished successfully. + * @XZ_UNSUPPORTED_CHECK: Integrity check type is not supported. Decoding + * is still possible in multi-call mode by simply + * calling xz_dec_run() again. + * Note that this return value is used only if + * XZ_DEC_ANY_CHECK was defined at build time, + * which is not used in the kernel. Unsupported + * check types return XZ_OPTIONS_ERROR if + * XZ_DEC_ANY_CHECK was not defined at build time. + * @XZ_MEM_ERROR: Allocating memory failed. This return code is + * possible only if the decoder was initialized + * with XZ_DYNALLOC. The amount of memory that was + * tried to be allocated was no more than the + * dict_max argument given to xz_dec_init(). + * @XZ_MEMLIMIT_ERROR: A bigger LZMA2 dictionary would be needed than + * allowed by the dict_max argument given to + * xz_dec_init(). This return value is possible + * only in multi-call mode (XZ_PREALLOC or + * XZ_DYNALLOC); the single-call mode (XZ_SINGLE) + * ignores the dict_max argument. + * @XZ_FORMAT_ERROR: File format was not recognized (wrong magic + * bytes). + * @XZ_OPTIONS_ERROR: This implementation doesn't support the requested + * compression options. In the decoder this means + * that the header CRC32 matches, but the header + * itself specifies something that we don't support. + * @XZ_DATA_ERROR: Compressed data is corrupt. + * @XZ_BUF_ERROR: Cannot make any progress. Details are slightly + * different between multi-call and single-call + * mode; more information below. + * + * In multi-call mode, XZ_BUF_ERROR is returned when two consecutive calls + * to XZ code cannot consume any input and cannot produce any new output. + * This happens when there is no new input available, or the output buffer + * is full while at least one output byte is still pending. Assuming your + * code is not buggy, you can get this error only when decoding a compressed + * stream that is truncated or otherwise corrupt. + * + * In single-call mode, XZ_BUF_ERROR is returned only when the output buffer + * is too small or the compressed input is corrupt in a way that makes the + * decoder produce more output than the caller expected. When it is + * (relatively) clear that the compressed input is truncated, XZ_DATA_ERROR + * is used instead of XZ_BUF_ERROR. + */ +enum xz_ret { + XZ_OK, + XZ_STREAM_END, + XZ_UNSUPPORTED_CHECK, + XZ_MEM_ERROR, + XZ_MEMLIMIT_ERROR, + XZ_FORMAT_ERROR, + XZ_OPTIONS_ERROR, + XZ_DATA_ERROR, + XZ_BUF_ERROR +}; + +/** + * struct xz_buf - Passing input and output buffers to XZ code + * @in: Beginning of the input buffer. This may be NULL if and only + * if in_pos is equal to in_size. + * @in_pos: Current position in the input buffer. This must not exceed + * in_size. + * @in_size: Size of the input buffer + * @out: Beginning of the output buffer. This may be NULL if and only + * if out_pos is equal to out_size. + * @out_pos: Current position in the output buffer. This must not exceed + * out_size. + * @out_size: Size of the output buffer + * + * Only the contents of the output buffer from out[out_pos] onward, and + * the variables in_pos and out_pos are modified by the XZ code. + */ +struct xz_buf { + const uint8_t *in; + size_t in_pos; + size_t in_size; + + uint8_t *out; + size_t out_pos; + size_t out_size; +}; + +/** + * struct xz_dec - Opaque type to hold the XZ decoder state + */ +struct xz_dec; + +/** + * xz_dec_init() - Allocate and initialize a XZ decoder state + * @mode: Operation mode + * @dict_max: Maximum size of the LZMA2 dictionary (history buffer) for + * multi-call decoding. This is ignored in single-call mode + * (mode == XZ_SINGLE). LZMA2 dictionary is always 2^n bytes + * or 2^n + 2^(n-1) bytes (the latter sizes are less common + * in practice), so other values for dict_max don't make sense. + * In the kernel, dictionary sizes of 64 KiB, 128 KiB, 256 KiB, + * 512 KiB, and 1 MiB are probably the only reasonable values, + * except for kernel and initramfs images where a bigger + * dictionary can be fine and useful. + * + * Single-call mode (XZ_SINGLE): xz_dec_run() decodes the whole stream at + * once. The caller must provide enough output space or the decoding will + * fail. The output space is used as the dictionary buffer, which is why + * there is no need to allocate the dictionary as part of the decoder's + * internal state. + * + * Because the output buffer is used as the workspace, streams encoded using + * a big dictionary are not a problem in single-call mode. It is enough that + * the output buffer is big enough to hold the actual uncompressed data; it + * can be smaller than the dictionary size stored in the stream headers. + * + * Multi-call mode with preallocated dictionary (XZ_PREALLOC): dict_max bytes + * of memory is preallocated for the LZMA2 dictionary. This way there is no + * risk that xz_dec_run() could run out of memory, since xz_dec_run() will + * never allocate any memory. Instead, if the preallocated dictionary is too + * small for decoding the given input stream, xz_dec_run() will return + * XZ_MEMLIMIT_ERROR. Thus, it is important to know what kind of data will be + * decoded to avoid allocating excessive amount of memory for the dictionary. + * + * Multi-call mode with dynamically allocated dictionary (XZ_DYNALLOC): + * dict_max specifies the maximum allowed dictionary size that xz_dec_run() + * may allocate once it has parsed the dictionary size from the stream + * headers. This way excessive allocations can be avoided while still + * limiting the maximum memory usage to a sane value to prevent running the + * system out of memory when decompressing streams from untrusted sources. + * + * On success, xz_dec_init() returns a pointer to struct xz_dec, which is + * ready to be used with xz_dec_run(). If memory allocation fails, + * xz_dec_init() returns NULL. + */ +XZ_EXTERN struct xz_dec *xz_dec_init(enum xz_mode mode, uint32_t dict_max); + +/** + * xz_dec_run() - Run the XZ decoder + * @s: Decoder state allocated using xz_dec_init() + * @b: Input and output buffers + * + * The possible return values depend on build options and operation mode. + * See enum xz_ret for details. + * + * Note that if an error occurs in single-call mode (return value is not + * XZ_STREAM_END), b->in_pos and b->out_pos are not modified and the + * contents of the output buffer from b->out[b->out_pos] onward are + * undefined. This is true even after XZ_BUF_ERROR, because with some filter + * chains, there may be a second pass over the output buffer, and this pass + * cannot be properly done if the output buffer is truncated. Thus, you + * cannot give the single-call decoder a too small buffer and then expect to + * get that amount valid data from the beginning of the stream. You must use + * the multi-call decoder if you don't want to uncompress the whole stream. + */ +XZ_EXTERN enum xz_ret xz_dec_run(struct xz_dec *s, struct xz_buf *b); + +/** + * xz_dec_reset() - Reset an already allocated decoder state + * @s: Decoder state allocated using xz_dec_init() + * + * This function can be used to reset the multi-call decoder state without + * freeing and reallocating memory with xz_dec_end() and xz_dec_init(). + * + * In single-call mode, xz_dec_reset() is always called in the beginning of + * xz_dec_run(). Thus, explicit call to xz_dec_reset() is useful only in + * multi-call mode. + */ +XZ_EXTERN void xz_dec_reset(struct xz_dec *s); + +/** + * xz_dec_end() - Free the memory allocated for the decoder state + * @s: Decoder state allocated using xz_dec_init(). If s is NULL, + * this function does nothing. + */ +XZ_EXTERN void xz_dec_end(struct xz_dec *s); + +/* + * Standalone build (userspace build or in-kernel build for boot time use) + * needs a CRC32 implementation. For normal in-kernel use, kernel's own + * CRC32 module is used instead, and users of this module don't need to + * care about the functions below. + */ +#ifndef XZ_INTERNAL_CRC32 +# ifdef __KERNEL__ +# define XZ_INTERNAL_CRC32 0 +# else +# define XZ_INTERNAL_CRC32 1 +# endif +#endif + +#if XZ_INTERNAL_CRC32 +/* + * This must be called before any other xz_* function to initialize + * the CRC32 lookup table. + */ +XZ_EXTERN void xz_crc32_init(void); + +/* + * Update CRC32 value using the polynomial from IEEE-802.3. To start a new + * calculation, the third argument must be zero. To continue the calculation, + * the previously returned value is passed as the third argument. + */ +XZ_EXTERN uint32_t xz_crc32(const uint8_t *buf, size_t size, uint32_t crc); +#endif + +#ifdef __cplusplus +} +#endif + +/* This function verifies if the input buffer matches the xz header. + * If so, it returns 0 else -1 */ +static inline int verify_xz_header(uint8_t *buffer) +{ + const uint8_t xz_header[6] = { 0xFD, '7', 'z', 'X', 'Z', 0x00 }; + if (buffer) { + if (!memcmp(buffer, xz_header, 6)) + return 0; + } + return -1; +} +#endif diff --git a/platform/mcu/xr871/lib/libamr/libamr.a b/platform/mcu/xr871/lib/libamr/libamr.a new file mode 100644 index 0000000000000000000000000000000000000000..edc7195093ffd476eae1f593b652f3ec4b6218c3 GIT binary patch literal 252734 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ì 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 literal 0 HcmV?d00001 diff --git a/platform/mcu/xr871/lib/libamr/libamr.mk b/platform/mcu/xr871/lib/libamr/libamr.mk new file mode 100644 index 0000000000..5ace32a5c9 --- /dev/null +++ b/platform/mcu/xr871/lib/libamr/libamr.mk @@ -0,0 +1,8 @@ + + +NAME := libamr + +$(NAME)_TYPE := kernel +$(NAME)_PREBUILT_LIBRARY := libamr.a + + diff --git a/platform/mcu/xr871/lib/libamren/libamren.a b/platform/mcu/xr871/lib/libamren/libamren.a new file mode 100644 index 0000000000000000000000000000000000000000..ad84d8a69b9552c688e94d0f3edc5009329c01ba GIT binary patch literal 936122 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ℑ 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
O%w&G1E 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 z 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

E4#$% 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�T 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

A{J^U(b3y>ygj?Htm%MANlOqX8s*Ril>*wX5 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 {RP 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%0D#*7+q;D3yQ}9=U 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 NBJ^A(^fg52`+sVGmC^tJ literal 0 HcmV?d00001 diff --git a/platform/mcu/xr871/lib/libamren/libamren.mk b/platform/mcu/xr871/lib/libamren/libamren.mk new file mode 100644 index 0000000000..46fca2958c --- /dev/null +++ b/platform/mcu/xr871/lib/libamren/libamren.mk @@ -0,0 +1,8 @@ + + +NAME := libamren + +$(NAME)_TYPE := kernel +$(NAME)_PREBUILT_LIBRARY := libamren.a + + diff --git a/platform/mcu/xr871/lib/libmp3/libmp3.a b/platform/mcu/xr871/lib/libmp3/libmp3.a new file mode 100644 index 0000000000000000000000000000000000000000..455178fac92d1d34672dd4355339def85d63bc9f GIT binary patch literal 461652 zcmeFacX(aJwLUz1?{lOhd0Udpv4!iAj%>?SvfRN%vZQ0nmMz(mD~6+P39F&vLMST6 zm>N?;sIdd-A=Hon7h(e@fdq)1kc65zkOVL(c7Q~LB>diYW^L)naDVsS=egha&-ZOT z`@FN(tXZ>WX3gx`WkzX>E8D8;mnLR~1|PHME}A!I&Vo6!b3&nP9Toc<3eBFsaK1@V zk*Ji~pj7;f|NFV?k4l|+-khn_e|EmTQYoC)lu~6~Jx*EAbtrpC>;LTR-L35Z=IO-0 zq@4QP&pvM}Rt`?z7j}R4dHQ+f`%g~4KcslZ#ow>uD(@LMp8@ZK-2dCz)H zHGQRCSGuAz&24F$$Hr$nTpgOdsqNsHE??N#&gy2d?C zmAh5mE|i;n%^25a81$XNjE3eK7Hv~YTa6Kcw-#5ncj%&Px6_)yU5id~2G^)k?RCp5 zn^{w-&d-^bJ!kMWa&~t1{Q0;#<}6&8Jy%OtBwkin9I-45mr8S3DpL(mO69K7eE(bx znQDs4P^}NgMeYcF^7ns#;)|p_T5{@>?A9k#+8ycSJXAlff*7h(nWvAnHqVf)J^xDUZ}Lb(*%-&f{gF^JrC#d0MXAY?>rx*eb+qr6if`Aa)`v9p?Ru3yra~c&QVSm*r>#s_ z<183%p>4^zJi1)iAs?y0=%m&Zkt zBjY1+cf8Rzt}AeC@dJU#ut<7@GAzjlrH`Ce$-ZN`kze+GFq+(%V-cFN@T{p?<{L=w ze>GIFut24KYb@ilBTu|@s(VOe=DY9bp@ufjuJ=W5T|Vkn)KmOZTW)w1H5It_oj8Ts z@=uMF_62ll=F~d{b1Dk@0;PdRt?*mxSgR*C+xJtKQZwKEdr4eme`I9j#`=p(e%_}J zTvqT(UqxwlBrYwzp1HHwQ+1B*jQ*U0rjGUQ`5=gIeT?emImE7-I7@zYlF?2pD|mwa&1MTs9f|Jbrxy_ai!y!pC< zvc6d<@yFe;^Ks%oZu|K3>0t#|s^k-oBQM`^mFXTn5MCKpCk}*}>M3v9-0$CMUL_V~Ysm#rl;DofsSMNR@P$>n0@Q)$$6X-neZMz_KjLHNZ z0<8+(P+@iBx(I4lZH>&)ZQK*7c;J=3k=bGVre<4np2P2$(?gRl`(W991#eR_;dFt; z{9frx$%ahGnlpm!6M7Ex&-$Jn{j+?@Och;SKZnnXp8WT<@Cal!;vZmn0w|%E;luBEu`*v+z zsJY~Wt_$JW%H$fzJo?6=(*b{|d1{1QmfW6D-5mGo^n&WT%*bqQ+gops0A<2+>a!zb z>yP&B&(`f1QJGtgTjBRBrba3%4^^gMOv%(*CRJu@8>~!!#5(57bdI;z-*v&(isdI% zX6|dbkwuXl9j`KjuZEk|Bh;|Bp$8_{qbSa(sSFW0mcT58?L54*2g35aRbhk8dh8*e@01*YERfjfZr6 zAqd|uhT$h=FJpcF9^~rFr}z|d;>J>WrNvb7KHsV*fq|ivDZZJ()s$RAu1d(p&vBfI z{BdXmIYR=z75@UdHxGem5p%S|%B=+Y*50KlpYjbsHolUV$$Wn(e#)A4JS_8%ngE}M zvUf2O4*eUmsy(f03aA?u}>x z|JjCe-S?2#e~zJ2+ylv=78`1$`%To0e~F<|-4mpi8fu*TE9zWksE|sT2Q>l9zp|OO zJju8s23eyYqbdBWNIBsy%U#PXidhtpwwn)^@|PHLudNaqQNNc$R+n+2N-0LJ2Q2@& z&rq?h*=-C`@e9;+o-x7ae%l8s`V$=&aA}Xff;HoW_gkny>P&bIKRiWwvhTMbhFL?W z!-#;t-G$$}2hq&_4hEUWoe=PMN*+){&l#G8R;>3w)QB9DqOHpRU6D?TwkiL^BAgW6 zrTpJBM8F-+%KQETq_)GZn?Y1XQ^Av z9`O3ru)z+OyVq08i2#1>@Mib-%;8P;JUhJ2O=E|6%V?=l!!3rGZiN0}xkvoPk00_; z{nsOEq=lbMpMR0fnBXncaE?s@G+1yrc>Y}bH`-=jFppMxWFYtqGxtb0_$=#sxui`A z?!utt&$s`y<>RU)w~gI}WNQhS_ETO9l)isCO5;nH9wh0AqeQJcX#S%h6S zCPv6;bCCVR|2dm|!U;cX2cxv(Vw>*ngrB#AiHL@5Ujo|+pYR8F zF|&R4**gA@e$?@Ys6huRv+i81nI zd99eEg_xKQIv|Afo50WmHl0p)Q;Z4hbT@c3#(p2&YrHia?KAob81DMhtXB{p)Z;@~ zhv^GHWLYz`PuXE#@BxVWCt1cd0?3S&knE%8(P8*Wl7ER6LaPSHvnwstVXC=@{<}=b zxZp63Zn@S{7=^y95*WxvGO;%Gn18cfft-UY=|5ZTVlAkGPtq07m3H(6p99;!O-tJ0 zK=6Kg$ayx0cl74qSlW8N_`u3wJX<+xZ`G+dW>(;7|3 z8~T7pk5;Lt{1@7ffDW$XrK3ZK*-R(E^LN_M0_^Z^mB0axgw``&C9oan{X(C0%D+)} z^)Vbz{7p97a7-?(-DxvFlXD^(LZ?VoV=eY>?XGYeMkM>#!N*$cU7mB#)%9M<@mX5!b52AODE*Rj7uhzBdjKD zLp@s)o<-Q6Z~%&&2}NN0CTwDROz1?d#;Kv#z!Mh72d@U@F2vv#KbpM^wd0OJZN#S< z%ID5SS>w+#RKV?m>i98+a^2fd9r0rgmExX8hVkPJHPW3126|Nx}j#c-)6e7p|aiQm_vr4=DIgia+0AID&Nmo zq$#YXq1$2CjQDA*0d5{`oWb^GNmHqBrlEZ9g|u;&p#ts`kcrPSlAEssD*AZb#5@!B6k;U*<|ut z?0&?!Er!Z<4>AXt;1#&zScVFdZl$|}HCt&Ui`{Oft2R`r`(1iKjiJijhv@;ehT3fW ztIlZI<_4K=yP?i=o0+a*sxHI%?iX1TO(tE1I|r>3-(n>t-{D3k_B0 zUd&c$YtgM)?|zSx?I!MGcOLWWFm-mR+s-=M$v8bT{s$B8GGw>q{(-U=Gk+(%*K$8c z11_PPaYpOjMe_@KW6MzhP=meZ(|!>Ll1Gn_gn4{>F?K? z*za2I`7F`rS$Ze@h-HS4119!y&e0jvm($CjKo6P;%J)@P(oGv-Om+Ci`fTnFx)A^a%cddlk6Jz9Aq?T%H$aX00R<&%7~90x{7FSQ|@|X9yq@ci7=0H8=*81HB`Wzj8uUN zL%Ht#l&myVin|OM2dWG;()}kIDNt>wROR!bwgWXR+|VsZf+%DLV zP->_+cPUDeuuk;38&N9>>rFaW`JSNmvaeG6Lx@?Nu(1u`enfqnOd92W9@U?)*-*9{ zL17ZMm=gNj@6(R0p7tNp_H#{~>&|4(QokwgpGloh^|oD-Q;U6!${ZX+j-UIpJ$k+_~sG3H63Nkm4R;y*7lA z)Cu2|;$F?P7ns;vQrz#Ob0zFB-P@_^rl}~V6aKC=4>jrLxe+sH=JGNa+Mg`*t9${v=WTP5Z0N5ckdingb0k{p z4ftu|P$pq(l`vV7p?vNr2qz{RD&YRf4=Tk_=r3P@Rf)sjLV}@vh*_98!WV?T$CzfM zky7r5Fgh`4D4+XtICkPFLj~NYn6pe+U3VFCmI-T$tEfdLtRr~Rkvv3Sfgc} z`!FgzaU848#xs&)KdAAB%5bkmkrO8vD${l0HHk7|o#B3rKA0#I)@*kcCDY7QXRh+y zL(%j(sGp$^!b&%BQiNr@o~4;$a#rqL)R1W?pL>`|rWy)k)t7KBNt~99R6}1xTi=m5 z^CUI=1$HK8vEo_VFECBEu`k6DOZHFt|H<;f-Hj^E$K{uw8_-rP~T?b~GH3ng8 zhs9iZv>8y=*GDOp-ijz*2Seb79R#dq@_BvFKvk%H`twqW1*T-C()Cv^;U6zl+$q({AAxLZSkMs zDAslT7W4fet+uSu#^UQ}@eSZ)YKsrhVyk>Innb;ToF~)B-!d>w0;q-Q=(<+ErLXzm z=Hk2`r?m|_!1e{;;Fi>-&|JP zM>wrp#oRh7J_|0#XDr$_3>(%sc76ty8H-Y{WG}u~Tlp%O(>A~bKy!8)hhPpm^1a%-b3o0hZG-Li>pagV z-G&cItOqoGG3XgzWf>mSG+*@0(EaFPJtX{^w662-={i3RdM+D+jsGT2>qpkJW0aaV z4m=-**!f<>f=qBmgR@{R0y`MUMc`5fiV?Vjfz1d!%RmhR|6rgMflQQr!Nmwf5Lmb$ zf%#7%s}ndEd;wGq*f=uM@v{NW_FEW0SDH`H+=+<4B7!~ULmg3y0SE4k9zb3)>W^L! z*h^`|OkRkXT~L#8DFQbkuvCkbK^6V}b6Wf&_~0c9V_U{IAoVl}cmpWhQLX5V{lx+DADKa3Zzn1-ite^tcyDhFy|u%J)E?tsCn< z3ZWta14j5h1T0PR3TB49$IXy;&J4uyq5Ml2@-pv6!1@t$v#&1?a}cwhrIsI4%jy)6 z&ynO?f8CQ=k<*Zn6?rzR=K6A#aTO%k#h2=cQhN`CZv&6L_1S0HKlYwpOY9GYc!&s`TQ~)X=Jvp zXIl}Ewl&vQi?6~ckZ^Aq_?9(_;t302bH^ESS~^KBZa{k`+}CZSHv^Vf%P<@dsk9oA z%3`rbR3fC7T3?~(Xs*R;=m-Y&v&a$0DX`4qeP>|1)ef+7t#qgf)LRy>aU;l6xz@92 z)gc=#i{rOW54DTg=0+`R-KgDwQOBrVVqK2vu%>B`wK#^GJb6P7W9Kq*s)=Ol4!pz} zArim9(2%&?NU*76>=uc~X=#&@U_TqdE0=l7y2QGfkqsvD2qRgP5jv#r!GA~|Jr)xK zrc3s*x^u0cx=6RfvVPy6h9N|Af|P501z~HxWhKEkOo6ClgeHp-f*@yEORO2Z6r64H zrz4pM0(sUiP?gp^!#6x-4YMXCjTqu2jZNARHw;sk6v)^aYRHhKU}DlPL1^@iQclv@ zL(Umegb^FhFNWZf5+^)AdGVvIAwwu&fd~wZkxsCzq7mdKrm3P4Sx7i!M2xIEAiEF) z$j}j%lQf_y39$i_FJqILGqX$4Vty~fVL>$o^*20dO>s?TBmBq=U9VrxPZNTY8BC_S zQIoaM=>9ylP^y@xiL<0!niw;X91qu-8I_KkjOrin8)Wx{Ow_r*}Zrm4_Yjx@V{vz|8Rd{ZHU6RbWvd2QZ&DJ$K>8c(}*@@RLM_DE# z0&k3G!=Ndi42I8`)DW85pRfN@`A$Q=Y+}?6GnyVlBIOKGB(XDRp|}*LnvibvSz@<# zhpYi{j6opSh&pEW5cI&D0a@lXXF!1D-0>&|>tyqgExO+4`PFP~)cheR`*0{&FjqJW z8Nk&>tc7z>!`Y%xNO&cav9T75VTf7gE{4x6>93L@EX!iu21^Z-jJc3v!ez5mY?Co* zZfsH`l^4sRHg-)MwmvV>*k;1$tpf*a#^t4yz-UWt^x{zWibEG-3Ai6au$+MFHn{|o zrW~RE1A{MeyF?M^n(&|;sTZjUjJEjY|G*JzZ!Xx-)SsHyV9Z3BnlMWT%L%zUSc>2N zBUTaff+D8xKhPPdh`?x{!X3oH{#eZ!D91L?P=MLbz(E2E2rG4PBY~2i)`e*lWY-Ze zGBO!~vX=*18D5JnaoTx~GXFyrMEX^v`0;L3#YkxC? zquBavG9slB4tc50EOr*+VL!nr9n|=AI~0^)1j{@@+P|*frfdl0YS}@J7VA6zL~ldp z*+fp>5zau0{v)RT5rEKps@D8f5RrbMj;AEVToDVqL1V4~OlmMsy@KU`A&O11h)P-tzplj5~nVFUvHKJd3 zI8&gsmfFG)j{%$e`xG-YruDb@Qn0SU8He5*_+-pMhEL#h)lTas3!FKV=8QQ=4o&c+ z7(R`2uoM{jU@M-$i6UwmKMYjhbk*E;H5)3Sd0fBswB{b1aq3cS1j8qACaRsbHX>)z zAld75+=Wmyn~DySraa7nyvGGYAM5c;i2&+u?xa|Gqu=n1tWtL&@CZ&zZG8&xvI*i> zANS+;ahz~##=dTX_|?Zt_&tHsQe~Q!pIApu2M^9Wj`%G!I-Q!xI~ww1)6D>ZIwUYU zW;mu4=VdU1U}86u>m30EFm@13Z2!m-<8`YLyH5+2vVv|f!6Sg@O_0E8GBmMlZy4^c z0e{3vXVs{UE42ZB9b8A?y%UBZUMKku1O*|?H(Y)>^)(YD@GdaMTV{b=YFHHf%HY@; z$l7$94J^PNIAg9GRUlh&4p-N2)xNVJ)leHHo z`V`?RoQ~SF^B@DPF7WRGd`|~U34g}vsLnvYvvN4JadH^)4qhdB%r&5L1rr6g2^I@- z5aYpSj6ME0^i*K#Z>xjQY+D~=mTTca9<1-Y^=q(BLDihSjf-oxHMDeWtK2nvLH4|D z9W5=5?c26jSI?|$Yns{I(p)>Uwz8^W=B|e3ZH*08jM+9QX4~9Zi)PK)*3ewt*jZEi zKUFzcl_=TPzNh_vMUk47<=G|chz2dwsAgTXchuC>*3GJCQ)0c>4unu|(T+W>we2R1 zWj)OuCJ5n%76j4YX4yxyvKnhXs#}`C!~_FN*lKF4`@`TjwKN-&i%TQ2++C*5p zj$eC$^PaLo=}WSguFgI`ZDQ7vC0ny6q@UZ6HZd(N4c!loCvw+enmI9TV!D&K*x5D9 zNtl(Mm9sQ!nQh0&JguzoqKt7F>BY{7#Y>mjLt^6opscj)LDIjqt=Z!nl>{W5aYsP1-UcGfnczZ^#Gh|lpk}M~2)!3}%X%m-Z<;|Xu zmbN8x$;33BA{3XsCVR{5b78=i^-Je0S&}t5V_aHV7EDS{Tbea7YySDVaylD}nrmuz ztD>4YJ9Du1Q5CLVAElEOtu4qa&x@`tDO_8!VZ}-;*}AAU+M$82r8*l6+FLi)wzb10 z)^;}Oba@*Jib|qbU>V(5R8Uy5YQvgR6^-t!Z0xL6Q7q4_<%&AJY%CgWuTxcRMK!xs z6bmJ56rROyYQqY`=Ix~&ZQc?^ik6nC{DH-OctQsMRMB0KtyRsnwKdTevsx}%*$($? zs*UogZnS!52MZI8at&S;7E^Y%HgJ_>L3!Eo)f@T?8ChRg7+qdezCN$KFj}@{Z9a+> zX=&S4*;cb0iAozQ_vkf--V!yvG)*B@UV&LGrJ|k9om|ZsjdnD_tm^i*j+UmX_Go(t z7hXo2wKa%~w$p4>?7$+qXyg33mD=10S5SHi*@tNca@mNbYptrNV9xRlk;3&V+SJ+D zp(1%@<*eCgS^4@6`Q>^ECKg$$>Xz0$(U!J`?a|sME{1DpS9z^me_Dr%mrBZ8j@7Br zx<-h5?Yy?6JX*G)w6tV>ISR3>x?Y*FHS05$g`osgrlKv)(ON9ejaIg8Z&xexHlbWb zybYFXhnnpLusW`zt)+Q;9oJ~;)s-kmRU2AI>ZlQsA+9fgBebJ{_~bsae1q{4Ivwkx zvZl77IYP6eT+-Uu-_RJKu!0v0{F*Bpp|7p88Wj}r7H6($scER&!{xA8@Tr?9is4LA z5!X6m>E(L1l3v3&&@Nb5+TZXfm0nvMt?q1V!zdc%dfM$RZF^Mninhw;c8;QY&9eAG z6wDgcTHQqNYj4=zTw4>WuCHvPQH2|$+L<~V^UXrr{FY`^O`~e9p^~~5w2fX{%W%#d zxS7{mVoRwyu`F>wFT9#9(p1^d%%#qHQKc%YZ`qYs)vlM8c4DDyTT^8ti=1)W$Ej1dI{s;!wn zZK|oZiR)pbb*OJH#mt$f8nFnK%W^5SUHfyiy0N9bw%@Ib)^5xzE-Hwcg|D4vVYs&} zo8Hjbn1}Z~mD}kBdWEpJHXLqLgB6ut|L_!*<>wU_M)J_%%xX|=2wiJqWh2&(QYBlW zyrn}2>FscXcE!sk4a{NbYO6Z8YtPo+Q-`r*N*Dw_GoLgL$BD?WOYj?`{%Bjb`+v;oo>b`ywEIg_A)HlP#!IbMAsCq zDOtY-V}y*$7>emL^ib56ilCBYq%UceHRE}>Wazc0XxBYajGlGvx?yW|%fj(=u~n1{ z!a0g}HloqEcwEuNbsw&S8>?n5hpCq=y~a9ij*QXPdF_}QVVLRfJ9@l{4qUI+7nW@( z<^b3pDJm``)-=Ei;2L_gY|lYX+Y8qg=%TaT%u06JipN>lKj=t z^@aHx)|VA+ER2@ZY~B5&k+BOvTj7Nb&8?>Q3|`qB#fvRz4CRrjnkSxL=ShAZnf>%TrUVWzJgB4)_QlR>Pxp#}bJ23akG7Tm6<9nBc~aLqv0 z1-0#%U+CclqZKbugE={zgh-h=35jm6Y-+01HH(lk9fKDnjEL|_Si`Fw$F{PL%C-(Y zwsV0yI!|qz=|}z5DE&+>BKjJ}3!8C)GitD8ZDC~n8dH6A>&Aw5%)53pwANsNS5aNk zX4(l0brIS2P=>1=+6@Eas!on8b&V~RdSvrPbu@A8{upSBmd4JeS~ka-p0#pAB$8iJ zP>3PfyV}rQa9za(l-JZoGfuJ0oY}PCqBm&fR0elyuCCQ}60H|!ribfZJJ@45ZPK$Q zZ_>o+Qd12sSJCF$U2ut}{$7ryP;l^0T@z+}F^!EOn=ig`;Xx1N<{wbWd6=gWGcr9N z%Uiw%r84tDGu)Mz+mcN%7DyySyiUXmUXt(%6(H?K5mV8t*4ZU z^_=~T=3I;R#DJ2=`z#9Umg{W9g~D70&2?{(Z$jJ*xdU@Ouuv&hjaREEB!(GozQK!c zQ_Id;G%+t$(Yn@_cHA21*<+RJY-1@Sda~A3)zPx6T3SK2!1z?(9^_(!3x{%G{}EMi z_O^W9<`=b;Z_C>>d%>KAp_;Y^6lhyzEArmf-d4TMnC)#xF?f>-47_c)@|9ttL$54s z#@GYzs%dYDVyvve{3&Li89m?A(+06r`^Mabu`+tf*KTfK;Cq<=pxEVYow{*ZQG<)I zwuCnsn6DS+tr?iA)nW+NmyT#tLo@D>FiD4RV2X^6UDdcl&wZm+*wq3fI)+Eown8at zI5c%OVggpJXJDvYbe8C<33=mDxZA9Wft@0p`oF*@eVLAB_q4Y&O$uC1f7 zWf;^-akY`CXq1NH{;PjB$`K9zgy}!$lyoUnHLjPuDB|{pyO?06Ij~1bG>S_;{o0#a z<5mOpCJxwM-_q8hZ#CFFyvfyl6G=Sr`pTWP`XVA-gubu_S2>dvTiqM@b@y#WPP+S4 z!EW9~V~VPuyP#|wv-BQ7xYn@As`|IP(ayo#z3aH%mS`pJDS3~_Zoa1$vtFpsV;R!p z#;6^&Si`F^^m62qnGRNU))_~^b0SQ?llhW2Rn?n;G<=4ik{v`(#9yCIYx z(vQ|cIoY%4WzWl56v}MFm;r~L8afv@nOU_>Rke6_RWoyLbkWQzjKJ0Pp|;wc4Rp#- zPR?9xDRN$DCi3W9QjM0Y-958u4(N{h&Zer+%xMX{C=b}ZS znRU%AGjYd+J3WM(P<8OInJ^CCwzlM_A9`1EvcPQR~s9SPS)^xT2G%)L+<9?BH0jg%<8hfI!cY%{CT6Iz+&!_DMI z0mHaV;!V#PG*D|tt#85lV-89%R_Xhg*!ybcKQJ-W{R#v6hV^UBsHYQ4x6q~I+zK6* zk5KR+MLKQFemycCi%obeg5t8v>8B8IO3v47Yjm-+bq(z`7!zs5hPA8LmTX!ZyR#UD zwdk26hMs+3WHz%rJ<>+LyK%Thp?hjv6=I5Us%4AO{ z=R@fbhTstVK2#`vAL8CZ@K5YSH1&U>tMCnzQlH?!2M-q~;iHk%m?!dlAfK0%ve$RH zv(LWH8WhKe{mcK8xLlL2*SFWXoash__71)j$V&?Jk{|nSh%X72C#CLpvZ&vmZ-$aJb5vYn{F(%NxNoEt zv_?Cr)>+mVYl1J`QnT9kG~xCah-<6HdM|`gs6)-t7rI&8#cURav{_`%it(2-nR-ew zz>Jw}X!rWem^0E~P8B$& z1v_J7c>|?Yhycdw?X^;P!}>I*v7x!v(}>zrvp6R6QB#Z}ZHbzNPBE*pqob{%suPm~ zuT}qRG+%d^j{+#|y|nUD%DX9*1_oS&??v=+rX1sVOu@-_lK+)lHe{HFA0hCdKJSGW zX)oUy2;-x>{$nrRb<+nY;pBT0p3JWhXRN-jfsVD&)0YQ+tiF4okA0l_Xp5(hul8g0 zy$(7Tr*}|}2j6VR>N^U3EF1OFE>B+v!m;|sf$&}kBC5Z9Odi7o`LablkRS3uG`zQ2;z@t8nb)LSjBOGfVzVgyz4A}bc>hEq)vHHfL0zCVY z5a;Q865&{V$3S>5I;oIyJf6TAt1kokO2MOjDL6fSzd|@x-|KKNj#=K}+4plWV)b=E zUj=y7Hyo#@?@tKFBv)7YF%C}zQw}fgUC^=mUV^^;FpT;};`H==gs_(eLG_airM7#W zASRBVrNzqSWMWZbJX4s%i;G7fHs5XA@%;(pm@hwv^70*XhQ6=k3!KfOi{R-Sb%wrw zHk*%ss1M)#_UoH@hQ3drZzUDU;pv-thQ5LprMe(PeH>Ff`<9)dZ$&mfNdJs_Ip++0 zN1-oQ^x>=AetoM&AJpjM+jFp@VuTO?MC)_o8FFze6uu4_I6S#(k@I49LvAD^~S5r-RD(JUxh^jfabA$<`8JhoE>-FX z5>7@q9Vhcm!AaX<^`(~MRko)DaWO$q14k_1r%rz|5JP>jv7}=yAsw3|X`dPut3X>f zz;H0a4vzOmeB+6m8EyHL&If4hJL5V0n%>1BWwc=12PmOFOz}$I7#34jhi|S+O*G#=zm=D?+9i zuqtgJ5+O%l#Rpi93JnlCKCI`UY;|6Xhok+b#7I!@tTX6&XV4MQOwViYaJ2WjGvcex zptpmjd@j!6X#ZV6?_hq{;$%F>`r-JyRcN;KaJ2t7h32(yINJLWpm#9+VUGZk{>d4% zcU|FpdN};yZ6NdGwQ4w0ynlv#YzLvfVHB2w^!PLAIcL!P>d8AOUvvh&=?t3Nnt2E1 z<=H8pee`cNTCFlVR7Oqg?Up=LlGlFvDG%q#-lG>Y(bn_hPd%H`uYt~-Uq@FImn_dK z#>=TlSz&p!Ja2h%VU!v6PlRJqXuW65w&g)b>}z_w&h7sO;LIs_?8H0vRqUsqS80wZ zmwCkWsr_~FcEsE0IOYk`;CDrX-phFJzBoE1hRysiF+-^kXBkY#(Q=5m{^k;qcs&t% zI44e=X+984#Kq;$q-SG=0DOhW#V3jAfEN+bLHT)I;(VoEBrbq3GNK(m;FyJ&zY!Nn ze$cm%`jGFs=304VW3ZeNI30@(1jo=przb*JZ z!IuPoEBF_|e+wp{Jj{>t1Y(*X=isDs1iAAQ>2n1e1aBAoy5PftPYWIw{EgtB1V0u` zKzmW|Xu-*Xa|H7QO9i8XmkC}Yc$?ri1ivT94SlHpl;FPwxrGw>5y5i7R>4aJuMxaW z@Ed~P6XXUz)bmflRCE;5lLhArE*C5n{9%G;3l<4(7ThklQ}A-Zn*{F`d{FR7!DE6a1WyU_29Nfn2!;f+1#<;U1#c7l zrr?``e->Pg$tv}6zYJoHV5{IxL2e|(_#VOig4YS&BzT+PA;GT+en;?8!5<0!LhvoY z(}JG}4#j}Ze8&n-6Jx?%SkPAe}j|w&keopX8!7mDaRq!FfCk457k$T<`d{6KrLGC5Mcr3ru$UO~7 z&lX%JxJK|?!3zZU2<{jBqTtsA9~L|+_;bNG1y2idArrCq}XLxQsexg?kIs|2?Q)(Ex=UMhHv;O&B6 z6MRtch~SHYuM3_O{7BG;i3;;g5#;Vsq-O}8Ex1yU`zSG>jUcxaA^%^3 z?g&GV6`UrxNRZntP=1qOonWWnUcnm$?-G1P@OOfz1ph7QVj@kwV+5xP&K1lRTr0Ru z@B+czg8Kw-61+?Be!(9IJ}>yH;5&kU6ZB)^N_$cS#|cgoTqw9gaJ}I9f{lU~3GNrX zS@0`@4+CC?%PC+Irj-ZL`3>0B>q{UUl96bq2Cbt9ijh3gq|Tt zOTAMC^90ujZV{{%yio8G!7BxC7W}H<14N7k4+}m@#Q8K4`MoIg&xQWA(0>&Cr=UB= zq#H&=x^Y6M2|YvT*+MTCx{!$civ+g_|9qjV1a}C(L+}#8eS)7CyhZTKg5MJSE)n@W zBKV~6pA-5`!M_Qn;NF$;X9+G7EElXJqMR2J(N{YK_YhGhR|_5>Lhd%94+{NFp&t=^ zLij%t`gx&WAwvHff`1VH2ZH}5B0mTB*vw}r5%h2((oGj!CKwg$5Zo_#hu~KQ9~JzC z;Jbne$b))U5OH=2?iV~H_@v-Z1>X|1GMy zHeQc+xE(a(q#(MCrbB`mg3|@F1eXdf7c3H7BUmoDSuiSCE!alH__k#{^#zd`<8-f`1S^Dfkf)VP4&uxH-g2M!Zg5v~3g4|V$^793k2<8cLpLoU> z3v$;j(p7>Df=zQ*i3oa4N6I>&>PH=}{iy$}sqP`wM?)^ntKkowGBedQ( z7c{p7WBl`i`nebA6GH3fUZDRiw9DsEpi>07ff(r-g4~;z^a4TdD@J;i;5xw#g4|$? z@%4hch#2obC)g`^h2ZA}Zxp;;P(MdQI{h3C_>k})6a0Z7_x+;&zApHl;9ms) zCio9Q?hMBCLj^|)rV36J3=2*ZoFzC{Ah<=aLa;^<*(w`zY7A-mdW`9;zFvli zxb9^VaUIGgVq9KGgm5kqez=l|e0cD_25zqrj~}%jB-N<(LmzJEv|gPLA~9{z`a?wM z&k)r4BGC-ez-%J&n=7d8K)#Df1Nnb5nEy*el&z1*{5XyxAN|}O`d5*Lz6)f0eVd5% z+Ahplv|Z4n^8>B(gT7+s1G;GT)IP%BQ|Njdo@ZEN}w<{2~DxU6Y zUGemORi9XIWv7gB-aDdxvj6GR0VlP<@B`CR4_n!-E3zLxx<4?(KJ3(w-QC+|g?skj zook)*<>k&v+wuLZa?F!m`Twv^Wj|rpCycdn@SjG~pXy4C#7~QV#aACVyLOzvB4c;K zNhkc_+rv)9TN4l4^;hQ(-~Ht20Kz?8iFrtW(pG`Psgdj_{{HZ&NK<57WOU@(ckrG) zYw{~0{I5IyhqHOBj=moszNn!0WJ+~T--v9>?LFzs=sr0z?YReY`i7;1mUV;Pdr~=1 zLk`syN;ai``F7l?iSZu??CHafIn(W9*7T{z&%%Eo3+VriH-5E|{#?Nn zdrn_U?!~!YOzM9cGyGFAai`+rN1y&=LDJ7NBbL_Nb@HX`r{hab7WfJd@BKg(>^ms;=a&L^ZZ+0{n7V*PO2T@bg7onQCU&l&*AD-Bkzq zhOXV}lCE7{ZSctrUD2+sYIWCIRiN^^p6>b~_8fVx>t*#u*RQ&M24DNI>yxg3sQ0@5 ztbV6{+jU5Nwd=mFZ>j(2`T^#(4|Ltwbu%{pxV3A)y0+_zt{&Cfy$^eWUfF$%b!Yb% zyKlh$oDX*&?taYrcK5y3SFA60pRj)4{g>{O*2mrdwmz^@-f3NI?dh(!n!2}lS6Sii$<_>OYWG5GN%w;894o~N zc2DdcV-4v}vV4}+?exYuu9MK4>WuFl-8pk0<-aE_5aMF4!oZ8-&-V2=1 z^wIa_*HI%|4Y^e%UfIM4L{wD-r(YrVgAUUH82zTf*d=bz3;z3(`G z>V3QSP3P|3uQ~TP-|T(Fd7}6Gy$?DEoGX~Mr)3el`YtQZ( z(KE^(XP?y*u!r`<_t^HIIVNd8@L4M`j$!XVg@!YOljh40{tKRd?`Q=o!NGsQJ@Ik#6%NSOs1( z)@eYN#@&H4C4+JuH2FQ8(1o{3$~t7Rdh~&GQ*m1S*KwxshiIXFeT7kVh^jU)qh$zK zUuMF)K@Q`8?6&S^>`{=ofS6mGlx+xDUt^9*P&oomM-)pnlH16JMw)G8Rq%PRc?a4H znPE-sRm%DSRi!b>O9M%)*XjCYK4ZJNfAi z8WUxM#>AMt2GL|gVg+R{M^6+JdQT&_B^(M;cC)mRGU+o?db>a-$A}_j%D}dTR5Wbx z{)WScSR3LjJd7VPu=C@{n7tu`1N$G2@+2re+S{5l)!Ul$teAZ0jy!xgu|*Vt9qQBD zy2S{%pDDmr!xme5oN6>-3icn95#laY08Zsh4h+?q2m;f6I_r=YSV!V7=kvS05q#(t1pANJ zV*VLi6gfmhI)+E~3nGA7AB`e#R*L-x3FqT<)UMD$25JodhkzfOAmJ2Fh**PiHrmPv z%#8Iku<>93%ou@dv`qPRfUlY0HvmUW@F~FWOz<55GsA9^_|*po3{wxK!qL05odglH zhZqaOs-PX-;WalMh=)hBD)j-Yy@TV`**K|(br5@a>Ha%<{*8pTc7#uDv4!oO@F835 z4pf{{W6(5nYrWqT1|3}r=eV*)W=%-9V|Q?RLs=uzin5jrV!jRL7JO@E^=}6CS7n8> z8fK==OixQMn2??|eM(l^#0#>pb<~XPw238yWWQ`#p=IN7fwgAi$I(B<4E6G7B7d^@ z<6MMY6MfF)en#(?_ZFp{bFO#JS=KNy`vSeE6s`lZ)kWp5Xj5fP6s3&v;vivcaH7l( zMvYvw8Y9GYZ;H#v6n7#~ReC)yQZt2Fe6N=Y^{<-4z6@=#>CKYF&)AsaGgjO6E8#Ny zfo%~6v8%auI5q>W#MGw!_t#HzEveq#X&?{g7*lFpU|kgBAX&yM{=c%`b6^eeK%KC` z`**2`-JIwD{S|;_)QDXJ=#2&(AsCLOIYPwJ411#nf_f0oOTvG+(<}d!3X$m@7zgsO zhR=VwZ=Z7o*X8X@;^>{1gP;Eg*W2l}b^~>fg=^@5W(8ghSNhhc`2AhtQ!%$L7mjoiicFeDP^pf4Lti~o{-2;E=%ORDE=db zE{^~*UmnvT7hB%@VP7fuw2%G7vu`ov;7Y6teJoD?@2%--fgHbm_YN;ej47VtcR<-- zAbY*(FY`^oN!z@!06}#Jo{7iyH%}kqVvkt<|HhgwJZg?PyxKD?2V0&Fj`zpN(*C$J z;=NJJOCNtm{9tWd6E>I=(CaNu_2_w_jQ_ zxkfDZ;F_@5gKNHGkI!1;MSBKP(X$2)XV8eu{L#oUhoi=54%?JtvbQFSG&K!J{VxIz z#K$Rqhd`RXG90ycp3r1+jg@||?=2b{xE#k^ul0-|hL*WN45xn<5W~~g{}`HA{ZYs} zG0p7COuRwpdjyXOvOOJC(g%Wlg8vkx*Vq^|SO@%81)oG}MAy*>mVRe6iHM4sA^bUl zywhX4Ji!vdje@Kr#@7h$5Ns3NBiJQ)x#0DJyiaEO?+E@#kYhgiuM*KUel5snapbd{ z#Gz;#Vk!|1&$T>6KA9t*>&W;{X1&no5ivki3N{OWHxY7|39Y}Oz<7EcY2XbKufL&y z-meM$u%P~S0(?Hvqn@7-LI0A7uJNkiuO#Rf2VbO@h1*G2Q0`FB9A+c$MHCf`Rtk8p&<_e~KL!6;p?@m)bHPFW3c244|6RdT zf*%O#HCB-OL})(2X8!t{A0Qu3lb$4~$0a=Dr0cPNW9ZR-79zs0INxD?YyUzz9!#(O z6UeI$SM*jqSz%YSu5jwR>bHz}>b)oJlrf+p%j>OE6Rjst zPgLpuP^YfS-CA*5{i%oX|5>J%oGewhR^-3>dVSGyXGZGllOig8YlXGsf^l2xIcX$OYrZRE+Agrw@@am)Waj^TS5PMBXr|*x?};-q2?qc<%kYgy&BC+$Z}2N6L5mPFcRaIG?Y$X?I3~Tlt0E&%EF5 zUsHMWZfE8X-_MP^uJVh!<4;-fKdro>&pA|b^0qPEeW63Y+Wn*VtFS+Sh@{AqFemy0czbf7S*x$CWE&1T0JqaJje>An^$-adL7_*^Z zQ{SZ4$!kigH~0BV*B8wDXn5#xX#a6_{Bbwze4OyntsnpWqqPO!Q$tTYe%eX)9aq!c z!w14E!|KF=F!K25Q5~ODeg8*&3(6i);X{3W3#{;wzJIPb(&u~UP~SfmB%V0d_s=PJ zEl)j6>R6v|VS8cxF-oe1&I#7_6Yre5!V2+M9p+ee<}vf#zWk(!uRbS|7V+!%{|ib~ z=4AzSuZ8#ARZojbPT5H%r>vx$qRr}=zGG9K={x%N<$W(LQ13Z`J)4&oSZB{h$!_Wk z9l7D-fB%&wd8Y5#Dehq_-A;d|@9`;&o2n?c&6{vg0mmU+0(u!E`;wK!f#bQ@lIdwn^iuPzOUER zMPKhP&#VB@@WqnE$mREUMbP<9ebjZVPaSv^)~QqPcde+$zXCe-m#$m;e7U#wg|eF> zjsM}GR)$RuO|Fm3)-{%pRcPyX!YRO68*pgFN z5G`LTB)&JqNj&XKJg4BX(<#ZfRQ&b0HTRvjsXJlvuzuZv&(Lk9*`k~JUhX@(;I_i9 zzuwYyAj9`@Vh4CbnCI=_UESr%R~-pyhpNo?9f7nH5<;1>L)C0!Us{65S!os3M($9x z%3N^F?&qHnzCSHgVBsuxgPDsc}J0ZG5bu3Bv zQLkpz2^FfSv-&MRRA;Oia@rblqHg8fmbVj6TZvU~pHrV5dA9H61(xn9{`=8OKA(#T zJ}!kgeAZE1C#=_TMcazIR$fSA4}2Gpgc~8e(fWwwkp3EKFRNy71i1cDBajfvu7R?loYJKFXYK*G`B34{Vh{?Gus^>)+i3T>xsdg! zfP0K(k~r5bLj`zLN>X&U^3T>2`|wEoBgQe%mJqMX8RX- zrT90b{0qHY50hGCsDS%6CC@gL>%Qa%b&jD@T;6{B7aI!ycb@5%7%J6GWxAz?8t3lw zgIZ>&kV@gyI$-%%R@0U^G@QSPLDuL!Z~^}+Qck$ba@R7lm_-3;yI&z$Vo1#U6DpyI z-$Ls$PE;vVQAYQq}t(s)S@x zv{m`PE7D2PHsyad8%2)N&*J>Oq|Upwr&Ty*Y##862|(W*M-fAnQ2wZkdy zAxb`G(xti=vQ$s9QuwdWeWac~jnp>&-TNP?<(V{8kR8r&pP;4BMl_Y_-px`xSE;EP zN!SNk`JXpAWP^guFBqE_f`ZX6n*0{KuTsnL?K)kq+eGSTyEK)bgw0cx{})CJ{<%6` z(=2$d{gSrX7tCRe zd1N5?9JBLCH<(TjS}ti*f_JkJ`SxEpW`#!v*VFDoiOmkaMZ+WZD6M&J@DXOTQeqbd zx4{c7a$Q)|W^fK;ciAs!BR2=xKm4Dw*(aRvvv!c*r~5Cq>F!SWc{})TX4Pefq1y?+ z?hpP70srMTwH?dd7$Z-X*NRgRWt7rE2ZWHm7>opVGCG~^rWg}Q^LuDbW9;W17yj{9 zJS-f21ctl*G>hLi2ldDs)?sws;Fnlc8QQ1purK&|R?Q^KxJCe(u@aJf)I2%}l}Y|3 zRuD;o_s~n0>M+&tmdd|O$hhDotifFUNfr9CN>~c(CLx(vn|jQ@+1`NG3AR(sR-2b7 zs`(Amoh$9=3od0LwrNQ_90-o1t>@Vsa?zWEKc=nciw~>}K1fwjn=fzdaA}bJ%wJ)@ z3d`;A=HPQ|<4W7~sruj_nNgLHt-;L{t`>54a3wRUv1?%=$KxO$UifS6PEGH(g503N zU#D|+!iTIN9~Stx>*P-OD^`#TA^i11-(v-fn0bTH-?f5a*470=KW+sdVf+rEk6OX6 zGrm#uKX0i7Iu5PT^>>z`X^p1i4Sl&sk5;Lt{1@8a0UhM+y1zq*S-;0wN1gT)h_b`G z@y#DHCZY9=R|#wf_8OtjI_2M}yZRW8C;ldzZ8&B+YiFm;{7lZAz~~gIYOKZHt=$!F z!-!-bJNQ_Oy~}g%sR$?gM&mbup0x}Co!(Y{?hO$)k6hb_yEQ)ei_dL@!1x8QRgLFb z8Ed>=fu#KZ06)H99e#(dh3MkI#y=qJ-imNwleu&#_lwXK*lZ}m(CRTHnc$Ce5%K=rxGaiHxSzF1m!;nvjP>nz)9JNpNv4D zn(19WW((9Xi0XI8Qb{c<9ar2G_Z*USRON)PNpZK*(0W52NO2#a$_Cbl6TT_My#*~9 zxWL5TlHzv2#R5AFc}I%-K4TjVc__sU2TVHQhtM9+j?7B-|H%)rCVuglaz7$ArvzFi zxiFJW6u7VwESlMk1`f0t%I7AMYByBC{Q~pFe=ne<>%IXDbQ&tft)me;4K>nziqtMc zrMkRe1TJD`yhtCV9hVq$?@xA5z@WgThWu`_`!@?@w;>-%cAubdk0BpVcJrBMuOW{l zyQi6UuOW{nyIj)}xZIG>CcAAkVxJ+OPe$$_uQ24XWcL=5`{`Xy_@!ibBg=M`iG4ZQ zy$Afj)rNc>8L>UCoev$jC?vbTWNUrC7#f`Lsbp8z>7m<-9BoH7Zn1oGHKml2;w>}xbNsV>NqZ_s3?vi!We~dzW@L9zwhk?XB_97_x+yt`QCog`JGc$r>eTT zx_Y~PsxfWX+=!|MJ8aeNVNSMtuEGJ#QidbI`cuID@5Eoi+OOLLvqtyECPZe#q3+U`VF4m$cb;kEvlJ-kan-5jRVOGUxwUvDT;C0 z{szSa+Mb3sh->>tZif@U4Ao9P?>3}qR0GqzuG%~`3hI- z6CM_ilvlc1zwm6e}xR)#ymnAtm{`K6o=0 z9?cZ%bg>&gpNXy?Zd(-!zt5WA;FgjUo`_aYzR}gfPNRNEIwW~RGDZyRYRSKKqnt2u zbdoo^8V*Nha+8-yQarSqT`e50WZ}2CT1t303%}LXnuoh^U*6_wso}S%ZDL_O92dqz zyWQ0~ha=qlJKU5#!y8%Novzj=+=zMJ+BXl z#XVZ=f6z&0k=N7>#qR>S3z8qmKzJmOj*7rNH$vQ}%=nS{$B0NTs)9f778C;`+jH*( zTCg60QR)0O7A(+afzjv>yRp+d1U56p1iNx#R@g54y?Sm6hJVXy0<+@MF+1Lp!akkq zo!Cu*3Cuee>(N&*)=o)Np*o$GFuHpnI1M>?R8TmnnGjzDfd zK+L4xZ4$JwwHr3SiYcy*ZayA+*ygv#i^z_A`4qU8B2UX%(@LGdb)BK%E|Vwz;W&Pp zgx*Ihb`PA0y&p&pMDt-1`hyExjr7@;QF4GeJ%Yd<%qcm*tOBEY+FpSvn)Lu8do{+t zE?9f>_A79VwGBVEU~|2Qz8SbbS_*FxbD8Q8vU00?KMN^BScc_$KeN3Ys@47>7WoXc zLE?#f}TGU&dWAb}5YhbC|fFm-uT%+^7hvB`sOVTbb^@5Qagd`v}qfr=j$uEiK&;D}k2~ z^f6So-e=)-l(cn#o30J;tG=}1w=}pK@qa>-ASM13^v}d20}UJYZFr)6h?5li5C_fU zeT9Obp`6l;zVjM3^QFaHAKo`KeQ*|TRQHn}Or7n0Qp4CL-X}dNmMq>Y@Q4-;o!~WX z5$F^?rI~#y6OY31fGV0Uo}utiXN954%&w*TVbGD$GA&W6s%J_oZI>ynW9GQkWMRxS zE#%vrww+Nz8V1OtY-=%qwhIMB5Nc5&Bur+c$Fl$ObFmvBz+DLu<)hVbsu65_e%tUbIEa5dxYEauch)x^Hz*Ht}z zDq?%N-$3E4tPNl?gdgvH5QXu=_sP@=s)kbajj1@z`Eo!({k$Bwulp;m;VI2d3im^o zvjro9gD+$VUY;HP9TO^o1fvKq!KfN2(FNro!DzxuFglumW{io)uP%FJM1v9H{%ioB zUZq0bjJ5uENUR}hMlc#*IN^VE!Nm%zMYMKBf(aAwiP5MLt)pdiM%Z0$68yI%78K2s zLD^XGO%ZXLiQ~=$iri4*a;$MqRhYa6YtX3d1eE-v2thlm;al5&3%tki4y@*Z1VJ}Q@a*c&hA=XQC`xO6JB-2433lQgiOXPfn{@H zT(uChB>%dM@VOU@sF$J5@aJXwYqR{>y4CyQP(Q@7?+!U{Wu|?2IK`$}bY)Eahlzn9 zPwIiRbuDJxwO=LXxs!4~+IF+Cl9DiHlh@ohX( zXn$0CcAdceP9URQx8dD_3nQI6w8`oa+`2dte8q_bw=Irj^&fWnfYa04rDt_)Gj~|K zbo^x<)h*aRvoyV3w~RRd2awm|A`}nM0|}9Y9%FmdiMl8*ke!y@CnMdB=SPj92>&H8 zB*d>CL(}=MKmQHmzfSyz8r2Q8L>4~-5hx1(Ubo@Rp)-3l20-2rK2j^!7lOx{$2<0}B zcI~Ghm(?|EZr5&IyPY0PuD0lY1M@pf?LOeBZl`3Om_9x`*rZ>2w|4!r(u2QW+%Ncy zlL^N!vCQ9y!vh>vb|@=5En!sYuwlcxT2m}bn1--mLKgq8;y?TBlOKS_wd-NYmO~CC zWS-BUgl=b`{rlsuYx>-F>BF<6F`&IGpVYQP`}B0?0;7bi?pbG~bDz=1{`i$?Ai-M2 zzXz;IS!~F5NN{vN%E8P`ATBtnUog90Mp}08K6H%Nuz5jj9_9?iL)dc6z>UvJ@qx5v zPsEhm)njbJP@7m#lgn0=PbtA1q*H8)!(2|C%Bdlv6DbdzFvXksz220^oQQgCVUatN z>mbe_I})=r;!`>QOyPAtAC1li8=X>=6B|}xx_i!cZI#kI&QmMqESyqNiTU44X3r@t z{95 z7vLbBGH^a7!$6IzP-SFx75 zE8`FpTvExpc~hWA=T+u+12&gqCBEdXDDOVVo%J4*ug)#2b~yX^U``Ls^zPJ^-lVT% zs!J+MDs1}gsWyMFTSLv^?0yPdqzUWI9P|C6SqGn--OHOU)th5jvw-3^Hn9(I>+Gc| zg;kiO6tiv@&7H;>S>2g?Ifr|>bq|DDkbeT>a+{gcnWBj^*=elC(P?&V=lZ7~j?K=l zT`>rgv~#O4VR>=AN!l4aykfpnf9iAW!x`uXrNw2AH;d@B!l~6ZpQqc3m~Yj|AB`R2 zb}pOw)~5ZA&Sg7$#)6XKTn_uzUftbRa~tjOsY3tt&zz%8U^{Kf6!!z~DRX*N9X`7% zKJlK0nNWFrab{A?PpV#8XKrCk6N%ZvF}>~7YRA5gc6T&4r_S`pJp86`#@YIFG*3a# z#*v|O)2Z$v(Oy|z2c@~ zhflAl#FW(0`O~qRk&**Q)U|M0yE&1n9CtBh_%7Ixn?>N{fV0G`DLfcC3UMxLoiKRH zA74_*>CL0lR{PyW;N;Kwaev1B$Dq-JCR9`w+Y!#A-HuT^M58m#<1jqisx_2tWhZ-# zeN)QqI>9}T>>>kmd-DoxbjiFaxTKjeZJ|DobT1^BXH|A|O7|&MB|Q8vY!%zQ?l{;> zN^mGm#ia9gS;h&mr<7~LHvB3RJNU3^z-`#8a{eDU!{Ckhzv>Hv4*}AAckz4}FuZZ| zn12iC-i-3|2Lgv9)895#kz zyYoYvn~%dH4~vVA$p6#baoFsb=pJ}z>@Gb;58kt8*?9Xol8r=V&(A$pZkj|f9G4gu{Va$*1?d7)%GV}A6yYS2^&(|Bpa9SUE~ zNXK&dh}5+rjluq3oh%@!N5Keq|#ZCkKg`Uo)&;ei`-h`yBaAM7+0n^^4TY z?=IxG8i|-6-ts>FulQJBKKi%*aFF*d^-e13dBk>XpiI^XMOXk%tSNXAza7jn! zt7S*g!u+_NS}$Fz9zX8~gg+f%dT+TD>DXVqh2?S?k3|0ZRil1$5YGBxS4ZnN9qF)n zjIyN|0|&B>=Fcy6fa@m# z!M1q$dAdh`_BR8;^s|x6ltr-FR=5_Useu#V7i#x&2A?;*+dx zs-qjpktw#&4@lke8d&f(9gBn%g!dR8>ue6zoPI(wrL*Sr6Ot=^v+3^K%uLUr(eEdO z=LY%-iFS0vvtBE(dJA=)SGxtn)*ks#y!$0qepu`)f+ z1)SU2_ffg$z2u)+kC}ev;lyDD*yBFl z7gzZo4YRAcOOfT!E(!UPO~r`Fcgzg$DGm^a ziyV6yUm&g#*NdCP`@~1Zm&JF*T_Q)7ICN+p1N3LWqmty&N46sI4^8C6;SfSoSUAUV z#`hHmia7OI_;~R*VzF2u@>-1cOT|mY4I+;P#=juGCGHZx6M1dGbjjErvZdHY>?HDB z$$0*5jyzLbEt-E9gx?|kLGfAfP4Q!KulSS5V~+W@5j%^$#lhlOv0CJB>uI-2G=DA7 z@09*0@l%l>axi@oPH^OjB0nLbULsbBOT|^E|Dtgzg8*FH@=PV6h5B2E;ii}S?g;u`U0ajW>W_@>D3 zF<9S~H~`2s;%`Np3k2bhOaHTIbA2HEd+7-{?b5!RI6ypAoGi`~&lWEd*NV4^+r($Y zKZ>7<2gG=szFBSyv7^{a94xL9*Nbn9pNKiQSYke>ij%|wahf<=Tp%tJuM{_ko5e@P z7sYqP??wI;jO8{F+lpPp6UCw8>0+rkUtBNVE^ZT_7T*weieHO|#0CxBdZda+i@n4= zag10b+8n{C{}Sn|#P#Cc;-lhA;=AG(Vy&2n0hsO4Qan|hDBdV;61R#Ei%*Epim!<8 zh`U5yw6Pri)R8<^>?7ujW5ptIj(DDUsklzuByJOJ&R^vJf%GrL{UR?9S$<t}D7J3mmeWzp7IVbY#B0Ty#5+ig zDVxRT6>f9(qFx86k{d=__5#LUFhIE_57x6u$_gDNN>1T+gVik#FYrc36 z3Hw#z8WQEKmwvDGhowJ7!u~lD=leGl{eOi{T1=g;!YBFKat3{ zK~oSn1dYX}B(@`sM0_Xd{J?|Z`QkXSghaYI(&tKFC4IekxA+)|@@&pvuvWUw6%4&2 zR>q$sP87?;<>Gqr0r4f#<^+cPH|Z^#yZLn!hlqvZ+2U2=UE(w12jcgl&Ebo@kHLi^ z>zOOs+`MRqP1M01Vy)N!*P0B^7IFDz#|172jObEE945wev7^{U%o20OeDPFqlsHwK zE}l)IpPwhL5HA+jiR;B%#M{LO#Ye@b#OK6U#W%#y#4p7kM1CX5dW3oX2&Rbi?MmIQ zU%~d$j~30(9KyRxKSAs-o<^b(M~kP6=Km1kd?v$q^D_rlN}nh4UY7Bfiu~4!$6<|l zn|P2t(t@jUT-@gnguagDfE+$i2E-Yx!4UO^a;`977zc|Ahn zxa>^g_{k#CFZ+;4oI}E%3)3N6BQC>@xI8mjez=ZEqYg$$O#kxI;Ntk|@WHZ{+(mb>x@K{)c>SCy`Hr+8d_78J{Y=UDr{#PuGT*bSf+;yru9F_@QgK<6-`_eDnf0+)Zf)(V zmAn?PwhjgIlJJO)&aQ4l$Ev@;2ijQPrv;LxpbNnVnt5x9OPr3tm=82Nl_$lYfL;tA zXnDvv^cVKlH`&MIL!qyLVB%o>4?P3JP|7lhaEC?@OCdg23w2^hLKdnL=ea`*4Sf<} zNl%8D<{FCQ(W^tJGSqdS@j8SC{Acdaf-r8F6grvb^)Md_g-&6KaDWirg)Ix6w$ZeZ z6M6;p42^gPYUobpKBk;v6++`%9tA_VLpu_gHChK5?$G$aGCUnw#CM~J4C}f>dlc33 zT%IL`yZW?ncn+Gui<3LFi!BejL+c1Bnf}*UkZlMGt%W_@p>>2|n1_1I9a?kd)yLhY zQ20-Nz0`otHoW#kTxc_wpSG_K0LN$4XeaEJC9st9*z zVPqISnHyG3k(K8Yio@KYmBI?{(8|z|t~)fP{wMCxZeV?)?$B;v8TH(ueS%EsN#iP} zjJiYPo3;2S*(h}F(2CkqhhRO z86L^F_bx#l=nm~~)OPY2I(#>UA4YERAKMi*+@T$037@#Rc<#_>*UbKLyPJK3&27dM zI1H{pRPzGZm^(DawAhRYpF6Y;2#e2&`_SqfYSD)+=BY_7I&Ihq3eH-mywfY{BCL?$CHl zm^-w^tk$A9c6W1!HjVm{xJ+bX?$G$?4(`y_!z@U5Xq}neinzWOZ|=|zu%JNFAQ(0Y zv5&i+%edXXy<+NMcdv9pR7;vQ!KV~Ve+k0b6Mg9!#mUM>}L36aQkFW=r%xsFVWpIbasJIRZE%=bMzB{xm#HqIC4y^zsx8T!E zpF6aURL{_U47E6e8!;L4_A=L`7JS4EcW4~m=??8!mKk%0b_Dxv%pKZnG#%WbeTtgX z9on(nu);X^m?~|N&4P-g&S^1)iKj|k(1LzU;SOyM%H??6qLR@iaZ5~xJG9%mc|Lb& ze8&ZMXw2N)q0M1orPARJZ87`u4C!!(b^&WXQ##zCwPN~N(%}xRIVxSp9U40hYh&#P zcWBgE8`I%UiTY|!mpe3ir)hBPiD^= z3c>8-oX}rUP(m+;;u|1-B}sQ^bTiiW4Aj+WTZsRGwi$>(;v6JS%$tnZMq^P3+@XC7 zIlKjO%pID!^NPAd<86A(9oqTGAm$G3eiRUMhxRGsqVCWVQKO_YnSIn98oi2HwUeC? z@79tE&PR00_4r43XjdRQ+@4t$Q{xOR=Zj_yn~xJ*VR(O*Rm4xT&;QdLu&I~ zEj2s>#V4K3!g!L-Wj$i<&`v_P^tnUht4E(Zv=f>5e0Njf4s9sQyuelA4s9fDWA4zd zVdCX(EZm{p#H?1hD%_#*e%0p=?Gmah*}Kdg+Kt?{m^-w6tg`vt!dan7_;kizHV|8Y zlR}d)U2gi^p;^mb$*nbaXuoAOu3`@{cW6hld#s^fvzR+H=489abB9JZW!9ep?tdrr zC~LoN3(Oj2B15=CyAWy29U296DRYN*t;uqSM#b#q4(&EiZ{&pLa*Jw;5YlcB_Bh<3 zy$!kTeJI9h`zVSDw6)isZJ+0MIHA{3?d0?BLz+gP!W8b%=))-76t?xF6zFFzmJHyFXLOznESGW+~~XAJafW*7+2SQ z*_$Z9Iu8cdw-|j7{k^3ej{$-1%lHl{%%ddczAT5uZE>>JtW)k0lw2}n40|1jV$*rmZXb*C%l2p^N8C7p)g-MCqL@e zA|1u0xyA@zYzCCq>$?e>@xFftc zt24TIh>zWpKbirfMq%VVAo-hYB*IXYyqAJIR3(3#Yx0rqbpsX47DE-UU?zHc*h0|< z`6JupeT@*_=x^BAjN}8?L3Vu({&g^#i5vbQ6MfGJ3_y*`;heG7Rphqxb}w6uXdkd& z{V)|FDcjJ$(*nr{KV*)VurC~<$Q;v|?LnJ`mxJP++9fF>oxx0(=F6 z2aZ@Xs5ZkGee`T>Vqhi3$q4I&1I?iesZMF0aI`1zA)-YM@Py%>FkV9E0tf*nIwmj@ zKGa6?vTp)L$zuasSd$a1Or&vkV_nb0Cm`{0R*4f?iN_Gufo|LSG2%UH^xD?{Bxv!d zeut`QPzMZ#nn|@3+DXHpO`ui`EoT(8CDdj^8%S?&x0@l3>p(7@z0n(G7gWlb59M(b z%E~sFnBsK7&%m5vXykV-a1;X(2S;~Kmx$7T?WT`Q%5%D~QJF`ux$;O0I9=FG3{3R` zhaiwm#=x%8z@XD*hyruHz?7y=m(dEG;01=-J6*VIXkO+8M%p-C)*_HSVu>4QwY(K! z+<D=K{*M)97NPbGo{J#YwPceJPXJ9qJj9xW zIPx_Kaj3jK=|{dE`s82897?fo6pa`&GWg{7%H;UB_bo z>r3q4^d;uG^7=KN#N_M>KCJjmBqQe1ia)0681={n2Vr&maUCsxT(au~zgT!e*&Rn{ zXV*p6)wyA6$N2ng{gMYUb42FWql@3qR##6!7xY)CpLtyC<_|ihS=0{~x+U&Uvua3m zUc6+ha^^T`H4vkVo90}5aliu|zKc@G0#`N`DIAC{xE6EZy@+6PxVR$t1OuT&0an); z6D4MW2>g#OJaeHmJA-jRhTPeU2`9x8PO?<^*9B`9RwN|cgr~<6o{m6UhT{KeSdox$6HbgJoM@@=F9&NL zRwN|cgcD*3Cm_%kw#*u=NJzK|$1~vuHjt~gWJ z+U~-hz=Z{KVc!1bpT%E~%;MZh^8yFqj4*%&wpau12gASCZ_t02E24kLP0}y>G}6n{ zsce9#?;{2nJ{>wtL^^+A3VNI8w#&C!$%AnHf8kSLE}b`Ic5CdDPygwbBl`bg|}__JbuFcuX*Xg z78O^_t@iobfm4QG@0JH`6?InQ_&&b>Ssy)9i|lLv|Aecac{9q3OW=y=pS$VNr|ti7 zZ$0xXDrccPnMZ@z=HpI*-cbId9(>@`z}vfMQhEu1Ux0t&k*5NCpEZkl=Bb)CleL1w zl!gD$J5uGWp+3Gs&ab2-lSDRWixV(6{d1`^j1bEl+4iV(#^CRV0x-rhb>)ofy zoQC{!7eDn%obhjY{K2tcdz1||`~TMQ4{P+V`t>o#Dstpw9x`Hk-1%9@K7Lm|^~Q&v z-;X@h|2rN2{F0v^v@?HzG_Z^gW-#93AU@phPnF-_4;+omo8bAuaXYi?{f7}Yru@A3 zpFwcrGXrM_x&X3X`>(hIqO+3nsu|S_t#RGbX2aRq;(cb|XLt&iurD0+21 z#dYWN|Jbe1AdG7~9b>q2%ID4=iP6d~7kCZ%zy5ycN-2_^-7~vSpWon)k`Dm>k-s0_ zpk`q87T(AXY@BjIXjyPM{PfIj@ai*9yalS0jyJlwjXPh^;Bb#T|2=+q{5>G% zH%DhcyA?SsG`r6P>^HO1=Xw*xY5yluSlD7-AmV3>ApDT=Y7er4|#Qf=uF02pg9z@uv{*?u=?wFgKv9VBF?Md0mwLpqFKC*{Lp2h zi&qxjfJBcqM*bb?_(sfIJdyNu52qL#bT0zYj8PvuDUew=@7RUtG)KQ5OhJp6pQn5D zhyN#h$@q2!iU?WXVj0|rc=PKOYou?Py6Jhu$Ce#1?!~_+L01V z^2@$nSj*TF#EcMLe|AH;r(;X7j#n<0?;AjnEQlW57UcF9VWqDYbi?Sv{g#Q9@$856 z)xz=6TNoexpu{|*_)mBl#g6|>XEy)}h@Ry1hz8j-;EDr^#}wIKM0Ya1uQ)^;E%GJA zQAp1*t^pc@Ex@1NrAg=e0)|^YpydPRDf~2XqPT{{*tn5IK6i=R#3#fT#W%$dM4s>X z9S+C9IP5MSM0t?i>wgO7B3Vp6|WWV z6uBL=W4+0b#Bao(MDqiJ_~zL8OqVX&_mj}GrPC)Cz2Z+|GYnG9 zFJBxh+V^@0x9{}8OBK%PvzdOAxJ~?8JS3)KXkoge#hzlW$jNgUf2Me)xIw%}d`#p| zKbZbKkzW;1|4~fFP(?je>?HOU2a99HsbZzLR9r3Ai1&z(i?4|vinSu=vu61<;$7k+ z;)~*EB0nNx`ZyeOr|QhZH(U)&@9ASU93$b4Fe9mMWpjyOU* zQ=Ba>5-$&jQEoHx@dkt zkp2_t--rz{=Ciy`VlQ!!I7*x%&KA!RFA}d6ZxOeMPl~UJ<_84jnI90){D6Sw2Lv=f zAfWjH0XZKm>z6N%6^q0<;(6kw;yQ7YxJ~?{xKlL$AIRVQe}Fd0EjSuKsA0J!VwJd5 zTqUj-Zx^?T`^CSDVVpaeUu$uSI8!vg9f)5p-TZYxze)OiVi4zE=F?a-zZuYrrOy%1 z6U`q6;_sKfUECr5RSX~v^KBv?CFY33#W5tt$_e6G3SU8@O)rtYPW&y2vE>%=b`t5f zDE{}-w=4c7@lEl4@iXxo(fnl~Urz4E{M(R-JDS8XeXN*CqP#we&y_w>JX4%4o+Dl) zUMt=z-bo_=C&WLC--w~+ZhR||GkP-pAaSBNTU;t$FK!cG7rzn{aUsTh+KVM5wwLo< zl3ci7a2hq@@HV2`7&Az;Q)e+t>?z_l*y3}we6*Q>NI5T}c?#7c3Vc&@lqyhL0j zt|zfQ8^t@s&EjL?cJT%A74bcBr}(+JN8Bg=RpdP&%d_i4(EM$HcAW^eQ9OTt&UF0Q zBx%=+;0e;r&j$27>G;{ArQ;7Pncl7^!6NC?#M$Cm;u6t5=R^8Sq^}aM5$)?O#Pi3Q z%>Qn2tN5V!xMhw26S-kZ-C}xLz(0whxihh$Lr1kl;myt!(R{WSp(RS9EivP%X&00)Lp~=XpNo{w$>ZV= z9BSd_^HeW4m%)X_&iTsaO6Pp!lB-;`#qbbA6i&4+aN~u1!K7X^YNq689t| z1+RL%&FI?H;iGHA{l?XX%T^pryf(Ban7kPICY62CdIc=T)rN*A?MZHMhV#Onq@<(s zR~$?l65NxRJmSlhp@XfD&quCPo%Po{p{ue+yofw6JeX7(Sl=6^CG>W#pXaR03tT_q z%OOc=d2!eG%*#b-xwVnIa%H%8^VFmB;;+xG4MR&R>$1~%eAM=4IS1P>A61z@BIoymx8%GQX*fHj z-$%K#yZ$&ZujYy&={#( zul%;73dZmG>+mU|)7Fn#xA6Q)_|2;m$zGQm|7~jg;{E;Zd3jI6xJT*fVsBzn^T(Zz zBlfQ>Njk8kIH5FtPbe7Jb3x*OqJgy?3YtHTm}$<@Czqa?7ud5R*bbpb&pEj~zhXe% z$z|WSda5>Z)>F04S-CS$o^@m%v`T1|xwBhRo(g&D$raC*Bkc_6)zGVRXFre%c`oF+ zxwD>|X;$-~&D&mPR?{I*f3Eyll<-_BQZ9hLV0)QaErh&q`^@Luvd)Hf_I9_dS&(Nv zJL997dvhi1@+_XXVc{`yyyX7tZd+ys98qE$7yZg*0|W`SOZhc`Ig{RS7~$E}XeymRU`M zHtoVPvziZi{&H^3L`V}8Dwd

eF`%bAflYlU0ZOlUI`-LlSsd`>*enha@jLV0{e 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 bJp1lt?HKWw1R3P-Mf?*%*7l(tE_CiszHB2k literal 0 HcmV?d00001 diff --git a/platform/mcu/xr871/lib/libmp3/libmp3.mk b/platform/mcu/xr871/lib/libmp3/libmp3.mk new file mode 100644 index 0000000000..5d6272bcef --- /dev/null +++ b/platform/mcu/xr871/lib/libmp3/libmp3.mk @@ -0,0 +1,8 @@ + + +NAME := libmp3 + +$(NAME)_TYPE := kernel +$(NAME)_PREBUILT_LIBRARY := libmp3.a + + diff --git a/platform/mcu/xr871/mkimage.mk b/platform/mcu/xr871/mkimage.mk new file mode 100644 index 0000000000..7f12868b01 --- /dev/null +++ b/platform/mcu/xr871/mkimage.mk @@ -0,0 +1,57 @@ + +no_with_xip := 0 + +ifeq ($(HOST_OS),Win32) +MKIMAGE_TOOL := "$(SOURCE_ROOT)/platform/mcu/$(HOST_MCU_FAMILY)/tools/mkimage.exe" +else # Win32 +ifeq ($(HOST_OS),Linux32) +MKIMAGE_TOOL := "$(SOURCE_ROOT)/platform/mcu/$(HOST_MCU_FAMILY)/tools/mkimage" +XZ_TOOL := "$(SOURCE_ROOT)/platform/mcu/$(HOST_MCU_FAMILY)/tools/xz" +else # Linux32 +ifeq ($(HOST_OS),Linux64) +MKIMAGE_TOOL := "$(SOURCE_ROOT)/platform/mcu/$(HOST_MCU_FAMILY)/tools/mkimage" +XZ_TOOL := "$(SOURCE_ROOT)/platform/mcu/$(HOST_MCU_FAMILY)/tools/xz64" +else # Linux64 +$(error not surport for $(HOST_OS)) +endif # Linux64 +endif # Linux32 +endif # Win32 + +EXTRA_POST_BUILD_TARGETS += mkimage + +ifneq ($(no_with_xip),1) +IMAGE_XIP := -xip +else +IMAGE_XIP := +endif + +ifneq ($(no_with_image_compress),1) +IMAGE_XZ := -xz +else +IMAGE_XZ := +endif + +ifneq ($(no_with_ota),1) +IMAGE_OTA := -O +else +IMAGE_OTA := +endif + +IMAGE_CFG_FILE ?= "$(SOURCE_ROOT)/platform/mcu/$(HOST_MCU_FAMILY)/image-${APP}${IMAGE_XZ}.cfg" +IMAGE_PACK_DIR ?= "$(SOURCE_ROOT)/platform/mcu/$(HOST_MCU_FAMILY)/pack" +BINARY_DIR ?= $(SOURCE_ROOT)out/$(CLEANED_BUILD_STRING)/binary + +mkimage: + $(OBJCOPY) -O binary -R .xip -R .eh_frame -R .init -R .fini -R .comment -R .ARM.attributes $(BINARY_DIR)/$(CLEANED_BUILD_STRING).elf $(BINARY_DIR)/$(CLEANED_BUILD_STRING).bin + $(OBJCOPY) -O binary -j .xip $(BINARY_DIR)/$(CLEANED_BUILD_STRING).elf $(BINARY_DIR)/$(CLEANED_BUILD_STRING).xip.bin + $(CP) -vf $(SOURCE_ROOT)/platform/mcu/$(HOST_MCU_FAMILY)/bin/xr871/*.bin $(IMAGE_PACK_DIR)/ + $(CP) -vf $(SOURCE_ROOT)out/$(CLEANED_BUILD_STRING)/binary/$(CLEANED_BUILD_STRING).bin $(IMAGE_PACK_DIR)/app.bin + $(CP) -vf $(SOURCE_ROOT)out/$(CLEANED_BUILD_STRING)/binary/$(CLEANED_BUILD_STRING).xip.bin $(IMAGE_PACK_DIR)/app-xip.bin +ifneq ($(no_with_image_compress),1) + $(RM) -vf $(IMAGE_PACK_DIR)/*.bin.xz + $(XZ_TOOL) -vk --check=crc32 --lzma2=preset=6e,dict=32KiB $(IMAGE_PACK_DIR)/net.bin + $(XZ_TOOL) -vk --check=crc32 --lzma2=preset=6e,dict=32KiB $(IMAGE_PACK_DIR)/net_ap.bin +endif + $(CP) -vf $(MKIMAGE_TOOL) $(IMAGE_PACK_DIR)/ + $(CP) -vf $(IMAGE_CFG_FILE) $(IMAGE_PACK_DIR)/ + cd $(IMAGE_PACK_DIR) && mkimage ${IMAGE_OTA} -c image-${APP}${IMAGE_XZ}.cfg diff --git a/platform/mcu/xr871/pack/.keep b/platform/mcu/xr871/pack/.keep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/platform/mcu/xr871/project/common/board/board.c b/platform/mcu/xr871/project/common/board/board.c new file mode 100644 index 0000000000..2f891491bd --- /dev/null +++ b/platform/mcu/xr871/project/common/board/board.c @@ -0,0 +1,147 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "board_debug.h" +#include "board.h" +#include "driver/chip/hal_codec.h" +#include + +/* uart */ +#if PRJCONF_UART_EN +__weak HAL_Status board_uart_init(UART_ID uart_id) +{ + static const UART_InitParam board_uart_param = { + .baudRate = BOARD_UART_BAUD_RATE, + .parity = BOARD_UART_PARITY, + .stopBits = BOARD_UART_STOP_BITS, + .dataBits = BOARD_UART_DATA_BITS, + .isAutoHwFlowCtrl = BOARD_UART_HW_FLOW_CTRL + }; + + return HAL_UART_Init(uart_id, &board_uart_param); +} + +__weak HAL_Status board_uart_deinit(UART_ID uart_id) +{ + return HAL_UART_DeInit(uart_id); +} + +__weak int32_t board_uart_write(UART_ID uart_id, char *buf, int count) +{ + return HAL_UART_Transmit_Poll(uart_id, (uint8_t *)buf, count); +} +#endif /* PRJCONF_UART_EN */ + +/* spi */ +#if PRJCONF_SPI_EN +__weak HAL_Status board_spi_init(SPI_Port spi) +{ + static const SPI_Global_Config board_spi_param = { + .mclk = BOARD_SPI_MCLK, + .cs_level = BOARD_SPI_CS_LEVEL + }; + + return HAL_SPI_Init(spi, &board_spi_param); +} + +__weak HAL_Status board_spi_deinit(SPI_Port spi) +{ + return HAL_SPI_Deinit(spi); +} +#endif /* PRJCONF_SPI_EN */ + +/* sound card0 */ +#if PRJCONF_SOUNDCARD0_EN +__weak HAL_Status board_soundcard0_init(void) +{ + static const I2C_InitParam i2c_param = { + .addrMode = BOARD_SOUNDCARD0_I2C_ADDR_MODE, + .clockFreq = BOARD_SOUNDCARD0_I2C_CLK + }; + + static const CODEC_Param acodec_param = { + .name = (uint8_t *)BOARD_SOUNDCARD0_CODEC_NAME, + .write = BOARD_SOUNDCARD0_CODEC_WRITE, + .read = BOARD_SOUNDCARD0_CODEC_READ, + .i2cId = BOARD_SOUNDCARD0_I2C_ID, + .param = NULL + }; + + I2S_Param i2s_param; + HAL_Status ret; + + ret = HAL_I2C_Init(BOARD_SOUNDCARD0_I2C_ID, &i2c_param); + if (ret != HAL_OK) { + BOARD_ERR("I2C %d init failed\n", BOARD_SOUNDCARD0_I2C_ID); + return ret; + } + + memset(&i2s_param, 0, sizeof(i2s_param)); + ret = HAL_I2S_Init(&i2s_param); + if (ret != HAL_OK) { + BOARD_ERR("I2S init failed\n"); + HAL_I2C_DeInit(BOARD_SOUNDCARD0_I2C_ID); + return ret; + } + + ret = HAL_CODEC_Init(&acodec_param); + if (ret != HAL_OK) { + BOARD_ERR("acodec init failed\n"); + HAL_I2S_DeInit(); + HAL_I2C_DeInit(BOARD_SOUNDCARD0_I2C_ID); + return ret; + } + + return ret; +} + +__weak HAL_Status board_soundcard0_deinit(void) +{ + HAL_CODEC_DeInit(); + HAL_I2S_DeInit(); + return HAL_I2C_DeInit(BOARD_SOUNDCARD0_I2C_ID); +} +#endif /* PRJCONF_SOUNDCARD0_EN */ + +/* sound card1 */ +#if PRJCONF_SOUNDCARD1_EN +__weak HAL_Status board_soundcard1_init(void) +{ + DMIC_Param dmic_param; + memset(&dmic_param, 0, sizeof(dmic_param)); + + return HAL_DMIC_Init(&dmic_param); +} + +__weak HAL_Status board_soundcard1_deinit(void) +{ + HAL_DMIC_DeInit(); + return HAL_OK; +} +#endif /* PRJCONF_SOUNDCARD1_EN */ diff --git a/platform/mcu/xr871/project/common/board/board.h b/platform/mcu/xr871/project/common/board/board.h new file mode 100644 index 0000000000..58a638e2dc --- /dev/null +++ b/platform/mcu/xr871/project/common/board/board.h @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _BOARD_H_ +#define _BOARD_H_ + +#include "board_config.h" + +#ifdef __cplusplus +extern "C" { +#endif + +HAL_Status board_ioctl(HAL_BoardIoctlReq req, uint32_t param0, uint32_t param1); + +/* uart */ +HAL_Status board_uart_init(UART_ID uart_id); +HAL_Status board_uart_deinit(UART_ID uart_id); +int32_t board_uart_write(UART_ID uart_id, char *buf, int count); + +/* spi */ +HAL_Status board_spi_init(SPI_Port spi); +HAL_Status board_spi_deinit(SPI_Port spi); + +/* sound card0 */ +#if PRJCONF_SOUNDCARD0_EN +HAL_Status board_soundcard0_init(void); +HAL_Status board_soundcard0_deinit(void); +#endif + +/* sound card1 */ +#if PRJCONF_SOUNDCARD1_EN +HAL_Status board_soundcard1_init(void); +HAL_Status board_soundcard1_deinit(void); +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* _BOARD_H_ */ diff --git a/platform/mcu/xr871/project/common/board/board_common.c b/platform/mcu/xr871/project/common/board/board_common.c new file mode 100644 index 0000000000..c5ce3a3215 --- /dev/null +++ b/platform/mcu/xr871/project/common/board/board_common.c @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "board_common.h" +#include "board.h" + +HAL_Status board_pinmux_cfg(HAL_BoardIoctlReq req, + const GPIO_PinMuxParam *pinmux, uint32_t count) +{ + switch (req) { + case HAL_BIR_PINMUX_INIT: + HAL_GPIO_PinMuxConfig(pinmux, count); + break; + case HAL_BIR_PINMUX_DEINIT: + HAL_GPIO_PinMuxDeConfig(pinmux, count); + break; + default: + return HAL_INVALID; + } + + return HAL_OK; +} + +void board_chip_clock_init(void) +{ +#if (BOARD_HOSC_CLOCK == HOSC_CLOCK_24M) + HAL_PRCM_SetHOSCType(PRCM_HOSC_TYPE_24M); + HAL_PRCM_SetSysPLL(PRCM_SYS_PLL_PARAM_HOSC24M); +#elif (BOARD_HOSC_CLOCK == HOSC_CLOCK_26M) + HAL_PRCM_SetHOSCType(PRCM_HOSC_TYPE_26M); + HAL_PRCM_SetSysPLL(PRCM_SYS_PLL_PARAM_HOSC26M); +#elif (BOARD_HOSC_CLOCK == HOSC_CLOCK_40M) + HAL_PRCM_SetHOSCType(PRCM_HOSC_TYPE_40M); + HAL_PRCM_SetSysPLL(PRCM_SYS_PLL_PARAM_HOSC40M); +#elif (BOARD_HOSC_CLOCK == HOSC_CLOCK_52M) + HAL_PRCM_SetHOSCType(PRCM_HOSC_TYPE_52M); + HAL_PRCM_SetSysPLL(PRCM_SYS_PLL_PARAM_HOSC52M); +#else + #error "Invalid HOSC value!" +#endif + +#if (defined(BOARD_LOSC_EXTERNAL) && BOARD_LOSC_EXTERNAL) + HAL_PRCM_SetLFCLKSource(PRCM_LFCLK_SRC_EXT32K); + HAL_PRCM_DisableInter32KCalib(); +#else + HAL_PRCM_SetLFCLKSource(PRCM_LFCLK_SRC_INTER32K); + HAL_PRCM_EnableInter32KCalib(); +#endif + + HAL_PRCM_SetCPUAClk(BOARD_CPU_CLK_SRC, BOARD_CPU_CLK_FACTOR); + HAL_PRCM_SetDevClock(BOARD_DEV_CLK_FACTOR); + HAL_CCM_BusSetClock(BOARD_AHB2_CLK_DIV, BOARD_APB_CLK_SRC, BOARD_APB_CLK_DIV); +} diff --git a/platform/mcu/xr871/project/common/board/board_common.h b/platform/mcu/xr871/project/common/board/board_common.h new file mode 100644 index 0000000000..a76b432142 --- /dev/null +++ b/platform/mcu/xr871/project/common/board/board_common.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _BOARD_COMMON_H_ +#define _BOARD_COMMON_H_ + +#include "driver/hal_board.h" +#include "driver/chip/hal_gpio.h" + +#ifdef __cplusplus +extern "C" { +#endif + +HAL_Status board_pinmux_cfg(HAL_BoardIoctlReq req, + const GPIO_PinMuxParam *pinmux, uint32_t count); +void board_chip_clock_init(void); + +#ifdef __cplusplus +} +#endif + +#endif /* _BOARD_COMMON_H_ */ diff --git a/platform/mcu/xr871/project/common/board/board_debug.h b/platform/mcu/xr871/project/common/board/board_debug.h new file mode 100644 index 0000000000..d800324ed3 --- /dev/null +++ b/platform/mcu/xr871/project/common/board/board_debug.h @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _BOARD_DEBUG_H_ +#define _BOARD_DEBUG_H_ + +#include +#include "sys/xr_util.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define BOARD_DBG_ON 1 +#define BOARD_WRN_ON 1 +#define BOARD_ERR_ON 1 +#define BOARD_ABORT_ON 0 + +#define BOARD_SYSLOG printf +#define BOARD_ABORT() sys_abort() + +#define BOARD_LOG(flags, fmt, arg...) \ + do { \ + if (flags) \ + BOARD_SYSLOG(fmt, ##arg); \ + } while (0) + +#define BOARD_DBG(fmt, arg...) BOARD_LOG(BOARD_DBG_ON, "[board] "fmt, ##arg) +#define BOARD_WRN(fmt, arg...) BOARD_LOG(BOARD_WRN_ON, "[board WRN] "fmt, ##arg) + +#define BOARD_ERR(fmt, arg...) \ + do { \ + BOARD_LOG(BOARD_ERR_ON, "[board ERR] %s():%d, "fmt, \ + __func__, __LINE__, ##arg); \ + if (BOARD_ABORT_ON) \ + BOARD_ABORT(); \ + } while (0) + +#ifdef __cplusplus +} +#endif + +#endif /* _BOARD_DEBUG_H_ */ diff --git a/platform/mcu/xr871/project/common/board/xr871_evb_main/board_config.c b/platform/mcu/xr871/project/common/board/xr871_evb_main/board_config.c new file mode 100644 index 0000000000..3f06e7c763 --- /dev/null +++ b/platform/mcu/xr871/project/common/board/xr871_evb_main/board_config.c @@ -0,0 +1,399 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include "../board_debug.h" +#include "board_config.h" +#include "driver/chip/hal_codec.h" + +#define BOARD_SWD_EN 1 + +static const GPIO_PinMuxParam g_pinmux_uart0[] = { + { GPIO_PORT_B, GPIO_PIN_0, { GPIOB_P0_F2_UART0_TX, GPIO_DRIVING_LEVEL_1, GPIO_PULL_UP } }, /* TX */ + { GPIO_PORT_B, GPIO_PIN_1, { GPIOB_P1_F2_UART0_RX, GPIO_DRIVING_LEVEL_1, GPIO_PULL_UP } }, /* RX */ +}; + +static const GPIO_PinMuxParam g_pinmux_uart1[] = { + { GPIO_PORT_A, GPIO_PIN_17, { GPIOA_P17_F5_UART1_TX, GPIO_DRIVING_LEVEL_1, GPIO_PULL_UP } }, /* TX */ + { GPIO_PORT_A, GPIO_PIN_18, { GPIOA_P18_F5_UART1_RX, GPIO_DRIVING_LEVEL_1, GPIO_PULL_UP } }, /* RX */ +}; + +static const GPIO_PinMuxParam g_pinmux_dmic[] = { + { GPIO_PORT_A, GPIO_PIN_21, { GPIOA_P21_F3_DMIC_CLK, GPIO_DRIVING_LEVEL_1, GPIO_PULL_NONE } }, + { GPIO_PORT_A, GPIO_PIN_22, { GPIOA_P22_F3_DMIC_DATA, GPIO_DRIVING_LEVEL_1, GPIO_PULL_NONE } }, +}; + +static const GPIO_PinMuxParam g_pinmux_i2s[] = { + { GPIO_PORT_A, GPIO_PIN_12, { GPIOA_P12_F4_I2S_MCLK, GPIO_DRIVING_LEVEL_1, GPIO_PULL_NONE } }, + { GPIO_PORT_A, GPIO_PIN_13, { GPIOA_P13_F4_I2S_BCLK, GPIO_DRIVING_LEVEL_1, GPIO_PULL_NONE } }, + { GPIO_PORT_A, GPIO_PIN_14, { GPIOA_P14_F4_I2S_DI, GPIO_DRIVING_LEVEL_1, GPIO_PULL_NONE } }, + { GPIO_PORT_A, GPIO_PIN_15, { GPIOA_P15_F4_I2S_DO, GPIO_DRIVING_LEVEL_1, GPIO_PULL_NONE } }, + { GPIO_PORT_A, GPIO_PIN_16, { GPIOA_P16_F4_I2S_LRCLK, GPIO_DRIVING_LEVEL_1, GPIO_PULL_NONE } }, +}; + +static const GPIO_PinMuxParam g_pinmux_irrx[] = { + { GPIO_PORT_A, GPIO_PIN_17, { GPIOA_P17_F3_IR_RX, GPIO_DRIVING_LEVEL_1, GPIO_PULL_UP } }, +}; + +static const GPIO_PinMuxParam g_pinmux_irtx[] = { + { GPIO_PORT_A, GPIO_PIN_18, { GPIOA_P18_F3_IR_TX, GPIO_DRIVING_LEVEL_1, GPIO_PULL_DOWN } }, +}; + +static const GPIO_PinMuxParam g_pinmux_i2c0[] = { + { GPIO_PORT_A, GPIO_PIN_4, { GPIOA_P4_F4_I2C0_SCL, GPIO_DRIVING_LEVEL_1, GPIO_PULL_UP } }, + { GPIO_PORT_A, GPIO_PIN_5, { GPIOA_P5_F4_I2C0_SDA, GPIO_DRIVING_LEVEL_1, GPIO_PULL_UP } }, +}; + +static const GPIO_PinMuxParam g_pinmux_i2c1[] = { + { GPIO_PORT_A, GPIO_PIN_17, { GPIOA_P17_F4_I2C1_SCL, GPIO_DRIVING_LEVEL_1, GPIO_PULL_UP } }, + { GPIO_PORT_A, GPIO_PIN_18, { GPIOA_P18_F4_I2C1_SDA, GPIO_DRIVING_LEVEL_1, GPIO_PULL_UP } }, +}; + +static const GPIO_PinMuxParam g_pinmux_adc[] = { + { GPIO_PORT_A, GPIO_PIN_8, { GPIOA_P8_F2_ADC_CH0, GPIO_DRIVING_LEVEL_1, GPIO_PULL_NONE } }, + { GPIO_PORT_A, GPIO_PIN_9, { GPIOA_P9_F2_ADC_CH1, GPIO_DRIVING_LEVEL_1, GPIO_PULL_NONE } }, + { GPIO_PORT_A, GPIO_PIN_10, { GPIOA_P10_F2_ADC_CH2, GPIO_DRIVING_LEVEL_1, GPIO_PULL_NONE } }, + { GPIO_PORT_A, GPIO_PIN_11, { GPIOA_P11_F2_ADC_CH3, GPIO_DRIVING_LEVEL_1, GPIO_PULL_NONE } }, + { GPIO_PORT_A, GPIO_PIN_12, { GPIOA_P12_F2_ADC_CH4, GPIO_DRIVING_LEVEL_1, GPIO_PULL_NONE } }, + { GPIO_PORT_A, GPIO_PIN_13, { GPIOA_P13_F2_ADC_CH5, GPIO_DRIVING_LEVEL_1, GPIO_PULL_NONE } }, + { GPIO_PORT_A, GPIO_PIN_14, { GPIOA_P14_F2_ADC_CH6, GPIO_DRIVING_LEVEL_1, GPIO_PULL_NONE } }, + { GPIO_PORT_A, GPIO_PIN_15, { GPIOA_P15_F2_ADC_CH7, GPIO_DRIVING_LEVEL_1, GPIO_PULL_NONE } }, +}; + +static const GPIO_PinMuxParam g_pinmux_spi0[] = { + { GPIO_PORT_B, GPIO_PIN_4, { GPIOB_P4_F2_SPI0_MOSI, GPIO_DRIVING_LEVEL_1, GPIO_PULL_NONE } }, + { GPIO_PORT_B, GPIO_PIN_5, { GPIOB_P5_F2_SPI0_MISO, GPIO_DRIVING_LEVEL_1, GPIO_PULL_NONE } }, + { GPIO_PORT_B, GPIO_PIN_7, { GPIOB_P7_F2_SPI0_CLK, GPIO_DRIVING_LEVEL_1, GPIO_PULL_NONE } }, +}; + +static const GPIO_PinMuxParam g_pinmux_spi0_cs0[] = { + { GPIO_PORT_B, GPIO_PIN_6, { GPIOB_P6_F2_SPI0_CS0, GPIO_DRIVING_LEVEL_1, GPIO_PULL_UP } }, +}; + +static const GPIO_PinMuxParam g_pinmux_spi1[] = { + { GPIO_PORT_A, GPIO_PIN_0, { GPIOA_P0_F2_SPI1_MOSI, GPIO_DRIVING_LEVEL_1, GPIO_PULL_NONE } }, + { GPIO_PORT_A, GPIO_PIN_1, { GPIOA_P1_F2_SPI1_MISO, GPIO_DRIVING_LEVEL_1, GPIO_PULL_NONE } }, + { GPIO_PORT_A, GPIO_PIN_2, { GPIOA_P2_F2_SPI1_CLK, GPIO_DRIVING_LEVEL_1, GPIO_PULL_NONE } }, +}; + +static const GPIO_PinMuxParam g_pinmux_spi1_cs0[] = { + { GPIO_PORT_A, GPIO_PIN_3, { GPIOA_P3_F2_SPI1_CS0, GPIO_DRIVING_LEVEL_1, GPIO_PULL_UP } }, +}; + +static const GPIO_PinMuxParam g_pinmux_spi1_cs1[] = { + { GPIO_PORT_A, GPIO_PIN_6, { GPIOA_P6_F3_SPI1_CS1, GPIO_DRIVING_LEVEL_1, GPIO_PULL_UP } }, +}; + +static const GPIO_PinMuxParam g_pinmux_spi1_cs2[] = { + { GPIO_PORT_A, GPIO_PIN_7, { GPIOA_P7_F3_SPI1_CS2, GPIO_DRIVING_LEVEL_1, GPIO_PULL_UP } }, +}; + +static const GPIO_PinMuxParam g_pinmux_flashc[] = { + { GPIO_PORT_B, GPIO_PIN_4, { GPIOB_P4_F5_FLASH_MOSI, GPIO_DRIVING_LEVEL_3, GPIO_PULL_NONE } }, + { GPIO_PORT_B, GPIO_PIN_5, { GPIOB_P5_F5_FLASH_MISO, GPIO_DRIVING_LEVEL_3, GPIO_PULL_NONE } }, + { GPIO_PORT_B, GPIO_PIN_7, { GPIOB_P7_F5_FLASH_CLK, GPIO_DRIVING_LEVEL_3, GPIO_PULL_NONE } }, + { GPIO_PORT_B, GPIO_PIN_6, { GPIOB_P6_F5_FLASH_CS, GPIO_DRIVING_LEVEL_3, GPIO_PULL_UP } }, +#if (!BOARD_SWD_EN) + { GPIO_PORT_B, GPIO_PIN_2, { GPIOB_P2_F5_FLASH_WP, GPIO_DRIVING_LEVEL_3, GPIO_PULL_UP } }, + { GPIO_PORT_B, GPIO_PIN_3, { GPIOB_P3_F5_FLASH_HOLD, GPIO_DRIVING_LEVEL_3, GPIO_PULL_UP } }, +#endif +}; + +/* flash */ +static const FlashBoardCfg g_flash_cfg[] = { + { + .type = FLASH_DRV_FLASHC, + .mode = FLASH_READ_DUAL_O_MODE, + .flashc.clk = (48 * 1000 * 1000), + }, +}; + +static const GPIO_PinMuxParam g_pinmux_pwm[] = { + { GPIO_PORT_A, GPIO_PIN_8, { GPIOA_P8_F3_PWM0_ECT0, GPIO_DRIVING_LEVEL_1, GPIO_PULL_NONE } }, + { GPIO_PORT_A, GPIO_PIN_9, { GPIOA_P9_F3_PWM1_ECT1, GPIO_DRIVING_LEVEL_1, GPIO_PULL_NONE } }, + { GPIO_PORT_A, GPIO_PIN_10, { GPIOA_P10_F3_PWM2_ECT2, GPIO_DRIVING_LEVEL_1, GPIO_PULL_NONE } }, + { GPIO_PORT_A, GPIO_PIN_11, { GPIOA_P11_F3_PWM3_ECT3, GPIO_DRIVING_LEVEL_1, GPIO_PULL_NONE } }, + { GPIO_PORT_A, GPIO_PIN_12, { GPIOA_P12_F3_PWM4_ECT4, GPIO_DRIVING_LEVEL_1, GPIO_PULL_NONE } }, + { GPIO_PORT_A, GPIO_PIN_13, { GPIOA_P13_F3_PWM5_ECT5, GPIO_DRIVING_LEVEL_1, GPIO_PULL_NONE } }, + { GPIO_PORT_A, GPIO_PIN_14, { GPIOA_P14_F3_PWM6_ECT6, GPIO_DRIVING_LEVEL_1, GPIO_PULL_NONE } }, + { GPIO_PORT_A, GPIO_PIN_15, { GPIOA_P15_F3_PWM7_ECT7, GPIO_DRIVING_LEVEL_1, GPIO_PULL_NONE } }, +}; + +#define BOARD_SD0_DATA_BITS 1 +#define BOARD_SD0_DET_VALID 0 +#define BOARD_SD0_DET_PORT GPIO_PORT_A +#define BOARD_SD0_DET_PIN GPIO_PIN_3 +#define BOARD_SD0_DET_PIN_MODE GPIOA_P3_F6_EINTA3 + +static const GPIO_PinMuxParam g_pinmux_sd0[BOARD_SD0_DATA_BITS + 2] = { + { GPIO_PORT_A, GPIO_PIN_0, { GPIOA_P0_F3_SD_CMD, GPIO_DRIVING_LEVEL_2, GPIO_PULL_UP } }, /* CMD */ + { GPIO_PORT_A, GPIO_PIN_2, { GPIOA_P2_F3_SD_CLK, GPIO_DRIVING_LEVEL_2, GPIO_PULL_UP } }, /* CLK */ + { GPIO_PORT_A, GPIO_PIN_1, { GPIOA_P1_F3_SD_DATA0, GPIO_DRIVING_LEVEL_2, GPIO_PULL_UP } }, /* D0 */ +// { GPIO_PORT_A, GPIO_PIN_3, { GPIOA_P3_F3_SD_DATA1, GPIO_DRIVING_LEVEL_2, GPIO_PULL_UP } }, /* D1 */ +// { GPIO_PORT_A, GPIO_PIN_4, { GPIOA_P4_F3_SD_DATA2, GPIO_DRIVING_LEVEL_2, GPIO_PULL_UP } }, /* D2 */ +// { GPIO_PORT_A, GPIO_PIN_5, { GPIOA_P5_F3_SD_DATA3, GPIO_DRIVING_LEVEL_2, GPIO_PULL_NONE } }, /* D3 */ +}; + +static const GPIO_PinMuxParam g_pinmux_sd0_det[] = { + { BOARD_SD0_DET_PORT, BOARD_SD0_DET_PIN, { BOARD_SD0_DET_PIN_MODE, GPIO_DRIVING_LEVEL_2, GPIO_PULL_NONE } }, /* DET */ +}; + +static const HAL_SDCGPIOCfg g_sd0_cfg = { + .data_bits = BOARD_SD0_DATA_BITS, + .has_detect_gpio = BOARD_SD0_DET_VALID, + .detect_port = BOARD_SD0_DET_PORT, + .detect_pin = BOARD_SD0_DET_PIN +}; + +#define BOARD_SPK_PORT GPIO_PORT_A +#define BOARD_SPK_PIN GPIO_PIN_3 + +static const GPIO_PinMuxParam g_pinmux_spk[] = { + { BOARD_SPK_PORT, BOARD_SPK_PIN, { GPIOx_Pn_F1_OUTPUT, GPIO_DRIVING_LEVEL_1, GPIO_PULL_UP } }, +}; + +static const SPK_Param g_spk_cfg = { + .ctrl_port = BOARD_SPK_PORT, + .ctrl_pin = BOARD_SPK_PIN, + .ctrl_on_state = GPIO_PIN_HIGH, + .ctrl_off_state = GPIO_PIN_LOW +}; + +GPIO_PinMuxParam g_pinmux_csi[] = { + { GPIO_PORT_A, GPIO_PIN_0, { GPIOA_P0_F5_CSI_D0, GPIO_DRIVING_LEVEL_1, GPIO_PULL_NONE } }, + { GPIO_PORT_A, GPIO_PIN_1, { GPIOA_P1_F5_CSI_D1, GPIO_DRIVING_LEVEL_1, GPIO_PULL_NONE } }, + { GPIO_PORT_A, GPIO_PIN_2, { GPIOA_P2_F5_CSI_D2, GPIO_DRIVING_LEVEL_1, GPIO_PULL_NONE } }, + { GPIO_PORT_A, GPIO_PIN_3, { GPIOA_P3_F5_CSI_D3, GPIO_DRIVING_LEVEL_1, GPIO_PULL_NONE } }, + { GPIO_PORT_A, GPIO_PIN_4, { GPIOA_P4_F4_CSI_D4, GPIO_DRIVING_LEVEL_1, GPIO_PULL_NONE } }, + { GPIO_PORT_A, GPIO_PIN_5, { GPIOA_P5_F5_CSI_D5, GPIO_DRIVING_LEVEL_1, GPIO_PULL_NONE } }, + { GPIO_PORT_A, GPIO_PIN_6, { GPIOA_P6_F5_CSI_D6, GPIO_DRIVING_LEVEL_1, GPIO_PULL_NONE } }, + { GPIO_PORT_A, GPIO_PIN_7, { GPIOA_P7_F5_CSI_D7, GPIO_DRIVING_LEVEL_1, GPIO_PULL_NONE } }, + { GPIO_PORT_A, GPIO_PIN_8, { GPIOA_P8_F5_CSI_PCLK, GPIO_DRIVING_LEVEL_1, GPIO_PULL_NONE } }, + { GPIO_PORT_A, GPIO_PIN_9, { GPIOA_P9_F5_CSI_MCLK, GPIO_DRIVING_LEVEL_1, GPIO_PULL_NONE } }, + { GPIO_PORT_A, GPIO_PIN_10, { GPIOA_P10_F5_CSI_HSYNC, GPIO_DRIVING_LEVEL_1, GPIO_PULL_NONE } }, + { GPIO_PORT_A, GPIO_PIN_11, { GPIOA_P11_F5_CSI_VSYNC, GPIO_DRIVING_LEVEL_1, GPIO_PULL_NONE } }, +}; + +struct board_pinmux_info { + const GPIO_PinMuxParam *pinmux; + uint32_t count; +}; + +#define BOARD_PINMUX_INFO_MAX 2 + +static HAL_Status board_get_pinmux_info(uint32_t major, uint32_t minor, uint32_t param, + struct board_pinmux_info info[]) +{ + HAL_Status ret = HAL_OK; + + switch (major) { + case HAL_DEV_MAJOR_UART: + if (minor == UART0_ID) { + info[0].pinmux = g_pinmux_uart0; + info[0].count = HAL_ARRAY_SIZE(g_pinmux_uart0); + } else if (minor == UART1_ID) { + info[0].pinmux = g_pinmux_uart1; + info[0].count = HAL_ARRAY_SIZE(g_pinmux_uart1); + } else { + ret = HAL_INVALID; + } + break; + case HAL_DEV_MAJOR_I2C: + if (minor == I2C0_ID) { + info[0].pinmux = g_pinmux_i2c0; + info[0].count = HAL_ARRAY_SIZE(g_pinmux_i2c0); + } else if (minor == I2C1_ID) { + info[0].pinmux = g_pinmux_i2c1; + info[0].count = HAL_ARRAY_SIZE(g_pinmux_i2c1); + } else { + ret = HAL_INVALID; + } + break; + case HAL_DEV_MAJOR_SPI: + if (minor == SPI0) { + info[0].pinmux = g_pinmux_spi0; + info[0].count = HAL_ARRAY_SIZE(g_pinmux_spi0); + info[1].pinmux = g_pinmux_spi0_cs0; + info[1].count = HAL_ARRAY_SIZE(g_pinmux_spi0_cs0); + } else if (minor == SPI1) { + info[0].pinmux = g_pinmux_spi1; + info[0].count = HAL_ARRAY_SIZE(g_pinmux_spi1); + switch (param) { + case SPI_TCTRL_SS_SEL_SS0: + info[1].pinmux = g_pinmux_spi1_cs0; + info[1].count = HAL_ARRAY_SIZE(g_pinmux_spi1_cs0); + break; + case SPI_TCTRL_SS_SEL_SS1: + info[1].pinmux = g_pinmux_spi1_cs1; + info[1].count = HAL_ARRAY_SIZE(g_pinmux_spi1_cs1); + break; + case SPI_TCTRL_SS_SEL_SS2: + info[1].pinmux = g_pinmux_spi1_cs2; + info[1].count = HAL_ARRAY_SIZE(g_pinmux_spi1_cs2); + break; + default: + ret = HAL_INVALID; + break; + } + } else { + ret = HAL_INVALID; + } + break; + case HAL_DEV_MAJOR_IRRX: + info[0].pinmux = g_pinmux_irrx; + info[0].count = HAL_ARRAY_SIZE(g_pinmux_irrx); + break; + case HAL_DEV_MAJOR_IRTX: + info[0].pinmux = g_pinmux_irtx; + info[0].count = HAL_ARRAY_SIZE(g_pinmux_irtx); + break; + case HAL_DEV_MAJOR_I2S: + info[0].pinmux = g_pinmux_i2s; + info[0].count = HAL_ARRAY_SIZE(g_pinmux_i2s); + break; + case HAL_DEV_MAJOR_DMIC: + info[0].pinmux = g_pinmux_dmic; + info[0].count = HAL_ARRAY_SIZE(g_pinmux_dmic); + break; + case HAL_DEV_MAJOR_ADC: + if (minor < HAL_ARRAY_SIZE(g_pinmux_adc)) { + info[0].pinmux = &g_pinmux_adc[minor]; + info[0].count = 1; + } else { + ret = HAL_INVALID; + } + break; + case HAL_DEV_MAJOR_PWM: + if (minor < HAL_ARRAY_SIZE(g_pinmux_pwm)) { + info[0].pinmux = &g_pinmux_pwm[minor]; + info[0].count = 1; + } else if (minor != ADC_CHANNEL_8) { + ret = HAL_INVALID; + } + break; + case HAL_DEV_MAJOR_FLASHC: + info[0].pinmux = g_pinmux_flashc; + info[0].count = HAL_ARRAY_SIZE(g_pinmux_flashc); + break; + case HAL_DEV_MAJOR_SDC: + info[0].pinmux = g_pinmux_sd0; + info[0].count = HAL_ARRAY_SIZE(g_pinmux_sd0); + info[1].pinmux = g_pinmux_sd0_det; + info[1].count = 1; + break; + case HAL_DEV_MAJOR_CSI: + info[0].pinmux = g_pinmux_csi; + info[0].count = HAL_ARRAY_SIZE(g_pinmux_csi); + break; + case HAL_DEV_MAJOR_AUDIO_CODEC: + info[0].pinmux = g_pinmux_spk; + info[0].count = HAL_ARRAY_SIZE(g_pinmux_spk); + break; + default: + BOARD_ERR("unknow major %u\n", major); + ret = HAL_INVALID; + } + + return ret; +} + +static HAL_Status board_get_cfg(uint32_t major, uint32_t minor, uint32_t param) +{ + HAL_Status ret = HAL_OK; + + switch (major) { + case HAL_DEV_MAJOR_SDC: + *((HAL_SDCGPIOCfg **)param) = (HAL_SDCGPIOCfg *)&g_sd0_cfg; + break; + case HAL_DEV_MAJOR_FLASH: + if (minor <= (sizeof(g_flash_cfg) / sizeof(FlashBoardCfg))) + *((FlashBoardCfg **)param) = (FlashBoardCfg *)&g_flash_cfg[minor]; + else + *((FlashBoardCfg **)param) = NULL; + break; + case HAL_DEV_MAJOR_AUDIO_CODEC: + *((SPK_Param **)param) = (SPK_Param *)&g_spk_cfg; + break; + default: + BOARD_ERR("unknow major %u\n", major); + ret = HAL_INVALID; + } + + return ret; +} + +HAL_Status board_ioctl(HAL_BoardIoctlReq req, uint32_t param0, uint32_t param1) +{ + HAL_Status ret = HAL_OK; + uint32_t major, minor, i; + struct board_pinmux_info info[BOARD_PINMUX_INFO_MAX]; + + switch (req) { + case HAL_BIR_PINMUX_INIT: + case HAL_BIR_PINMUX_DEINIT: + memset(info, 0, sizeof(info)); + major = HAL_DEV_MAJOR((HAL_Dev_t)param0); + minor = HAL_DEV_MINOR((HAL_Dev_t)param0); + ret = board_get_pinmux_info(major, minor, param1, info); + if (major == HAL_DEV_MAJOR_SDC) { + if (param1 == SDCGPIO_BAS) { + board_pinmux_cfg(req, info[0].pinmux, info[0].count); + } else if (param1 == SDCGPIO_DET) { + board_pinmux_cfg(req, info[1].pinmux, info[1].count); + } + } else { + for (i = 0; i < BOARD_PINMUX_INFO_MAX; ++i) { + if (info[i].pinmux != NULL && info[i].count != 0) { + board_pinmux_cfg(req, info[i].pinmux, info[i].count); + } else { + break; + } + } + } + break; + case HAL_BIR_CHIP_CLOCK_INIT: + board_chip_clock_init(); + break; + case HAL_BIR_GET_CFG: + major = HAL_DEV_MAJOR((HAL_Dev_t)param0); + minor = HAL_DEV_MINOR((HAL_Dev_t)param0); + ret = board_get_cfg(major, minor, param1); + break; + default: + BOARD_ERR("req %d not suppport\n", req); + ret = HAL_INVALID; + break; + } + + if (ret != HAL_OK) { + BOARD_ERR("req %d, param0 %#x, param1 %#x, ret %d\n", req, param0, param1, ret); + } + + return ret; +} diff --git a/platform/mcu/xr871/project/common/board/xr871_evb_main/board_config.h b/platform/mcu/xr871/project/common/board/xr871_evb_main/board_config.h new file mode 100644 index 0000000000..b96c3e6a85 --- /dev/null +++ b/platform/mcu/xr871/project/common/board/xr871_evb_main/board_config.h @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _BOARD_CONFIG_H_ +#define _BOARD_CONFIG_H_ + +#include "driver/chip/hal_chip.h" +#include "driver/hal_board.h" +#include "driver/hal_dev.h" +#include "../board_common.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * board configuration + */ + +/* chip clock */ +#define BOARD_HOSC_CLOCK HOSC_CLOCK_24M +#define BOARD_LOSC_EXTERNAL 1 /* 0: inter 32k, 1: external 32k */ +#define BOARD_CPU_CLK_SRC PRCM_CPU_CLK_SRC_SYSCLK +#define BOARD_CPU_CLK_FACTOR PRCM_SYS_CLK_FACTOR_160M +#define BOARD_DEV_CLK_FACTOR PRCM_DEV_CLK_FACTOR_192M +#define BOARD_AHB2_CLK_DIV CCM_AHB2_CLK_DIV_2 +#define BOARD_APB_CLK_SRC CCM_APB_CLK_SRC_HFCLK +#define BOARD_APB_CLK_DIV CCM_APB_CLK_DIV_1 + +/* uart */ +#define BOARD_MAIN_UART_ID UART0_ID /* debug and console */ +#define BOARD_SUB_UART_ID UART0_ID /* debug for netos */ + +#define BOARD_UART_BAUD_RATE 115200 +#define BOARD_UART_PARITY UART_PARITY_NONE +#define BOARD_UART_STOP_BITS UART_STOP_BITS_1 +#define BOARD_UART_DATA_BITS UART_DATA_BITS_8 +#define BOARD_UART_HW_FLOW_CTRL 0 + +/* spi */ +#define BOARD_SPI_MCLK (48 * 1000 * 1000) +#define BOARD_SPI_CS_LEVEL 0 +#define BOARD_SPI_PORT SPI0 + +/* sound card0 */ +#define BOARD_SOUNDCARD0_I2C_ID I2C0_ID +#define BOARD_SOUNDCARD0_I2C_ADDR_MODE I2C_ADDR_MODE_7BIT +#define BOARD_SOUNDCARD0_I2C_CLK 400000 +#define BOARD_SOUNDCARD0_CODEC_NAME "AC101" +#define BOARD_SOUNDCARD0_CODEC_WRITE HAL_I2C_Master_Transmit_Mem_IT +#define BOARD_SOUNDCARD0_CODEC_READ HAL_I2C_Master_Receive_Mem_IT + +#ifdef __cplusplus +} +#endif + +#endif /* _BOARD_CONFIG_H_ */ diff --git a/platform/mcu/xr871/project/common/cmd/cmd.h b/platform/mcu/xr871/project/common/cmd/cmd.h new file mode 100644 index 0000000000..f212897796 --- /dev/null +++ b/platform/mcu/xr871/project/common/cmd/cmd.h @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _CMD_H_ +#define _CMD_H_ + +#include "common/cmd/cmd_util.h" + +#include "common/cmd/cmd_echo.h" +#include "common/cmd/cmd_mem.h" +#include "common/cmd/cmd_heap.h" +#include "common/cmd/cmd_upgrade.h" +#include "common/cmd/cmd_sysinfo.h" + +#include "common/cmd/cmd_clock.h" +#include "common/cmd/cmd_uart.h" +#include "common/cmd/cmd_timer.h" +#include "common/cmd/cmd_wdg.h" +#include "common/cmd/cmd_rtc.h" +#include "common/cmd/cmd_irrx.h" +#include "common/cmd/cmd_irtx.h" +#include "common/cmd/cmd_ce.h" +#include "common/cmd/cmd_i2c.h" +#include "common/cmd/cmd_adc.h" +#include "common/cmd/cmd_flash.h" +#include "common/cmd/cmd_pwm.h" +#include "common/cmd/cmd_sd.h" +#include "common/cmd/cmd_efpg.h" +#include "common/cmd/cmd_audio.h" + +#include "common/cmd/cmd_pm.h" +#include "common/cmd/cmd_fs.h" +#include "common/cmd/cmd_cedarx.h" + +#include "common/cmd/cmd_wlan.h" +#include "common/cmd/cmd_ifconfig.h" +#include "common/cmd/cmd_smart_config.h" +#include "common/cmd/cmd_airkiss.h" +#include "common/cmd/cmd_ping.h" +#include "common/cmd/cmd_iperf.h" +#include "common/cmd/cmd_sntp.h" +#include "common/cmd/cmd_httpc.h" +#include "common/cmd/cmd_httpd.h" +#include "common/cmd/tls/cmd_tls.h" +#include "common/cmd/cmd_mqtt.h" +#include "common/cmd/cmd_ota.h" +#include "common/cmd/cmd_dhcpd.h" +#include "common/cmd/cmd_nopoll.h" + +#include "common/cmd/cmd_netcmd.h" +#include "common/cmd/cmd_etf.h" +#include "common/cmd/cmd_broadcast.h" +#include "common/cmd/cmd_arp.h" + +#endif /* _CMD_H_ */ diff --git a/platform/mcu/xr871/project/common/cmd/cmd_adc.c b/platform/mcu/xr871/project/common/cmd/cmd_adc.c new file mode 100644 index 0000000000..e7e1716645 --- /dev/null +++ b/platform/mcu/xr871/project/common/cmd/cmd_adc.c @@ -0,0 +1,407 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "cmd_util.h" +#include "cmd_adc.h" +#include "driver/chip/hal_gpio.h" +#include "driver/chip/hal_adc.h" + +#define CMD_ADC_TEST_DBG 0 + +static uint8_t cmd_adc_pass; + +static struct cmd_adc_test_priv{ + uint8_t enable; + uint8_t irq_mode; + uint32_t low_value; + uint32_t high_value; + uint32_t voltage; + uint32_t deviation; +} priv[ADC_CHANNEL_NUM]; + +static enum cmd_status cmd_adc_output_check(uint32_t data, uint32_t voltage, + uint32_t deviation) +{ + uint32_t voltage_low, voltage_high; + + voltage_low = (voltage < deviation) ? 0 : (voltage - deviation); + voltage_high = ((voltage + deviation) > 2500) ? 2500 : (voltage + deviation); + + voltage_low = voltage_low * 4096 / 2500; + voltage_high = voltage_high * 4096 / 2500; + + if ((data >= voltage_low) && (data <= voltage_high)) { +#if CMD_ADC_TEST_DBG + CMD_DBG("ADC: data = %u, voltage_low = %u, voltage_high = %u\n", + data, voltage_low, voltage_high); +#endif /* CMD_ADC_TEST_DBG */ + return CMD_STATUS_OK; + } else { + CMD_ERR("ADC: data = %u, voltage_low = %u, voltage_high = %u\n", + data, voltage_low, voltage_high); + return CMD_STATUS_FAIL; + } +} + +static void cmd_adc_irq_cb(void *arg) +{ + uint32_t channel, data; + ADC_IRQState irq_state; + + for (channel = ADC_CHANNEL_0; channel < ADC_CHANNEL_NUM; channel++) { + if (priv[channel].enable == 0) + continue; + + data = HAL_ADC_GetValue((ADC_Channel)channel); + irq_state = HAL_ADC_GetIRQState((ADC_Channel)channel); + +#if CMD_ADC_TEST_DBG + CMD_DBG("channel = %u, irq_mode = %u, irq_state = %d, low_value = %u, high_value = %u, data = %u\n", + channel, priv[channel].irq_mode, irq_state, priv[channel].low_value, + priv[channel].high_value, data); +#endif /* CMD_ADC_TEST_DBG */ + + if (CMD_STATUS_OK != cmd_adc_output_check(data, priv[channel].voltage, priv[channel].deviation)) + cmd_adc_pass = 0; + + switch (priv[channel].irq_mode) { + case ADC_IRQ_NONE: + cmd_adc_pass = 0; + CMD_ERR("irq_mode: ADC_IRQ_NONE, irq_state = %d\n", irq_state); + break; + case ADC_IRQ_DATA: + if (irq_state != ADC_DATA_IRQ) { + cmd_adc_pass = 0; + CMD_ERR("irq_mode: ADC_IRQ_DATA, irq_state = %d\n", irq_state); + } + break; + case ADC_IRQ_LOW: + if ((irq_state != ADC_LOW_IRQ) || (data > priv[channel].low_value)) { + cmd_adc_pass = 0; + CMD_ERR("irq_mode: ADC_IRQ_LOW, irq_state = %d, low_value = %u, data = %u\n", + irq_state, priv[channel].low_value, data); + } + break; + case ADC_IRQ_HIGH: + if ((irq_state != ADC_HIGH_IRQ) || (data < priv[channel].high_value)) { + cmd_adc_pass = 0; + CMD_ERR("irq_mode: ADC_IRQ_HIGH, irq_state = %d, high_value = %u, data = %u\n", + irq_state, priv[channel].high_value, data); + } + break; + case ADC_IRQ_LOW_DATA: + if (irq_state == ADC_LOW_DATA_IRQ) { + if (data > priv[channel].low_value) { + cmd_adc_pass = 0; + CMD_ERR("irq_state = %d, low_value = %u, data = %u\n", + irq_state, priv[channel].low_value, data); + } + } else if (irq_state != ADC_DATA_IRQ) { + cmd_adc_pass = 0; + CMD_ERR("irq_mode: ADC_IRQ_LOW_DATA, irq_state = %d\n", irq_state); + } + break; + case ADC_IRQ_HIGH_DATA: + if (irq_state == ADC_HIGH_DATA_IRQ) { + if (data < priv[channel].high_value) { + cmd_adc_pass = 0; + CMD_ERR("irq_state = %d, high_value = %u, data = %u\n", + irq_state, priv[channel].high_value, data); + } + } else if (irq_state != ADC_DATA_IRQ) { + cmd_adc_pass = 0; + CMD_ERR("irq_mode: ADC_IRQ_HIGH_DATA, irq_state = %d\n", irq_state); + } + break; + case ADC_IRQ_LOW_HIGH: + if (irq_state == ADC_LOW_IRQ) { + if (data > priv[channel].low_value) { + cmd_adc_pass = 0; + CMD_ERR("irq_state = %d, low_value = %u, data = %u\n", + irq_state, priv[channel].low_value, data); + } + } else if (irq_state == ADC_HIGH_IRQ) { + if (data < priv[channel].high_value) { + cmd_adc_pass = 0; + CMD_ERR("irq_state = %d, high_value = %u, data = %u\n", + irq_state, priv[channel].high_value, data); + } + } else { + cmd_adc_pass = 0; + CMD_ERR("irq_mode: ADC_IRQ_LOW_HIGH, irq_state = %d\n", irq_state); + } + break; + case ADC_IRQ_LOW_HIGH_DATA: + if (irq_state == ADC_LOW_DATA_IRQ) { + if (data > priv[channel].low_value) { + cmd_adc_pass = 0; + CMD_ERR("irq_state = %d, low_value = %u, data = %u\n", + irq_state, priv[channel].low_value, data); + } + } else if (irq_state == ADC_HIGH_DATA_IRQ) { + if (data < priv[channel].high_value) { + cmd_adc_pass = 0; + CMD_ERR("irq_state = %d, high_value = %u, data = %u\n", + irq_state, priv[channel].high_value, data); + } + } else if (irq_state != ADC_DATA_IRQ) { + cmd_adc_pass = 0; + CMD_ERR("irq_mode: ADC_IRQ_LOW_HIGH_DATA, irq_state = %d\n", irq_state); + } + break; + default: + cmd_adc_pass = 0; + CMD_ERR("channel = %u, irq_mode = %u\n", channel, priv[channel].irq_mode); + } + } + + return; +} + +static enum cmd_status cmd_adc_init_exec(char *cmd) +{ + int cnt; + uint32_t freq; + uint32_t delay; + ADC_InitParam adc_param; + HAL_Status hal_status; + + cnt = cmd_sscanf(cmd, "f=%u d=%u", &freq, &delay); + if (cnt != 2) { + CMD_ERR("cmd_sscanf return: cnt = %d\n", cnt); + return CMD_STATUS_INVALID_ARG; + } + + if ((freq < 1000) || (freq > 1000000)) { + CMD_ERR("invalid freq %u\n", freq); + return CMD_STATUS_INVALID_ARG; + } + + if ((delay >> 8) != 0) { + CMD_ERR("invalid delay %u\n", delay); + return CMD_STATUS_INVALID_ARG; + } + + cmd_memset(priv, 0, ADC_CHANNEL_NUM * sizeof(struct cmd_adc_test_priv)); + + adc_param.freq = freq; + adc_param.delay = delay; + + hal_status = HAL_ADC_Init(&adc_param); + if (hal_status == HAL_OK) { + return CMD_STATUS_OK; + } else { + CMD_ERR("HAL_ADC_Init return: hal_status = %d\n", hal_status); + return CMD_STATUS_FAIL; + } +} + +static enum cmd_status cmd_adc_deinit_exec(char *cmd) +{ + HAL_Status hal_status; + + hal_status = HAL_ADC_DeInit(); + if (hal_status == HAL_OK) { + return CMD_STATUS_OK; + } else { + CMD_ERR("HAL_ADC_DeInit return: hal_status = %d\n", hal_status); + return CMD_STATUS_FAIL; + } +} + +static enum cmd_status cmd_adc_conv_polling_exec(char *cmd) +{ + int cnt; + uint32_t channel, voltage, deviation; + uint32_t data; + HAL_Status hal_status; + + cnt = cmd_sscanf(cmd, "c=%u v=%u d=%u", &channel, &voltage, &deviation); + if (cnt != 3) { + CMD_ERR("cmd_sscanf return: cnt = %d\n", cnt); + return CMD_STATUS_INVALID_ARG; + } + + if (channel >= ADC_CHANNEL_NUM) { + CMD_ERR("invalid channel %u\n", channel); + return CMD_STATUS_INVALID_ARG; + } + + if (voltage > 2500) { + CMD_ERR("invalid voltage %u\n", voltage); + return CMD_STATUS_INVALID_ARG; + } + + if (deviation > 2500) { + CMD_ERR("invalid deviation %u\n", deviation); + return CMD_STATUS_INVALID_ARG; + } + + hal_status = HAL_ADC_Conv_Polling((ADC_Channel)channel, &data, 10000); + if (hal_status != HAL_OK) { + CMD_ERR("HAL_ADC_Conv_Polling return: hal_status = %d\n", hal_status); + return CMD_STATUS_FAIL; + } + + return cmd_adc_output_check(data, voltage, deviation); +} + +static enum cmd_status cmd_adc_config_exec(char *cmd) +{ + int cnt; + uint32_t channel, enable, irq_mode, low_value, high_value, voltage, deviation; + HAL_Status hal_status; + + cnt = cmd_sscanf(cmd, "c=%u e=%u i=%u l=%u h=%u v=%u d=%u", + &channel, &enable, &irq_mode, &low_value, + &high_value, &voltage, &deviation); + if (cnt != 7) { + CMD_ERR("cmd_sscanf return: cnt = %d\n", cnt); + return CMD_STATUS_INVALID_ARG; + } + + if (channel >= ADC_CHANNEL_NUM) { + CMD_ERR("invalid channel %u\n", channel); + return CMD_STATUS_INVALID_ARG; + } + + if (enable > 1) { + CMD_ERR("invalid enable %u\n", enable); + return CMD_STATUS_INVALID_ARG; + } + + if (irq_mode > 7) { + CMD_ERR("invalid irq_mode %u\n", irq_mode); + return CMD_STATUS_INVALID_ARG; + } + + if ((low_value >> 12) != 0) { + CMD_ERR("invalid low_value %u\n", low_value); + return CMD_STATUS_INVALID_ARG; + } + + if ((high_value >> 12) != 0) { + CMD_ERR("invalid high_value %u\n", high_value); + return CMD_STATUS_INVALID_ARG; + } + + if (voltage > 2500) { + CMD_ERR("invalid voltage %u\n", voltage); + return CMD_STATUS_INVALID_ARG; + } + + if (deviation > 2500) { + CMD_ERR("invalid deviation %u\n", deviation); + return CMD_STATUS_INVALID_ARG; + } + + hal_status = HAL_ADC_ConfigChannel((ADC_Channel)channel, (ADC_Select)enable, + (ADC_IRQMode)irq_mode, low_value, high_value); + if (hal_status != HAL_OK) { + CMD_ERR("HAL_ADC_ConfigChannel return: hal_status = %d\n", hal_status); + return CMD_STATUS_FAIL; + } + + priv[channel].enable = (uint8_t)enable; + priv[channel].irq_mode = (uint8_t)irq_mode; + priv[channel].low_value = low_value; + priv[channel].high_value = high_value; + priv[channel].voltage = voltage; + priv[channel].deviation = deviation; + +#if CMD_ADC_TEST_DBG + CMD_DBG("ADC config: channel = %u, enable = %u, irq_mode = %u, low_value = %u, high_value = %u, voltage = %u, deviation = %u\n", + channel, enable, irq_mode, low_value, high_value, voltage, deviation); +#endif /* CMD_ADC_TEST_DBG */ + + return CMD_STATUS_OK; +} + +static enum cmd_status cmd_adc_conv_it_start_exec(char *cmd) +{ + ADC_Channel chan; + HAL_Status hal_status; + + cmd_adc_pass = 1; + + for (chan = ADC_CHANNEL_0; chan < ADC_CHANNEL_NUM; chan++) { + hal_status = HAL_ADC_EnableIRQCallback(chan, cmd_adc_irq_cb, NULL); + if (hal_status != HAL_OK) { + CMD_ERR("HAL_ADC_EnableIRQCallback return: hal_status = %d\n", hal_status); + return CMD_STATUS_FAIL; + } + } + + hal_status = HAL_ADC_Start_Conv_IT(); + if (hal_status != HAL_OK) { + CMD_ERR("HAL_ADC_Start_Conv_IT return: hal_status = %d\n", hal_status); + return CMD_STATUS_FAIL; + } + + return CMD_STATUS_OK; +} + +static enum cmd_status cmd_adc_conv_it_stop_exec(char *cmd) +{ + ADC_Channel chan; + HAL_Status hal_status; + + hal_status = HAL_ADC_Stop_Conv_IT(); + if (hal_status != HAL_OK) { + CMD_ERR("HAL_ADC_Stop_Conv_IT return: hal_status = %d\n", hal_status); + return CMD_STATUS_FAIL; + } + + for (chan = ADC_CHANNEL_0; chan < ADC_CHANNEL_NUM; chan++) { + hal_status = HAL_ADC_DisableIRQCallback(chan); + if (hal_status != HAL_OK) { + CMD_ERR("HAL_ADC_DisableIRQCallback return: hal_status = %d\n", hal_status); + return CMD_STATUS_FAIL; + } + } + + if (cmd_adc_pass == 1) + return CMD_STATUS_OK; + else + return CMD_STATUS_FAIL; +} + +static struct cmd2_data g_adc_cmds[] = { + { "init ", 5, cmd_adc_init_exec }, + { "deinit", 6, cmd_adc_deinit_exec }, + { "conv-polling ", 13, cmd_adc_conv_polling_exec }, + { "config ", 7, cmd_adc_config_exec }, + { "conv-it-start", 13, cmd_adc_conv_it_start_exec }, + { "conv-it-stop", 12, cmd_adc_conv_it_stop_exec }, +}; + +enum cmd_status cmd_adc_exec(char *cmd) +{ + return cmd2_exec(cmd, g_adc_cmds, cmd_nitems(g_adc_cmds)); +} diff --git a/platform/mcu/xr871/project/common/cmd/cmd_adc.h b/platform/mcu/xr871/project/common/cmd/cmd_adc.h new file mode 100644 index 0000000000..a07c81cc8f --- /dev/null +++ b/platform/mcu/xr871/project/common/cmd/cmd_adc.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _CMD_ADC_H_ +#define _CMD_ADC_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +enum cmd_status cmd_adc_exec(char *cmd); + +#ifdef __cplusplus +} +#endif + +#endif /* _CMD_ADC_H_ */ diff --git a/platform/mcu/xr871/project/common/cmd/cmd_airkiss.c b/platform/mcu/xr871/project/common/cmd/cmd_airkiss.c new file mode 100644 index 0000000000..ba38aa7df5 --- /dev/null +++ b/platform/mcu/xr871/project/common/cmd/cmd_airkiss.c @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "cmd_util.h" +#include "common/framework/net_ctrl.h" +#include "net/wlan/wlan.h" +#include "net/wlan/wlan_defs.h" +#include + +#define AK_TIME_OUT_MS 120000 +#define AK_ACK_TIME_OUT_MS 30000 +static char *key = "1234567812345678"; + +static OS_Thread_t g_ak_ctrl_thread; +#define AK_CTRL_THREAD_STACK_SIZE (1 * 1024) + +static void ak_task(void *arg) +{ + int ret; + wlan_airkiss_status_t status; + wlan_airkiss_result_t result; + + memset(&result, 0, sizeof(result)); + + net_switch_mode(WLAN_MODE_MONITOR); + + status = wlan_airkiss_start(g_wlan_netif, AK_TIME_OUT_MS, &result); + + net_switch_mode(WLAN_MODE_STA); + + if (status == WLAN_AIRKISS_SUCCESS) { + CMD_DBG("ssid: %.32s\n", (char *)result.ssid); + CMD_DBG("psk: %s\n", (char *)result.passphrase); + CMD_DBG("random: %d\n", result.random_num); + } else { + CMD_DBG ("airkiss failed %d\n", status); + OS_ThreadDelete(&g_ak_ctrl_thread); + return; + } + + if (result.passphrase[0] != '\0') { + wlan_sta_set(result.ssid, result.ssid_len, result.passphrase); + } else { + wlan_sta_set(result.ssid, result.ssid_len, NULL); + } + + wlan_sta_enable(); + + ret = wlan_airkiss_ack_start(g_wlan_netif, result.random_num, AK_ACK_TIME_OUT_MS); + if (ret < 0) + CMD_DBG("airkiss ack error, ap connect time out\n"); + + OS_ThreadDelete(&g_ak_ctrl_thread); +} + +static int ak_create() +{ + if (OS_ThreadCreate(&g_ak_ctrl_thread, + "ak_thread", + ak_task, + NULL, + OS_THREAD_PRIO_APP, + AK_CTRL_THREAD_STACK_SIZE) != OS_OK) { + CMD_ERR("create ak thread failed\n"); + return -1; + } + return 0; +} + +enum cmd_status cmd_airkiss_exec(char *cmd) +{ + int ret = 0; + + if (g_wlan_netif == NULL) { + return CMD_STATUS_FAIL; + } + + if (cmd_strcmp(cmd, "start") == 0) { + if (OS_ThreadIsValid(&g_ak_ctrl_thread)) { + CMD_ERR("Airkiss is already start\n"); + ret = -1; + } else { + ret = ak_create(); + } + } else if (cmd_strcmp(cmd, "set_key") == 0) { + ret = wlan_airkiss_set_key(key, WLAN_AIRKISS_KEY_LEN); + CMD_DBG("Airkiss set key : %s\n", key); + } else if (cmd_strcmp(cmd, "stop") == 0) { + ret = wlan_airkiss_stop(); + } else { + CMD_ERR("invalid argument '%s'\n", cmd); + return CMD_STATUS_INVALID_ARG; + } + + return (ret == 0 ? CMD_STATUS_OK : CMD_STATUS_FAIL); +} diff --git a/platform/mcu/xr871/project/common/cmd/cmd_airkiss.h b/platform/mcu/xr871/project/common/cmd/cmd_airkiss.h new file mode 100644 index 0000000000..a05d49daec --- /dev/null +++ b/platform/mcu/xr871/project/common/cmd/cmd_airkiss.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _CMD_AIRKISS_H_ +#define _CMD_AIRKISS_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +enum cmd_status cmd_airkiss_exec(char *cmd); + +#ifdef __cplusplus +} +#endif + +#endif /* _CMD_AIRKISS_H_ */ diff --git a/platform/mcu/xr871/project/common/cmd/cmd_arp.c b/platform/mcu/xr871/project/common/cmd/cmd_arp.c new file mode 100644 index 0000000000..f4e2afe844 --- /dev/null +++ b/platform/mcu/xr871/project/common/cmd/cmd_arp.c @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "cmd_util.h" +#include "common/framework/net_ctrl.h" +#include "lwip/tcpip.h" +#include "netif/etharp.h" + +int arp_clean_table(struct netif *netif) +{ + tcpip_callback((tcpip_callback_fn)etharp_cleanup_netif, netif); + return 0; +} + +int arp_state(struct netif *netif) +{ +/* void etharp_printtable_netif(struct netif *netif); + tcpip_callback((tcpip_callback_fn)etharp_printtable_netif, netif); +*/ + return 0; +} + +int arp_del(struct netif *netif) +{ + return 0; +} + +int arp_set(struct netif *netif) +{ + return 0; +} + +enum cmd_status cmd_arp_exec(char *cmd) +{ + int ret = 0; + int argc; + char *argv[3]; + + if (g_wlan_netif == NULL) { + return CMD_STATUS_FAIL; + } + + argc = cmd_parse_argv(cmd, argv, cmd_nitems(argv)); + if (argc < 1) { + CMD_ERR("invalid arp cmd, argc %d\n", argc); + return CMD_STATUS_INVALID_ARG; + } + + if (cmd_strcmp(argv[0], "clean") == 0) { + ret = arp_clean_table(g_wlan_netif); + } else if (cmd_strcmp(argv[0], "state") == 0) { + ret = arp_state(g_wlan_netif); + } else if (cmd_strcmp(argv[0], "del") == 0) { + ret = arp_del(g_wlan_netif); + } else if (cmd_strcmp(argv[0], "set") == 0) { + ret = arp_set(g_wlan_netif); + } else { + CMD_ERR("invalid arp cmd <%s>\n", cmd); + return CMD_STATUS_INVALID_ARG; + } + + return (ret == 0 ? CMD_STATUS_OK : CMD_STATUS_FAIL); +} + + diff --git a/platform/mcu/xr871/project/common/cmd/cmd_arp.h b/platform/mcu/xr871/project/common/cmd/cmd_arp.h new file mode 100644 index 0000000000..bcf7b8e5a5 --- /dev/null +++ b/platform/mcu/xr871/project/common/cmd/cmd_arp.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _CMD_ARP_H_ +#define _CMD_ARP_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +enum cmd_status cmd_arp_exec(char *cmd); + +#ifdef __cplusplus +} +#endif + +#endif /* _CMD_ARP_H_ */ diff --git a/platform/mcu/xr871/project/common/cmd/cmd_audio.c b/platform/mcu/xr871/project/common/cmd/cmd_audio.c new file mode 100644 index 0000000000..4dde306cc6 --- /dev/null +++ b/platform/mcu/xr871/project/common/cmd/cmd_audio.c @@ -0,0 +1,369 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "cmd_util.h" +#include "cmd_audio.h" +#include "audio/pcm/audio_pcm.h" +#include "audio/manager/audio_manager.h" +#include "driver/chip/hal_codec.h" +#include "fs/fatfs/ff.h" + +typedef struct { + int samplerate; + int channels; + struct pcm_config *pcm_config; +} audio_format_param; + +struct pcm_config stereo_44100_config = { + .channels = 2, + .rate = 44100, + .period_size = 2048, + .period_count = 2, + .format = PCM_FORMAT_S16_LE, +}; + +struct pcm_config mono_44100_config = { + .channels = 1, + .rate = 44100, + .period_size = 2048, + .period_count = 2, + .format = PCM_FORMAT_S16_LE, +}; + +struct pcm_config mono_8000_config = { + .channels = 1, + .rate = 8000, + .period_size = 2048, + .period_count = 2, + .format = PCM_FORMAT_S16_LE, +}; + +struct pcm_config stereo_8000_config = { + .channels = 2, + .rate = 8000, + .period_size = 2048, + .period_count = 2, + .format = PCM_FORMAT_S16_LE, +}; + +static audio_format_param audio_param[4] = { + {8000, 1, &mono_8000_config}, + {8000, 2, &stereo_8000_config}, + {44100, 1, &mono_44100_config}, + {44100, 2, &stereo_44100_config}, +}; + +static char file_path[50] = {0}; +#define TEST_DELAY_TIME 0X1FF + +#define SOUND_PLAYCARD AUDIO_CARD0 +#define SOUND_CAPCARD AUDIO_CARD0 + +#define SEARCH_FOREACH_CONFIG(a, b, c, i) for (i = 0; i < 4; i++) { \ + if (audio_param[i].samplerate == a && audio_param[i].channels == b) { \ + c = audio_param[i].pcm_config; \ + break; \ + } \ + } + + +#define CREATE_CAP_FILE(FILE_PATH, RES, FS, FILE) FRESULT RES; \ + FATFS fs; \ + FIL file; \ + memset(&fs, 0, sizeof(fs)); \ + fs.drv = 1; \ + if ((RES = f_mount(&fs, "0:/", 1)) != FR_OK) \ + CMD_ERR("failed to mount\n"); \ + else if ((RES = f_open(&file, FILE_PATH, FA_OPEN_ALWAYS|FA_READ|FA_WRITE)) != FR_OK) \ + CMD_ERR("[music file]failed to open,%s\n",file_path) + + +#define AUDIO_THREAD_STACK_SIZE (2 * 1024) +static OS_Thread_t g_audio_stream_thread; +static OS_Thread_t g_audio_control_thread; + +#define AUDIO_DELETE_THREAD(THREAD) OS_ThreadDelete(&THREAD) + +#define AUDIO_CREAT_THREAD(THREAD, TASK, ARG) \ + if (OS_ThreadIsValid(&THREAD)) { \ + CMD_ERR("audio task is running\n"); \ + return CMD_STATUS_FAIL; \ + } \ + if (OS_ThreadCreate(&THREAD, \ + "", \ + TASK, \ + (void *)ARG, \ + OS_THREAD_PRIO_APP, \ + AUDIO_THREAD_STACK_SIZE) != OS_OK) { \ + CMD_ERR("audio task create failed\n"); \ + return CMD_STATUS_FAIL; \ + } + +void cap_exec(void *cmd) +{ + int argc, i = 0, ret = 0; + unsigned int writenum = 0; + char *argv[5]; + struct pcm_config *config = NULL; + + argc = cmd_parse_argv(cmd, argv, 5); + if (argc < 3) { + CMD_ERR("invalid audio capture cmd, argc %d\n", argc); + goto exit_thread; + } + int samplerate = cmd_atoi(argv[0]); + int channels = cmd_atoi(argv[1]); + cmd_memset(file_path, 0, 50); + cmd_strlcpy(file_path, argv[2], 50); + + CMD_DBG("CMD:drv audio cap (samplerate)%d (channel)%d (file)%s\n", samplerate, channels,file_path); + + CREATE_CAP_FILE(file_path, result, fs, file); + if (result != FR_OK) { + CMD_ERR("creat file failed.\n"); + goto exit_thread; + } + + SEARCH_FOREACH_CONFIG(samplerate, channels, config, i); + if (config == NULL) { + CMD_ERR("invalid audio cap param.\n"); + f_close(&file); + goto exit_thread; + } + + unsigned int pcm_buf_size = (config->channels)*2*(config->period_size); + char *pcm_data = malloc(pcm_buf_size); + if (pcm_data == NULL) { + CMD_ERR("malloc buf failed\n"); + f_close(&file); + goto exit_thread; + } + memset(pcm_data, 0, pcm_buf_size); + + if (snd_pcm_open(config, SOUND_CAPCARD, PCM_IN) != 0) + { + CMD_ERR("sound card open err\n"); + goto exit; + } + unsigned int delay_time = TEST_DELAY_TIME; + + CMD_DBG("Capture run.\n"); + while (--delay_time != 0) { + + ret = snd_pcm_read(config, SOUND_CAPCARD, pcm_data, pcm_buf_size); + if (ret != pcm_buf_size) { + CMD_ERR("read data failed(%d),line:%d\n", ret, __LINE__); + break; + } + if ((result = f_write(&file, pcm_data, pcm_buf_size, &writenum)) != FR_OK) { + CMD_ERR("write failed(%d).\n",result); + break; + } else { + if (writenum != pcm_buf_size) { + CMD_ERR("write failed %d,%d\n", writenum, __LINE__); + break; + } + } + } + snd_pcm_close(SOUND_CAPCARD, PCM_IN); + +exit: + free(pcm_data); + f_close(&file); + //f_mount(NULL, "", 1); + CMD_DBG("Capture end.\n"); +exit_thread: + AUDIO_DELETE_THREAD(g_audio_stream_thread); +} + +void play_exec(void *cmd) +{ + int argc, i = 0; + unsigned int readnum = 0; + char *argv[5]; + struct pcm_config *config = NULL; + + argc = cmd_parse_argv(cmd, argv, 5); + if (argc < 3) { + CMD_ERR("invalid audio capture cmd, argc %d\n", argc); + goto exit_thread; + } + int samplerate = cmd_atoi(argv[0]); + int channels = cmd_atoi(argv[1]); + cmd_memset(file_path, 0, 50); + cmd_strlcpy(file_path, argv[2], 50); + + CMD_DBG("CMD:drv audio play (samplerate)%d (channel)%d (file)%s\n", samplerate, channels,file_path); + + CREATE_CAP_FILE(file_path, result, fs, file); + if (result != FR_OK) { + CMD_ERR("creat file failed.\n"); + goto exit_thread; + } + + SEARCH_FOREACH_CONFIG(samplerate, channels, config, i); + if (config == NULL) { + CMD_ERR("invalid audio cap param.\n"); + f_close(&file); + goto exit_thread; + } + + unsigned int pcm_buf_size = (config->channels)*2*(config->period_size); + char *pcm_data = malloc(pcm_buf_size); + if (pcm_data == NULL) { + CMD_ERR("malloc buf failed\n"); + f_close(&file); + goto exit_thread;; + } + if (snd_pcm_open(config, SOUND_PLAYCARD, PCM_OUT) != 0) + { + CMD_ERR("sound card open err\n"); + goto exit; + } + CMD_DBG("Play on.\n"); + while (1) { + if ((result = f_read(&file, pcm_data, pcm_buf_size, &readnum)) != FR_OK) { + CMD_ERR("read failed(%d).\n",result); + break; + } else { + if (readnum != pcm_buf_size) { + CMD_DBG("file end: file size = %d\n", readnum); + break; + } + } + snd_pcm_write(config, SOUND_PLAYCARD, pcm_data, pcm_buf_size); + } + snd_pcm_close(SOUND_PLAYCARD, PCM_OUT); + +exit: + f_close(&file); + //f_mount(NULL, "", 1); + free(pcm_data); + CMD_DBG("Play end.\n"); +exit_thread: + AUDIO_DELETE_THREAD(g_audio_stream_thread); + +} + +void vol_exec(void *cmd) +{ + int argc; + char *argv[2]; + + argc = cmd_parse_argv(cmd, argv, 2); + if (argc < 1) { + CMD_ERR("invalid audio set vol cmd, argc %d\n", argc); + goto exit; + } + int vol = cmd_atoi(argv[0]); + CMD_DBG("CMD:drv audio vol (level)%d\n", vol); + + if ( vol > aud_mgr_maxvol()) { + CMD_ERR("invalid audio vol.Range(0-31)\n"); + goto exit; + } + + aud_mgr_handler(AUDIO_DEVICE_MANAGER_VOLUME, vol); +exit: + AUDIO_DELETE_THREAD(g_audio_control_thread); +} + +void path_exec(void *cmd) +{ + int argc; + char *argv[2]; + + argc = cmd_parse_argv(cmd, argv, 2); + if (argc < 1) { + CMD_ERR("invalid audio set path cmd, argc %d\n", argc); + goto exit; + } + int path = cmd_atoi(argv[0]); + CMD_DBG("CMD:drv audio out-path %d\n", path); + + if (path > AUDIO_DEVICE_SPEAKER) { + CMD_ERR("invalid audio out-path.Range(1-2)\n"); + goto exit; + } + + aud_mgr_handler(AUDIO_DEVICE_MANAGER_PATH, path); +exit: + AUDIO_DELETE_THREAD(g_audio_control_thread); +} + +static enum cmd_status audio_play_task(char *arg) +{ + char *cmd = (char *)arg; + + AUDIO_CREAT_THREAD(g_audio_stream_thread, play_exec, cmd); + return CMD_STATUS_OK; +} + +static enum cmd_status audio_cap_task(char *arg) +{ + char *cmd = (char *)arg; + + AUDIO_CREAT_THREAD(g_audio_stream_thread, cap_exec, cmd); + return CMD_STATUS_OK; +} + +static enum cmd_status audio_vol_task(char *arg) +{ + char *cmd = (char *)arg; + + AUDIO_CREAT_THREAD(g_audio_control_thread, vol_exec, cmd); + return CMD_STATUS_OK; +} + +static enum cmd_status audio_path_task(char *arg) +{ + char *cmd = (char *)arg; + + AUDIO_CREAT_THREAD(g_audio_control_thread, path_exec, cmd); + return CMD_STATUS_OK; +} + +static struct cmd_data g_audio_cmds[] = { + { "cap", audio_cap_task }, + { "play", audio_play_task }, + { "vol", audio_vol_task }, + { "path", audio_path_task }, +}; + +void audio_task_run(void *arg) +{ + char *cmd = (char *)arg; + cmd_exec(cmd, g_audio_cmds, cmd_nitems(g_audio_cmds)); +} + +enum cmd_status cmd_audio_exec(char *cmd) +{ + audio_task_run(cmd); + return CMD_STATUS_OK; +} diff --git a/platform/mcu/xr871/project/common/cmd/cmd_audio.h b/platform/mcu/xr871/project/common/cmd/cmd_audio.h new file mode 100644 index 0000000000..a4d7a0d93d --- /dev/null +++ b/platform/mcu/xr871/project/common/cmd/cmd_audio.h @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _CMD_AUDIO_H_ +#define _CMD_AUDIO_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * drv audio + * stream-cmd : play cap + * samplerate: 8000 44100 + * channel: 1 2 + * file: + * example: + * drv audio play 44100 2 0:/8.pcm + * drv audio cap 44100 2 0:/8.pcm + * + * drv audio + * + * example: + * control-cmd: vol out-path + * arg:vol (1- 30) + * path 0 1 + * drv audio vol 25 + * drv audio out-path 0 + */ + +enum cmd_status cmd_audio_exec(char *cmd); + +#ifdef __cplusplus +} +#endif + +#endif /* _CMD_AUDIO_H_ */ + diff --git a/platform/mcu/xr871/project/common/cmd/cmd_broadcast.c b/platform/mcu/xr871/project/common/cmd/cmd_broadcast.c new file mode 100644 index 0000000000..80f60dac13 --- /dev/null +++ b/platform/mcu/xr871/project/common/cmd/cmd_broadcast.c @@ -0,0 +1,264 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "cmd_util.h" +#include "net/lwip/lwip/sockets.h" + +#define BROADCAST_THREAD_STACK_SIZE (2 * 1024) +#define RECEIVE_BUF_LEN 1500 +OS_Thread_t g_broadcast_thread; + +typedef enum { + THREAD_STOP, + THREAD_RUN, +} run_flag; + +typedef enum { + BROADCAST_RECV, + BROADCAST_SEND, +} broadcast_mode; + +typedef struct { + int port; + ip_addr_t ip_addr; + broadcast_mode mode; + u16_t data_range_min; + u16_t data_range_max; + int time; + int count; + char* data_p; + run_flag run_flag; + int num; +} broadcast_format_param; + +static broadcast_format_param broadcast_param; + +static int get_range(char *buf, u16_t *min, u16_t *max) +{ + char *start, *end; + start = end = buf; + u16_t min_t = 0, max_t = 0; + + if (buf == NULL || min == NULL || max == NULL) + return -1; + if (*start == ',') { + min_t = 0; + max_t = (u16_t)atoi(++start); + if (max_t < min_t) + return -1; + *min = min_t; + *max = max_t; + return 0; + } else { + while (*end != ',' && *end != '\0') + end++; + if (*end == '\0') + return -1; + *end = '\0'; + min_t = (u16_t)atoi(start); + start = ++end; + if (*start == '\0') + max_t = 65535; + else + max_t = (u16_t)atoi(start); + if (max_t < min_t) + return -1; + *min = min_t; + *max = max_t; + return 0; + } +} + +int recv_broadcast_packet(broadcast_format_param *param) +{ + int fd; + int optval = 1; + int sock_timeout_val = 1000;//1s + int data_len=0; + char *message_buf; + struct sockaddr_in addr; + socklen_t addr_len = sizeof(addr); + + message_buf = malloc(RECEIVE_BUF_LEN); + if (message_buf == NULL) { + CMD_ERR("message_buf malloc faild\n"); + return -1; + } +/* + CMD_DBG("port=%d ip=%s mode=%d data_range_min=%d data_range_max=%d\n", + param->port, ipaddr_ntoa((ip_addr_t*)(&(param->ip_addr))), + param->mode, param->data_range_min, param->data_range_max); +*/ + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = htonl(INADDR_ANY); + addr.sin_port = htons(param->port); + + fd = socket(AF_INET, SOCK_DGRAM, 0); + if (fd == -1) { + CMD_ERR("socket create failed\n"); + return -1; + } + + setsockopt(fd, SOL_SOCKET, SO_BROADCAST, (void*)&optval, sizeof(optval)); + setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, + (void*)&sock_timeout_val, sizeof(sock_timeout_val)); + if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) == -1) { + CMD_ERR("socket bind failed\n"); + closesocket(fd); + return -1; + } + param->run_flag = THREAD_RUN; + while (param->run_flag) { + data_len = recvfrom(fd, message_buf, RECEIVE_BUF_LEN, 0, + (struct sockaddr *)&addr, &addr_len); + if ((data_len > 0) && (data_len >= param->data_range_min) && + (data_len <= param->data_range_max)) { + if (param->ip_addr.addr == INADDR_BROADCAST) { + param->num++; + //CMD_DBG("Receive message:%.*s\n", data_len, message_buf); + } else if (param->ip_addr.addr == addr.sin_addr.s_addr) { + param->num++; + //CMD_DBG("Receive message:%.*s\n", data_len, message_buf); + } + } + } + CMD_DBG("broadcast packets num received: %d\n", param->num); + closesocket(fd); + free(message_buf); + return 0; +} + +void broadcast_run(void *param) +{ + ((broadcast_format_param*)param)->num = 0; + if (((broadcast_format_param*)param)->mode == BROADCAST_RECV) + recv_broadcast_packet((broadcast_format_param*)param); + + OS_ThreadDelete(&g_broadcast_thread); +} + +int broadcast_start(broadcast_format_param *param) +{ + if (OS_ThreadIsValid(&g_broadcast_thread)) { + CMD_ERR("broadcast task is running\n"); + return -1; + } + if (OS_ThreadCreate(&g_broadcast_thread, + "", + broadcast_run, + (void *)param, + OS_THREAD_PRIO_APP, + BROADCAST_THREAD_STACK_SIZE) != OS_OK) { + CMD_ERR("broadcast task create failed\n"); + return -1; + } + return 0; +} + +enum cmd_status broadcast_state_exec(char *cmd) +{ + CMD_DBG("broadcast packets num received: %d\n", broadcast_param.num); + return CMD_STATUS_OK; +} + +enum cmd_status broadcast_reset_exec(char *cmd) +{ + broadcast_param.num = 0; + return CMD_STATUS_OK; +} + +enum cmd_status broadcast_stop_exec(char *cmd) +{ + broadcast_param.run_flag = THREAD_STOP; + return CMD_STATUS_OK; +} + +enum cmd_status broadcast_recv_exec(char *cmd) +{ + int argc; + int port; + char ip_buf[16]; + char range_buf[12]; + int ret = 0; + + argc = cmd_sscanf(cmd, "p=%d i=%16s r=%12s", &port, ip_buf, range_buf); + if (argc != 3) { + CMD_ERR("invalid broadcast cmd, argc %d\n", argc); + return CMD_STATUS_INVALID_ARG; + } + + broadcast_param.mode = BROADCAST_RECV; + if (port > 65535 || port < 0) { + ret = -1; + goto exit; + } + broadcast_param.port = port; + if (inet_aton(ip_buf, &(broadcast_param.ip_addr)) == 0) { + ret = -1; + goto exit; + } + if (get_range(range_buf, &(broadcast_param.data_range_min), + &(broadcast_param.data_range_max)) != 0) { + ret = -1; + goto exit; + } + if (broadcast_start(&broadcast_param) != 0) { + ret = -1; + goto exit; + } + +exit: + if (ret != 0) { + CMD_ERR("invalid broadcast cmd '%s'\n", cmd); + return CMD_STATUS_INVALID_ARG; + } + return CMD_STATUS_OK; +} + +enum cmd_status broadcast_send_exec(char *cmd) +{ + return CMD_STATUS_OK; +} + +/* + * dhcp commands + */ +static struct cmd_data g_broadcast_cmds[] = { + { "recv", broadcast_recv_exec }, + { "send", broadcast_send_exec }, + { "state", broadcast_state_exec }, + { "reset", broadcast_reset_exec }, + { "stop", broadcast_stop_exec }, +}; + +enum cmd_status cmd_broadcast_exec(char *cmd) +{ + return cmd_exec(cmd, g_broadcast_cmds, cmd_nitems(g_broadcast_cmds)); +} diff --git a/platform/mcu/xr871/project/common/cmd/cmd_broadcast.h b/platform/mcu/xr871/project/common/cmd/cmd_broadcast.h new file mode 100644 index 0000000000..4d5285567e --- /dev/null +++ b/platform/mcu/xr871/project/common/cmd/cmd_broadcast.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _CMD_BROADCAST_H_ +#define _CMD_BROADCAST_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +enum cmd_status cmd_broadcast_exec(char *cmd); + +#ifdef __cplusplus +} +#endif + +#endif /* _CMD_BROADCAST_H_ */ + diff --git a/platform/mcu/xr871/project/common/cmd/cmd_ce.c b/platform/mcu/xr871/project/common/cmd/cmd_ce.c new file mode 100644 index 0000000000..5878320659 --- /dev/null +++ b/platform/mcu/xr871/project/common/cmd/cmd_ce.c @@ -0,0 +1,808 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "cmd_util.h" +#include "cmd_ce.h" +#include "driver/chip/hal_crypto.h" + +#define CE_TEST_KEYNUM 5 + +#define CE_TEST_KEYMAXSIZE sizeof(CE_AES_Config) + +typedef enum { + NONE, + AES, + DES, + TDES +} crypto_key_type; + +typedef struct { + crypto_key_type type; + void *key_cfg; +} crypto_key; + +static crypto_key key[CE_TEST_KEYNUM]; +static uint32_t srv_cnt; + +/*static CE_CRC_Handler crc; +static CE_MD5_Handler md5; +static CE_SHA1_Handler sha1; +static CE_SHA256_Handler sha256; + +uint32_t crc_cont; +uint32_t md5_cont; +uint32_t sha1_cont; +uint32_t sha256_cont;*/ + +static uint32_t md5_iv[4]; +static uint32_t sha1_iv[5]; +static uint32_t sha256_iv[8]; + +static enum cmd_status cmd_crypto_addkey_exec(char *cmd) +{ + int32_t cnt; + char type_str[8]; + char mode_str[8]; + uint32_t i = 0; + uint32_t keysize; + CE_Crypto_Mode mode; + void *p; + uint8_t buf[32]; + uint32_t keysrc; + + /* get param */ + cnt = cmd_sscanf(cmd, "t=%7s m=%7s k=%u", type_str, mode_str, &keysrc); + + /* check param */ + if (cnt != 3) { + CMD_ERR("invalid param number %d\n", cnt); + return CMD_STATUS_INVALID_ARG; + } + + if (keysrc > 10) { + CMD_ERR("invalid keysrc %d\n", keysrc); + return CMD_STATUS_INVALID_ARG; + } + + /* create key */ + while (key[i].type != NONE) { + i++; + if (i > CE_TEST_KEYNUM) { + CMD_ERR("key full\n"); + return CMD_STATUS_FAIL; + } + } + + key[i].key_cfg = cmd_malloc(CE_TEST_KEYMAXSIZE); // staic may be good. + p = key[i].key_cfg; + if (p == NULL) { + CMD_ERR("no memory\n"); + return CMD_STATUS_FAIL; + } + + /* check param */ + if (cmd_strcmp(mode_str, "ecb") == 0) { + mode = CE_CRYPT_MODE_ECB; + } else if (cmd_strcmp(mode_str, "cbc") == 0) { + mode = CE_CRYPT_MODE_CBC; + } else { + cmd_free(key[i].key_cfg); + CMD_ERR("invalid mode %s\n", mode_str); + return CMD_STATUS_INVALID_ARG; + } + + if (cmd_strcmp(type_str, "aes128") == 0) { + keysize = 16; + key[i].type = AES; + ((CE_AES_Config *)p)->mode = mode; + ((CE_AES_Config *)p)->src = (CE_Crypto_KeySrc)keysrc; + ((CE_AES_Config *)p)->keysize = CE_CTL_AES_KEYSIZE_128BITS; + } else if (cmd_strcmp(type_str, "aes192") == 0) { + keysize = 24; + key[i].type = AES; + ((CE_AES_Config *)p)->mode = mode; + ((CE_AES_Config *)p)->src = (CE_Crypto_KeySrc)keysrc; + ((CE_AES_Config *)p)->keysize = CE_CTL_AES_KEYSIZE_192BITS; + } else if (cmd_strcmp(type_str, "aes256") == 0) { + keysize = 32; + key[i].type = AES; + ((CE_AES_Config *)p)->mode = mode; + ((CE_AES_Config *)p)->src = (CE_Crypto_KeySrc)keysrc; + ((CE_AES_Config *)p)->keysize = CE_CTL_AES_KEYSIZE_256BITS; + } else if (cmd_strcmp(type_str, "des") == 0) { + keysize = 8; + key[i].type = DES; + ((CE_DES_Config *)p)->mode = mode; + ((CE_DES_Config *)p)->src = (CE_Crypto_KeySrc)keysrc; + } else if (cmd_strcmp(type_str, "3des") == 0) { + keysize = 24; + key[i].type = TDES; + ((CE_3DES_Config *)p)->mode = mode; + ((CE_3DES_Config *)p)->src = (CE_Crypto_KeySrc)keysrc; + } else { + cmd_free(key[i].key_cfg); + CMD_ERR("invalid type %s\n", type_str); + return CMD_STATUS_INVALID_ARG; + } + + cmd_write_respond(CMD_STATUS_OK, "%d", i); + + if (keysrc == 0) { + + cmd_raw_mode_enable(); + cnt = cmd_raw_mode_read(buf, keysize, 30000); + cmd_raw_mode_disable(); + + buf[cnt] = 0; + + switch (key[i].type) { + case AES: + cmd_memcpy(((CE_AES_Config *)p)->key, buf, keysize); + break; + case DES: + cmd_memcpy(((CE_DES_Config *)p)->key, buf, keysize); + break; + case TDES: + cmd_memcpy(((CE_3DES_Config *)p)->key, buf, keysize); + break; + default: + break; + } + } + + if (srv_cnt++ == 0) + HAL_CE_Init(); + + return CMD_STATUS_ACKED; +} + +static enum cmd_status cmd_crypto_delkey_exec(char *cmd) +{ + int32_t cnt; + uint32_t i; + + /* get param */ + cnt = cmd_sscanf(cmd, "k=%u", &i); + + /* check param */ + if (cnt != 1) { + CMD_ERR("invalid param number %d\n", cnt); + return CMD_STATUS_INVALID_ARG; + } + + if ((i >= CE_TEST_KEYNUM) || (key[i].type == NONE)) { + CMD_ERR("invalid key %d\n", i); + return CMD_STATUS_INVALID_ARG; + } + + cmd_free(key[i].key_cfg); + key[i].type = NONE; + + if (--srv_cnt == 0) + HAL_CE_Deinit(); + + return CMD_STATUS_OK; +} + +static enum cmd_status cmd_crypto_keyiv_exec(char *cmd) +{ + int32_t cnt; + uint32_t i; + uint32_t size; + void *p; + uint8_t buf[16]; + uint32_t iv_size; + + + /* get param */ + cnt = cmd_sscanf(cmd, "k=%u s=%u", &i, &size); + + /* check param */ + if (cnt != 2) { + CMD_ERR("invalid param number %d\n", cnt); + return CMD_STATUS_INVALID_ARG; + } + + if ((i >= CE_TEST_KEYNUM) || (key[i].type == NONE)) { + CMD_ERR("invalid key %d\n", i); + return CMD_STATUS_INVALID_ARG; + } + + if (key[i].type == AES) { + iv_size = 16; + if (size > 16) { + CMD_ERR("invalid size %d\n", size); + return CMD_STATUS_INVALID_ARG; + } + } else { + iv_size = 8; + if (size > 8) { + CMD_ERR("invalid size %d\n", size); + return CMD_STATUS_INVALID_ARG; + } + } + + p = key[i].key_cfg; + + cmd_write_respond(CMD_STATUS_OK, "%d", iv_size); + + cmd_raw_mode_enable(); + cmd_raw_mode_read(buf, iv_size, 30000); + cmd_raw_mode_disable(); + + switch (key[i].type) { + case AES: + cmd_memset(((CE_AES_Config *)p)->iv, 0, iv_size); + cmd_memcpy(((CE_AES_Config *)p)->iv, buf, size); + break; + case DES: + cmd_memset(((CE_DES_Config *)p)->iv, 0, iv_size); + cmd_memcpy(((CE_DES_Config *)p)->iv, buf, size); + break; + case TDES: + cmd_memset(((CE_3DES_Config *)p)->iv, 0, iv_size); + cmd_memcpy(((CE_3DES_Config *)p)->iv, buf, size); + break; + default: + break; + } + + return CMD_STATUS_ACKED; +} + +static enum cmd_status cmd_crypto_enc_exec(char *cmd) +{ + int32_t cnt; + uint32_t keyi; + uint32_t size; + uint32_t enc_size; + uint8_t *plain; + uint8_t *cipher; + + /* get param */ + cnt = cmd_sscanf(cmd, "k=%u s=%u", &keyi, &size); + + /* check param */ + if (cnt != 2) { + CMD_ERR("invalid param number %d\n", cnt); + return CMD_STATUS_INVALID_ARG; + } + + if ((keyi >= CE_TEST_KEYNUM) || (key[keyi].type == NONE)) { + CMD_ERR("invalid key %d\n", keyi); + return CMD_STATUS_INVALID_ARG; + } + + plain = cmd_malloc(size); + if (plain == NULL) { + CMD_ERR("no memory\n"); + return CMD_STATUS_FAIL; + } + + cipher = cmd_malloc(size + 16); + if (cipher == NULL) { + CMD_ERR("no memory\n"); + cmd_free(plain); + return CMD_STATUS_FAIL; + } + + if (key[keyi].type == AES) + enc_size = (size + 15) & (~0xF); + else + enc_size = (size + 7) & (~0x7); + + cmd_write_respond(CMD_STATUS_OK, "%d", enc_size); + cmd_raw_mode_enable(); + cmd_raw_mode_read(plain, size, 30000); + + switch (key[keyi].type) { + case AES: + HAL_AES_Encrypt((CE_AES_Config *)(key[keyi].key_cfg), plain, cipher, size); + break; + case DES: + HAL_DES_Encrypt((CE_DES_Config *)(key[keyi].key_cfg), plain, cipher, size); + break; + case TDES: + HAL_3DES_Encrypt((CE_3DES_Config *)(key[keyi].key_cfg), plain, cipher, size); + break; + default: + break; + } + + cmd_raw_mode_write(cipher, enc_size); + cmd_raw_mode_disable(); + + cmd_free(plain); + cmd_free(cipher); + return CMD_STATUS_ACKED; +} + +static enum cmd_status cmd_crypto_dec_exec(char *cmd) +{ + int32_t cnt; + uint32_t keyi; + uint32_t size; + uint8_t *plain; + uint8_t *cipher; + + /* get param */ + cnt = cmd_sscanf(cmd, "k=%u s=%u", &keyi, &size); + + /* check param */ + if (cnt != 2) { + CMD_ERR("invalid param number %d\n", cnt); + return CMD_STATUS_INVALID_ARG; + } + + if ((keyi >= CE_TEST_KEYNUM) || (key[keyi].type == NONE)) { + CMD_ERR("invalid key %d\n", keyi); + return CMD_STATUS_INVALID_ARG; + } + + if (key[keyi].type == AES) { + if (size & 0xF) { + CMD_ERR("invalid size %d\n", size); + return CMD_STATUS_INVALID_ARG; + } + } else { + if (size & 0x7) { + CMD_ERR("invalid size %d\n", size); + return CMD_STATUS_INVALID_ARG; + } + } + + plain = cmd_malloc(size); + if (plain == NULL) { + CMD_ERR("no memory\n"); + return CMD_STATUS_FAIL; + } + + cipher = cmd_malloc(size + 16); + if (cipher == NULL) { + CMD_ERR("no memory\n"); + cmd_free(plain); + return CMD_STATUS_FAIL; + } + + cmd_write_respond(CMD_STATUS_OK, "%d", size); + cmd_raw_mode_enable(); + cmd_raw_mode_read(cipher, size, 30000); + + switch (key[keyi].type) { + case AES: + HAL_AES_Decrypt((CE_AES_Config *)(key[keyi].key_cfg), cipher, plain, size); + break; + case DES: + HAL_DES_Decrypt((CE_DES_Config *)(key[keyi].key_cfg), cipher, plain, size); + break; + case TDES: + HAL_3DES_Decrypt((CE_3DES_Config *)(key[keyi].key_cfg), cipher, plain, size); + break; + default: + break; + } + + cmd_raw_mode_write(plain, size); + cmd_raw_mode_disable(); + + cmd_free(plain); + cmd_free(cipher); + return CMD_STATUS_ACKED; +} + +static enum cmd_status cmd_crypto_crc_exec(char *cmd) +{ + char type_str[16]; + CE_CRC_Types type; + uint32_t size; + + uint8_t *buf; + CE_CRC_Handler crc; + uint32_t half; + uint32_t res; + + /* get param */ + cmd_sscanf(cmd, "t=%16s s=%u", type_str, &size); + + /* check param */ + if (cmd_strcmp(type_str, "ibm") == 0) { + type = CE_CRC16_IBM; + } else if (cmd_strcmp(type_str, "maxim") == 0) { + type = CE_CRC16_MAXIM; + } else if (cmd_strcmp(type_str, "usb") == 0) { + type = CE_CRC16_USB; + } else if (cmd_strcmp(type_str, "modbus") == 0) { + type = CE_CRC16_MODBUS; + } else if (cmd_strcmp(type_str, "ccitt_1") == 0) { + type = CE_CRC16_CCITT_1; + } else if (cmd_strcmp(type_str, "ccitt") == 0) { + type = CE_CRC16_CCITT; + } else if (cmd_strcmp(type_str, "x25") == 0) { + type = CE_CRC16_X25; + } else if (cmd_strcmp(type_str, "xmodem") == 0) { + type = CE_CRC16_XMODEM; + } else if (cmd_strcmp(type_str, "dnp") == 0) { + type = CE_CRC16_DNP; + } else if (cmd_strcmp(type_str, "crc32") == 0) { + type = CE_CRC32; + } else if (cmd_strcmp(type_str, "mpeg2") == 0) { + type = CE_CRC32_MPEG2; + } else { + CMD_ERR("invalid type %s\n", type_str); + return CMD_STATUS_INVALID_ARG; + } + + half = size / 2; + buf = cmd_malloc(size); + if (buf == NULL) { + CMD_ERR("no memory\n"); + return CMD_STATUS_FAIL; + } + + cmd_write_respond(CMD_STATUS_OK, "%d", sizeof(res)); + + cmd_raw_mode_enable(); + cmd_raw_mode_read(buf, size, 30000); + + if (srv_cnt++ == 0) + HAL_CE_Init(); + + if (HAL_CRC_Init(&crc, type, size) != HAL_OK) { + CMD_ERR("crc init failed\n"); + return CMD_STATUS_FAIL; + } + if ((half) && (HAL_CRC_Append(&crc, buf, half) != HAL_OK)) { + CMD_ERR("crc append failed\n"); + return CMD_STATUS_FAIL; + } + if (HAL_CRC_Append(&crc, buf + half, size - half) != HAL_OK) { + CMD_ERR("crc append failed\n"); + return CMD_STATUS_FAIL; + } + if (HAL_CRC_Finish(&crc, &res) != HAL_OK) { + CMD_ERR("crc deinit failed\n"); + return CMD_STATUS_FAIL; + } + + if (--srv_cnt == 0) + HAL_CE_Deinit(); + + cmd_raw_mode_write((uint8_t *)&res, sizeof(res)); + cmd_raw_mode_disable(); + + cmd_free(buf); + return CMD_STATUS_ACKED; +} + + +static enum cmd_status cmd_crypto_md5_exec(char *cmd) +{ + char ivsrc_str[8]; + CE_Hash_IVsrc ivsrc; + uint32_t size; + + uint8_t *buf; + CE_MD5_Handler md5; + uint32_t half; + uint32_t res[4]; + + /* get param */ + cmd_sscanf(cmd, "i=%7s s=%u", ivsrc_str, &size); + + /* check param */ + if (cmd_strcmp(ivsrc_str, "fips180") == 0) { + ivsrc = CE_CTL_IVMODE_SHA_MD5_FIPS180; + } else if (cmd_strcmp(ivsrc_str, "input") == 0) { + ivsrc = CE_CTL_IVMODE_SHA_MD5_INPUT; + } else { + CMD_ERR("invalid ivsrc %s\n", ivsrc_str); + return CMD_STATUS_INVALID_ARG; + } + + half = size / 2; + buf = cmd_malloc(size); + if (buf == NULL) { + CMD_ERR("no memory\n"); + return CMD_STATUS_FAIL; + } + + cmd_write_respond(CMD_STATUS_OK, "%d", sizeof(res)); + + cmd_raw_mode_enable(); + cmd_raw_mode_read(buf, size, 30000); + + if (srv_cnt++ == 0) + HAL_CE_Init(); + + if (HAL_MD5_Init(&md5, ivsrc, md5_iv) != HAL_OK) { + CMD_ERR("md5 init failed\n"); + return CMD_STATUS_FAIL; + } + if ((half) && (HAL_MD5_Append(&md5, buf, half) != HAL_OK)) { + CMD_ERR("md5 append failed\n"); + return CMD_STATUS_FAIL; + } + if (HAL_MD5_Append(&md5, buf + half, size - half) != HAL_OK) { + CMD_ERR("md5 append failed\n"); + return CMD_STATUS_FAIL; + } + if (HAL_MD5_Finish(&md5, res) != HAL_OK) { + CMD_ERR("md5 deinit failed\n"); + return CMD_STATUS_FAIL; + } + + if (--srv_cnt == 0) + HAL_CE_Deinit(); + + cmd_raw_mode_write((uint8_t *)&res, sizeof(res)); + cmd_raw_mode_disable(); + + cmd_free(buf); + return CMD_STATUS_ACKED; + +} + + +static enum cmd_status cmd_crypto_sha1_exec(char *cmd) +{ + char ivsrc_str[8]; + CE_Hash_IVsrc ivsrc; + uint32_t size; + + uint8_t *buf; + CE_SHA1_Handler sha1; + uint32_t half; + uint32_t res[5]; + + /* get param */ + cmd_sscanf(cmd, "i=%7s s=%u", ivsrc_str, &size); + + /* check param */ + if (cmd_strcmp(ivsrc_str, "fips180") == 0) { + ivsrc = CE_CTL_IVMODE_SHA_MD5_FIPS180; + } else if (cmd_strcmp(ivsrc_str, "input") == 0) { + ivsrc = CE_CTL_IVMODE_SHA_MD5_INPUT; + } else { + CMD_ERR("invalid ivsrc %s\n", ivsrc_str); + return CMD_STATUS_INVALID_ARG; + } + + half = size / 2; + buf = cmd_malloc(size); + if (buf == NULL) { + CMD_ERR("no memory\n"); + return CMD_STATUS_FAIL; + } + + cmd_write_respond(CMD_STATUS_OK, "%d", sizeof(res)); + + cmd_raw_mode_enable(); + cmd_raw_mode_read(buf, size, 30000); + + if (srv_cnt++ == 0) + HAL_CE_Init(); + + if (HAL_SHA1_Init(&sha1, ivsrc, sha1_iv) != HAL_OK) { + CMD_ERR("sha1 init failed\n"); + return CMD_STATUS_FAIL; + } + if ((half) && (HAL_SHA1_Append(&sha1, buf, half) != HAL_OK)) { + CMD_ERR("sha1 append failed\n"); + return CMD_STATUS_FAIL; + } + if (HAL_SHA1_Append(&sha1, buf + half, size - half) != HAL_OK) { + CMD_ERR("sha1 append failed\n"); + return CMD_STATUS_FAIL; + } + if (HAL_SHA1_Finish(&sha1, res) != HAL_OK) { + CMD_ERR("sha1 deinit failed\n"); + return CMD_STATUS_FAIL; + } + + if (--srv_cnt == 0) + HAL_CE_Deinit(); + + cmd_raw_mode_write((uint8_t *)&res, sizeof(res)); + cmd_raw_mode_disable(); + + cmd_free(buf); + return CMD_STATUS_ACKED; + +} + +static enum cmd_status cmd_crypto_sha256_exec(char *cmd) +{ + char ivsrc_str[8]; + CE_Hash_IVsrc ivsrc; + uint32_t size; + + uint8_t *buf; + CE_SHA256_Handler sha256; + uint32_t half; + uint32_t res[8]; + + /* get param */ + cmd_sscanf(cmd, "i=%7s s=%u", ivsrc_str, &size); + + /* check param */ + if (cmd_strcmp(ivsrc_str, "fips180") == 0) { + ivsrc = CE_CTL_IVMODE_SHA_MD5_FIPS180; + } else if (cmd_strcmp(ivsrc_str, "input") == 0) { + ivsrc = CE_CTL_IVMODE_SHA_MD5_INPUT; + } else { + CMD_ERR("invalid ivsrc %s\n", ivsrc_str); + return CMD_STATUS_INVALID_ARG; + } + + half = size / 2; + buf = cmd_malloc(size); + if (buf == NULL) { + CMD_ERR("no memory\n"); + return CMD_STATUS_FAIL; + } + + cmd_write_respond(CMD_STATUS_OK, "%d", sizeof(res)); + + cmd_raw_mode_enable(); + cmd_raw_mode_read(buf, size, 30000); + + if (srv_cnt++ == 0) + HAL_CE_Init(); + + if (HAL_SHA256_Init(&sha256, ivsrc, sha256_iv) != HAL_OK) { + CMD_ERR("sha256 init failed\n"); + return CMD_STATUS_FAIL; + } + if ((half) && (HAL_SHA256_Append(&sha256, buf, half) != HAL_OK)) { + CMD_ERR("sha256 append failed\n"); + return CMD_STATUS_FAIL; + } + if (HAL_SHA256_Append(&sha256, buf + half, size - half) != HAL_OK) { + CMD_ERR("sha256 append failed\n"); + return CMD_STATUS_FAIL; + } + if (HAL_SHA256_Finish(&sha256, res) != HAL_OK) { + CMD_ERR("sha256 deinit failed\n"); + return CMD_STATUS_FAIL; + } + + cmd_raw_mode_write((uint8_t *)&res, sizeof(res)); + cmd_raw_mode_disable(); + + if (--srv_cnt == 0) + HAL_CE_Deinit(); + + cmd_free(buf); + return CMD_STATUS_ACKED; + +} + + +static enum cmd_status cmd_crypto_prng_exec(char *cmd) +{ + uint8_t *res; + uint32_t size; + + /* get param */ + cmd_sscanf(cmd, "s=%u", &size); + + (void)size; //----------------------------the interface of prng should set size + + res = cmd_malloc(size); + if (res == NULL) { + CMD_ERR("no memory\n"); + return CMD_STATUS_FAIL; + } + + if (srv_cnt++ == 0) + HAL_CE_Init(); + + if (HAL_PRNG_Generate(res, size) != HAL_OK) { + CMD_ERR("prng generate failed\n"); + return CMD_STATUS_FAIL; + } + + if (--srv_cnt == 0) + HAL_CE_Deinit(); + + cmd_write_respond(CMD_STATUS_OK, "OK"); + + cmd_raw_mode_enable(); + cmd_msleep(1000); + cmd_raw_mode_write((uint8_t *)res, size); + cmd_raw_mode_disable(); + + cmd_free(res); + + return CMD_STATUS_ACKED; +} + +static enum cmd_status cmd_crypto_digiv_exec(char *cmd) +{ + char type[8]; + uint32_t size; + uint8_t *p; + + /* get param */ + cmd_sscanf(cmd, "t=%7s s=%u", type, &size); + + /* check param */ + if (cmd_strcmp(type, "md5") == 0) { + if (size > 4 * 4) { + CMD_ERR("invalid ivsize %d\n", size); + return CMD_STATUS_INVALID_ARG; + } + p = (uint8_t *)&md5_iv; + cmd_memset(p, 0, 16); + } else if (cmd_strcmp(type, "sha1") == 0) { + if (size > 5 * 4) { + CMD_ERR("invalid ivsize %d\n", size); + return CMD_STATUS_INVALID_ARG; + } + p = (uint8_t *)&sha1_iv; + cmd_memset(p, 0, 20); + } else if (cmd_strcmp(type, "sha256") == 0) { + if (size > 8 * 4) { + CMD_ERR("invalid ivsize %d\n", size); + return CMD_STATUS_INVALID_ARG; + } + p = (uint8_t *)&sha256_iv; + cmd_memset(p, 0, 32); + } else { + CMD_ERR("invalid type %s\n", type); + return CMD_STATUS_INVALID_ARG; + } + + cmd_write_respond(CMD_STATUS_OK, "OK"); + + cmd_raw_mode_enable(); + cmd_raw_mode_read(p, size, 30000); + cmd_raw_mode_disable(); + + return CMD_STATUS_ACKED; +} + +static struct cmd_data g_crypto_cmds[] = { + { "addkey", cmd_crypto_addkey_exec }, + { "delkey", cmd_crypto_delkey_exec }, + { "keyiv", cmd_crypto_keyiv_exec }, + { "enc", cmd_crypto_enc_exec }, + { "dec", cmd_crypto_dec_exec }, + { "crc", cmd_crypto_crc_exec }, + { "md5", cmd_crypto_md5_exec }, + { "sha1", cmd_crypto_sha1_exec }, + { "sha256", cmd_crypto_sha256_exec }, + { "prng", cmd_crypto_prng_exec }, + { "digiv", cmd_crypto_digiv_exec }, +}; + +enum cmd_status cmd_crypto_exec(char *cmd) +{ + return cmd_exec(cmd, g_crypto_cmds, cmd_nitems(g_crypto_cmds)); +} diff --git a/platform/mcu/xr871/project/common/cmd/cmd_ce.h b/platform/mcu/xr871/project/common/cmd/cmd_ce.h new file mode 100644 index 0000000000..2dfe7162ec --- /dev/null +++ b/platform/mcu/xr871/project/common/cmd/cmd_ce.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _CMD_CE_H_ +#define _CMD_CE_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +enum cmd_status cmd_crypto_exec(char *cmd); + +#ifdef __cplusplus +} +#endif + +#endif /* _CMD_CE_H_ */ diff --git a/platform/mcu/xr871/project/common/cmd/cmd_cedarx.c b/platform/mcu/xr871/project/common/cmd/cmd_cedarx.c new file mode 100644 index 0000000000..4767211243 --- /dev/null +++ b/platform/mcu/xr871/project/common/cmd/cmd_cedarx.c @@ -0,0 +1,532 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifdef __PRJ_CONFIG_XPLAYER + +#include "cmd_util.h" + +//#include +#include +#include +#include +#include "pthread.h" +//#include +//#include "errno.h" +//#include + +#include "iniparserapi.h" + +//#include "cdx_config.h" +#include +#include "xplayer.h" +#include "CdxTypes.h" +#include "fs/fatfs/ff.h" +//#include "memoryAdapter.h" +//#include "deinterlace.h" +//typedef unsigned long uintptr_t ; +extern SoundCtrl* SoundDeviceCreate(); + +static const int STATUS_STOPPED = 0; +static const int STATUS_PREPARING = 1; +static const int STATUS_PREPARED = 2; +static const int STATUS_PLAYING = 3; +static const int STATUS_PAUSED = 4; +static const int STATUS_SEEKING = 5; + +typedef struct DemoPlayerContext +{ + XPlayer* mAwPlayer; + int mPreStatus; + int mStatus; + int mSeekable; + int mError; + pthread_mutex_t mMutex; + sem_t mStoped; + sem_t mPrepared; +// int mVideoFrameNum; +}DemoPlayerContext; + + +//* define commands for user control. +typedef struct Command +{ + const char* strCommand; + int nCommandId; + const char* strHelpMsg; +}Command; + +#define COMMAND_HELP 0x1 //* show help message. +#define COMMAND_QUIT 0x2 //* quit this program. + +#define COMMAND_SET_SOURCE 0x101 //* set url of media file. +#define COMMAND_PLAY 0x102 //* start playback. +#define COMMAND_PAUSE 0x103 //* pause the playback. +#define COMMAND_STOP 0x104 //* stop the playback. +#define COMMAND_SEEKTO 0x105 //* seek to posion, in unit of second. +#define COMMAND_SHOW_MEDIAINFO 0x106 //* show media information. +#define COMMAND_SHOW_DURATION 0x107 //* show media duration, in unit of second. +#define COMMAND_SHOW_POSITION 0x108 //* show current play position, in unit of second. +#define COMMAND_SWITCH_AUDIO 0x109 //* switch autio track. +#define COMMAND_SETSPEED 0x10a + + +static void set_source(DemoPlayerContext *demoPlayer, char* pUrl) +{ + demoPlayer->mSeekable = 1; + + //* set url to the AwPlayer. + if(XPlayerSetDataSourceUrl(demoPlayer->mAwPlayer, + (const char*)pUrl, NULL, NULL) != 0) + { + printf("error:\n"); + printf(" AwPlayer::setDataSource() return fail.\n"); + return; + } + printf("setDataSource end\n"); + + if (!strncmp(pUrl, "http://", 7)) { + if(XPlayerPrepareAsync(demoPlayer->mAwPlayer) != 0) + { + printf("error:\n"); + printf(" AwPlayer::prepareAsync() return fail.\n"); + pthread_mutex_unlock(&demoPlayer->mMutex); + return; + } + sem_wait(&demoPlayer->mPrepared); + } + + //* start preparing. + pthread_mutex_lock(&demoPlayer->mMutex); + demoPlayer->mPreStatus = STATUS_STOPPED; + demoPlayer->mStatus = STATUS_PREPARING; + pthread_mutex_unlock(&demoPlayer->mMutex); + printf("preparing...\n"); +} + +static void play(DemoPlayerContext *demoPlayer) +{ + if(XPlayerStart(demoPlayer->mAwPlayer) != 0) + { + printf("error:\n"); + printf(" AwPlayer::start() return fail.\n"); + return; + } + demoPlayer->mPreStatus = demoPlayer->mStatus; + demoPlayer->mStatus = STATUS_PLAYING; + printf("playing.\n"); +} + +static void pause(DemoPlayerContext *demoPlayer) +{ + if(XPlayerPause(demoPlayer->mAwPlayer) != 0) + { + printf("error:\n"); + printf(" AwPlayer::pause() return fail.\n"); + return; + } + demoPlayer->mPreStatus = demoPlayer->mStatus; + demoPlayer->mStatus = STATUS_PAUSED; + printf("paused.\n"); +} + +static void stop(DemoPlayerContext *demoPlayer) +{ + if(XPlayerReset(demoPlayer->mAwPlayer) != 0) + { + printf("error:\n"); + printf(" AwPlayer::reset() return fail.\n"); + return; + } + demoPlayer->mPreStatus = demoPlayer->mStatus; + demoPlayer->mStatus = STATUS_STOPPED; + printf("stopped.\n"); +} + + +//* a callback for awplayer. +static int CallbackForAwPlayer(void* pUserData, int msg, int ext1, void* param) +{ + DemoPlayerContext* pDemoPlayer = (DemoPlayerContext*)pUserData; + + switch(msg) + { + case AWPLAYER_MEDIA_INFO: + { + switch(ext1) + { + case AW_MEDIA_INFO_NOT_SEEKABLE: + { + pDemoPlayer->mSeekable = 0; + printf("info: media source is unseekable.\n"); + break; + } + case AW_MEDIA_INFO_RENDERING_START: + { + printf("info: start to show pictures.\n"); + break; + } + } + break; + } + + case AWPLAYER_MEDIA_ERROR: + { + pthread_mutex_lock(&pDemoPlayer->mMutex); + pDemoPlayer->mStatus = STATUS_STOPPED; + pDemoPlayer->mPreStatus = STATUS_STOPPED; + pthread_mutex_unlock(&pDemoPlayer->mMutex); + printf("error: open media source fail.\n"); + sem_post(&pDemoPlayer->mPrepared); + sem_post(&pDemoPlayer->mStoped); + pDemoPlayer->mError = 1; + + loge(" error : how to deal with it"); + break; + } + + case AWPLAYER_MEDIA_PREPARED: + { + logd("info : preared"); + pDemoPlayer->mPreStatus = pDemoPlayer->mStatus; + pDemoPlayer->mStatus = STATUS_PREPARED; + sem_post(&pDemoPlayer->mPrepared); + printf("info: prepare ok.\n"); + break; + } + + case AWPLAYER_MEDIA_PLAYBACK_COMPLETE: + { + sem_post(&pDemoPlayer->mStoped);//* stop the player. + //* TODO + break; + } + + case AWPLAYER_MEDIA_SEEK_COMPLETE: + { + pthread_mutex_lock(&pDemoPlayer->mMutex); + pDemoPlayer->mStatus = pDemoPlayer->mPreStatus; + printf("info: seek ok.\n"); + pthread_mutex_unlock(&pDemoPlayer->mMutex); + break; + } + + default: + { + //printf("warning: unknown callback from AwPlayer.\n"); + break; + } + } + + return 0; +} + +static uint8_t cedarx_inited = 0; +static uint8_t xplayer_inited = 0; +void AwParserInit(void); +void AwStreamInit(void); + +static DemoPlayerContext *demoPlayer; +static FATFS fs; + +static enum cmd_status cmd_cedarx_create_exec(char *cmd) +{ + FRESULT res; + demoPlayer = malloc(sizeof(*demoPlayer)); + + if (cedarx_inited++ == 0) { + printf_lock_init(); + if ((res = f_mount(&fs, "0:/", 1)) != FR_OK) { + printf("can not mount\n"); + return -1; + } else { + printf("mount success\n"); + } + } + + if (!xplayer_inited) { + xplayer_inited = 1; + AwParserInit(); + AwStreamInit(); + } + + //* create a player. + memset(demoPlayer, 0, sizeof(DemoPlayerContext)); + pthread_mutex_init(&demoPlayer->mMutex, NULL); + sem_init(&demoPlayer->mStoped, 0, 0); + sem_init(&demoPlayer->mPrepared, 0, 0); + + demoPlayer->mAwPlayer = XPlayerCreate(); + if(demoPlayer->mAwPlayer == NULL) + { + printf("can not create AwPlayer, quit.\n"); + return -1; + } else { + printf("create AwPlayer success.\n"); + } + + //* set callback to player. + XPlayerSetNotifyCallback(demoPlayer->mAwPlayer, CallbackForAwPlayer, (void*)demoPlayer); + + //* check if the player work. + if(XPlayerInitCheck(demoPlayer->mAwPlayer) != 0) + { + printf("initCheck of the player fail, quit.\n"); + XPlayerDestroy(demoPlayer->mAwPlayer); + demoPlayer->mAwPlayer = NULL; + return -1; + } else + printf("AwPlayer check success.\n"); + + SoundCtrl* sound = SoundDeviceCreate(); + + XPlayerSetAudioSink(demoPlayer->mAwPlayer, (void*)sound); + + return CMD_STATUS_OK; +} + +static enum cmd_status cmd_cedarx_destroy_exec(char *cmd) +{ + FRESULT res; + + printf("destroy AwPlayer.\n"); + + if(demoPlayer->mAwPlayer != NULL) + { + XPlayerDestroy(demoPlayer->mAwPlayer); + demoPlayer->mAwPlayer = NULL; + } + + printf("destroy AwPlayer 1.\n"); + pthread_mutex_destroy(&demoPlayer->mMutex); + + sem_destroy(&demoPlayer->mPrepared); + sem_destroy(&demoPlayer->mStoped); + + if (--cedarx_inited == 0) { + if ((res = f_mount(NULL, "", 1)) != FR_OK) + printf("failed to unmount\n"); + printf_lock_deinit(); + } + + free(demoPlayer); + + return CMD_STATUS_OK; +} + +static enum cmd_status cmd_cedarx_play_exec(char *cmd) +{ + play(demoPlayer); + return CMD_STATUS_OK; +} + +static enum cmd_status cmd_cedarx_stop_exec(char *cmd) +{ + stop(demoPlayer); + msleep(5); + return CMD_STATUS_OK; +} + +static enum cmd_status cmd_cedarx_pause_exec(char *cmd) +{ + pause(demoPlayer); + return CMD_STATUS_OK; +} + +static enum cmd_status cmd_cedarx_seturl_exec(char *cmd) +{ + set_source(demoPlayer, cmd); + return CMD_STATUS_OK; +} + +static enum cmd_status cmd_cedarx_getpos_exec(char *cmd) +{ + int msec; + XPlayerGetCurrentPosition(demoPlayer->mAwPlayer, &msec); + cmd_write_respond(CMD_STATUS_OK, "played time: %d ms", msec); + return CMD_STATUS_ACKED; +} + +static enum cmd_status cmd_cedarx_seek_exec(char *cmd) +{ + int nSeekTimeMs = atoi(cmd); + + XPlayerSeekTo(demoPlayer->mAwPlayer, nSeekTimeMs); + return CMD_STATUS_OK; +} + +#include "driver/chip/hal_codec.h" +#include "audio/manager/audio_manager.h" + +static enum cmd_status cmd_cedarx_setvol_exec(char *cmd) +{ + int vol = atoi(cmd); + if (vol > 31) + vol = 31; + + aud_mgr_handler(AUDIO_DEVICE_MANAGER_VOLUME, vol); + + return CMD_STATUS_OK; +} + +static enum cmd_status cmd_cedarx_playdic_exec(char *cmd) +{ + return CMD_STATUS_OK; +} + + +#include +#include +#include +#include "awencoder.h" +#include "RecoderWriter.h" +#include "xrecord.h" +#include "CaptureControl.h" + + +static uint8_t xrecord_inited = 0; +static XRecord *xrecord; + +static enum cmd_status cmd_cedarx_rec_exec(char *cmd) +{ + FRESULT res; + + XRECODER_AUDIO_ENCODE_TYPE type; + if (strstr(cmd, ".amr")) + type = XRECODER_AUDIO_ENCODE_AMR_TYPE; + else if (strstr(cmd, ".pcm")) + type = XRECODER_AUDIO_ENCODE_PCM_TYPE; + else { + printf("do not support this encode type\n"); + return CMD_STATUS_INVALID_ARG; + } + + if (cedarx_inited++ == 0) { + printf_lock_init(); + + if ((res = f_mount(&fs, "0:/", 1)) != FR_OK) { + printf("can not mount\n"); + return -1; + } + } + + + void AwMuxerInit(); + if (xrecord_inited == 0) { + AwMuxerInit(); + xrecord_inited = 1; + } + + xrecord = XRecordCreate(); + if (xrecord == NULL) + printf("create success\n"); + + CaptureCtrl* cap = CaptureDeviceCreate(); + if (!cap) + printf("cap device create failed\n"); + XRecordSetAudioCap(xrecord, cap); + AudioEncodeConfig audioConfig; + + audioConfig.nType = AUDIO_ENCODE_AMR_TYPE; + audioConfig.nInChan = 1; + audioConfig.nInSamplerate = 8000; + audioConfig.nOutChan = 1; + audioConfig.nOutSamplerate = 8000; + audioConfig.nSamplerBits = 16; + audioConfig.nBitrate = 12200; + audioConfig.nFrameStyle = 0; + + + XRecordSetDataDstUrl(xrecord, cmd, NULL, NULL); + XRecordSetAudioEncodeType(xrecord, type, &audioConfig); + + XRecordPrepare(xrecord); + XRecordStart(xrecord); + printf("record start\n"); + + + return CMD_STATUS_OK; +} + +static enum cmd_status cmd_cedarx_end_exec(char *cmd) +{ + FRESULT res; + + XRecordStop(xrecord); + printf("record stop\n"); + XRecordDestroy(xrecord); + printf("record destroy\n"); + + if (--cedarx_inited == 0) { + printf_lock_deinit(); + + if ((res = f_mount(NULL, "", 1)) != FR_OK) + printf("failed to unmount\n"); + } + + return CMD_STATUS_OK; +} + + +/* + * brief cedarx Test Command + * command cedarx create + * cedarx destroy + * cedarx play + * cedarx stop + * cedarx pause + * cedarx seturl file://music/01.mp3 + * cedarx getpos + * cedarx seek 6000 + * cedarx setvol 8 + * cedarx playdic file://music + * cedarx rec file://record/wechat.amr + * cedarx end + */ +static struct cmd_data g_cedarx_cmds[] = { + { "create", cmd_cedarx_create_exec }, + { "destroy", cmd_cedarx_destroy_exec }, + { "play", cmd_cedarx_play_exec }, + { "stop", cmd_cedarx_stop_exec }, + { "pause", cmd_cedarx_pause_exec }, + { "seturl", cmd_cedarx_seturl_exec }, + { "getpos", cmd_cedarx_getpos_exec }, + { "seek", cmd_cedarx_seek_exec }, + { "setvol", cmd_cedarx_setvol_exec }, + { "playdic", cmd_cedarx_playdic_exec }, + + { "rec", cmd_cedarx_rec_exec }, + { "end", cmd_cedarx_end_exec }, +}; + +enum cmd_status cmd_cedarx_exec(char *cmd) +{ + return cmd_exec(cmd, g_cedarx_cmds, cmd_nitems(g_cedarx_cmds)); +} + +#endif /* __PRJ_CONFIG_XPLAYER */ diff --git a/platform/mcu/xr871/project/common/cmd/cmd_cedarx.h b/platform/mcu/xr871/project/common/cmd/cmd_cedarx.h new file mode 100644 index 0000000000..85b997ee85 --- /dev/null +++ b/platform/mcu/xr871/project/common/cmd/cmd_cedarx.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _CMD_CEDARX_H_ +#define _CMD_CEDARX_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __PRJ_CONFIG_XPLAYER +enum cmd_status cmd_cedarx_exec(char *cmd); +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* _CMD_CEDARX_H_ */ + diff --git a/platform/mcu/xr871/project/common/cmd/cmd_clock.c b/platform/mcu/xr871/project/common/cmd/cmd_clock.c new file mode 100644 index 0000000000..b84c21f7d2 --- /dev/null +++ b/platform/mcu/xr871/project/common/cmd/cmd_clock.c @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "cmd_util.h" +#include "driver/chip/hal_clock.h" + +enum CMD_CLOCK_TYPE { + CMD_CLOCK_TYPE_HF = 0, + CMD_CLOCK_TYPE_LF, + CMD_CLOCK_TYPE_CPU, + CMD_CLOCK_TYPE_DEV, + CMD_CLOCK_TYPE_AHB1, + CMD_CLOCK_TYPE_AHB2, + CMD_CLOCK_TYPE_APB, + CMD_CLOCK_TYPE_NUM +}; + +/* index by enum CMD_CLOCK_TYPE */ +static const char *cmd_clock_type_str[CMD_CLOCK_TYPE_NUM] = { + "LF", "HF", "CPU", "DEV", "AHB1", "AHB2", "APB" +}; + +static uint32_t cmd_clock_get(enum CMD_CLOCK_TYPE type) +{ + uint32_t clock; + + switch (type) { + case CMD_CLOCK_TYPE_HF: + clock = HAL_GetLFClock(); + break; + case CMD_CLOCK_TYPE_LF: + clock = HAL_GetHFClock(); + break; + case CMD_CLOCK_TYPE_CPU: + clock = HAL_GetCPUClock(); + break; + case CMD_CLOCK_TYPE_DEV: + clock = HAL_GetDevClock(); + break; + case CMD_CLOCK_TYPE_AHB1: + clock = HAL_GetAHB1Clock(); + break; + case CMD_CLOCK_TYPE_AHB2: + clock = HAL_GetAHB2Clock(); + break; + case CMD_CLOCK_TYPE_APB: + clock = HAL_CCM_BusGetAPBClock(); + break; + default: + clock = 0; + break; + } + return clock; +} + +/* + * drv clock + */ +enum cmd_status cmd_clock_exec(char *cmd) +{ + int type; + uint32_t clock = 0; + + for (type = 0; type < CMD_CLOCK_TYPE_NUM; ++type) { + if (cmd_strcmp(cmd, cmd_clock_type_str[type]) == 0) { + clock = cmd_clock_get((enum CMD_CLOCK_TYPE)type); + break; + } + } + + if (clock != 0) { + cmd_write_respond(CMD_STATUS_OK, "%d Hz (%s clock)", clock, cmd); + return CMD_STATUS_ACKED; + } + + return CMD_STATUS_INVALID_ARG; +} diff --git a/platform/mcu/xr871/project/common/cmd/cmd_clock.h b/platform/mcu/xr871/project/common/cmd/cmd_clock.h new file mode 100644 index 0000000000..31c56bf045 --- /dev/null +++ b/platform/mcu/xr871/project/common/cmd/cmd_clock.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _CMD_CLOCK_H_ +#define _CMD_CLOCK_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +enum cmd_status cmd_clock_exec(char *cmd); + +#ifdef __cplusplus +} +#endif + +#endif /* _CMD_CLOCK_H_ */ diff --git a/platform/mcu/xr871/project/common/cmd/cmd_debug.h b/platform/mcu/xr871/project/common/cmd/cmd_debug.h new file mode 100644 index 0000000000..0df08daffb --- /dev/null +++ b/platform/mcu/xr871/project/common/cmd/cmd_debug.h @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _CMD_DEBUG_H_ +#define _CMD_DEBUG_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define CMD_DBG_ON 1 +#define CMD_WRN_ON 1 +#define CMD_ERR_ON 1 + +#define CMD_SYSLOG printf + +#define CMD_LOG(flags, fmt, arg...) \ + do { \ + if (flags) \ + CMD_SYSLOG(fmt, ##arg); \ + } while (0) + +#define CMD_DBG(fmt, arg...) \ + CMD_LOG(CMD_DBG_ON, "[cmd] "fmt, ##arg) + +#define CMD_WRN(fmt, arg...) \ + CMD_LOG(CMD_WRN_ON, "[cmd WRN] "fmt, ##arg) + +#define CMD_ERR(fmt, arg...) \ + CMD_LOG(CMD_ERR_ON, "[cmd ERR] %s():%d, "fmt, __func__, __LINE__, ##arg) + +#ifdef __cplusplus +} +#endif + +#endif /* _CMD_DEBUG_H_ */ diff --git a/platform/mcu/xr871/project/common/cmd/cmd_defs.h b/platform/mcu/xr871/project/common/cmd/cmd_defs.h new file mode 100644 index 0000000000..e99ce7c5c4 --- /dev/null +++ b/platform/mcu/xr871/project/common/cmd/cmd_defs.h @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _CMD_DEFS_H_ +#define _CMD_DEFS_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +enum cmd_code_type { + CMD_CODE_TYEP_STATUS = 0, + CMD_CODE_TYEP_EVENT, +}; + +enum cmd_status { + CMD_STATUS_ACKED = 100, /* already acked, no need to send respond */ + + /* success status */ + CMD_STATUS_SUCCESS_MIN = 200, + CMD_STATUS_OK = 200, /* command exec success */ + CMD_STATUS_SUCCESS_MAX = 200, + + /* error status */ + CMD_STATUS_ERROR_MIN = 400, + CMD_STATUS_UNKNOWN_CMD = 400, /* unknown command */ + CMD_STATUS_INVALID_ARG = 401, /* invalid argument */ + CMD_STATUS_FAIL = 402, /* command exec failed */ + CMD_STATUS_ERROR_MAX = 402, +}; + +enum cmd_event { + CMD_EVENT_MIN = 600, + CMD_EVENT_TEST_FINISH = 600, + CMD_EVENT_TIMER_NOTIFY = 601, + CMD_EVENT_RTC_NOTIFY = 602, + CMD_EVENT_MQTT_MSG_RECV = 603, + CMD_EVENT_WDG_TIMEOUT = 604, + CMD_EVENT_MAX = 604, +}; + +#ifdef __cplusplus +} +#endif + +#endif /* _CMD_DEFS_H_ */ diff --git a/platform/mcu/xr871/project/common/cmd/cmd_dhcpd.c b/platform/mcu/xr871/project/common/cmd/cmd_dhcpd.c new file mode 100644 index 0000000000..78e894e443 --- /dev/null +++ b/platform/mcu/xr871/project/common/cmd/cmd_dhcpd.c @@ -0,0 +1,139 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "cmd_util.h" +#include "cmd_dhcpd.h" +#include "net/udhcp/usr_dhcpd.h" +#include "net/lwip/ipv4/lwip/inet.h" + +#define CMD_DHCPD_ADDR_START "192.168.51.150" +#define CMD_DHCPD_ADDR_END "192.168.51.155" + +static struct dhcp_server_info dhcpd_info; + +enum cmd_status dhcpd_start_exec(char *cmd) +{ + if (dhcpd_info.addr_start == 0) + dhcpd_info.addr_start = inet_addr(CMD_DHCPD_ADDR_START); + if (dhcpd_info.addr_end == 0) + dhcpd_info.addr_end = inet_addr(CMD_DHCPD_ADDR_END); + if (dhcpd_info.lease_time == 0) + dhcpd_info.lease_time = 60*60*12; + + dhcp_server_start(&dhcpd_info); + return CMD_STATUS_OK; +} + +enum cmd_status dhcpd_stop_exec(char *cmd) +{ + dhcp_server_stop(); + return CMD_STATUS_OK; +} + +enum cmd_status dhcpd_set_ippool_exec(char *cmd) +{ + int argc; + char *argv[2]; + ip_addr_t ip_addr_start; + ip_addr_t ip_addr_end; + + argc = cmd_parse_argv(cmd, argv, cmd_nitems(argv)); + if (argc < 2) { + CMD_ERR("invalid dhcp cmd, argc %d\n", argc); + return CMD_STATUS_INVALID_ARG; + } + if (inet_aton(argv[0], &ip_addr_start) ==0 || + inet_aton(argv[1], &ip_addr_end) == 0) { + CMD_ERR("invalid dhcp cmd <%s %s>\n", argv[0], argv[1]); + return CMD_STATUS_INVALID_ARG; + } + + dhcpd_info.addr_start = ip_addr_start.addr; + dhcpd_info.addr_end = ip_addr_end.addr; + + return CMD_STATUS_OK; +} + +enum cmd_status dhcpd_set_max_leases_exec(char *cmd) +{ + int argc; + char *argv[1]; + int leases; + + argc = cmd_parse_argv(cmd, argv, cmd_nitems(argv)); + if (argc < 1) { + CMD_ERR("invalid dhcp cmd, argc %d\n", argc); + return CMD_STATUS_INVALID_ARG; + } + + leases = cmd_atoi(argv[0]); + if (leases > 254) { + CMD_ERR("invalid dhcp cmd, leases=%s", argv[0]); + return CMD_STATUS_INVALID_ARG; + } + + dhcpd_info.max_leases = leases; + return CMD_STATUS_OK; +} + +enum cmd_status dhcpd_set_lease_time_exec(char *cmd) +{ + int argc; + char *argv[1]; + int lease_time; + + argc = cmd_parse_argv(cmd, argv, cmd_nitems(argv)); + if (argc < 1) { + CMD_ERR("invalid dhcp cmd, argc %d\n", argc); + return CMD_STATUS_INVALID_ARG; + } + if ((lease_time = cmd_atoi(argv[0])) < 0) { + CMD_ERR("invalid dhcp cmd, leasetime=%d", lease_time); + return CMD_STATUS_INVALID_ARG; + } + + dhcpd_info.lease_time = lease_time; + return CMD_STATUS_OK; +} + +/* + * dhcp commands + */ +static struct cmd_data g_dhcpd_cmds[] = { + { "start", dhcpd_start_exec }, + { "stop", dhcpd_stop_exec }, + { "ippool", dhcpd_set_ippool_exec }, + { "leases", dhcpd_set_max_leases_exec }, + { "leasetime", dhcpd_set_lease_time_exec }, +}; + +enum cmd_status cmd_dhcpd_exec(char *cmd) +{ + return cmd_exec(cmd, g_dhcpd_cmds, cmd_nitems(g_dhcpd_cmds)); +} diff --git a/platform/mcu/xr871/project/common/cmd/cmd_dhcpd.h b/platform/mcu/xr871/project/common/cmd/cmd_dhcpd.h new file mode 100644 index 0000000000..b6c1c2208c --- /dev/null +++ b/platform/mcu/xr871/project/common/cmd/cmd_dhcpd.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _CMD_DHCPD_H_ +#define _CMD_DHCPD_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +enum cmd_status cmd_dhcpd_exec(char *cmd); + +#ifdef __cplusplus +} +#endif + +#endif /* _CMD_DHCPD_H_ */ diff --git a/platform/mcu/xr871/project/common/cmd/cmd_echo.c b/platform/mcu/xr871/project/common/cmd/cmd_echo.c new file mode 100644 index 0000000000..cd1f8f074f --- /dev/null +++ b/platform/mcu/xr871/project/common/cmd/cmd_echo.c @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "cmd_util.h" + +enum cmd_status cmd_echo_exec(char *cmd) +{ + cmd_write_respond(CMD_STATUS_OK, "%s", cmd); + return CMD_STATUS_ACKED; +} diff --git a/platform/mcu/xr871/project/common/cmd/cmd_echo.h b/platform/mcu/xr871/project/common/cmd/cmd_echo.h new file mode 100644 index 0000000000..eaa28688d0 --- /dev/null +++ b/platform/mcu/xr871/project/common/cmd/cmd_echo.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _CMD_ECHO_H_ +#define _CMD_ECHO_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +enum cmd_status cmd_echo_exec(char *cmd); + +#ifdef __cplusplus +} +#endif + +#endif /* _CMD_ECHO_H_ */ diff --git a/platform/mcu/xr871/project/common/cmd/cmd_efpg.c b/platform/mcu/xr871/project/common/cmd/cmd_efpg.c new file mode 100644 index 0000000000..c571bfd271 --- /dev/null +++ b/platform/mcu/xr871/project/common/cmd/cmd_efpg.c @@ -0,0 +1,162 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include "sys/xr_debug.h" +#include "cmd_debug.h" +#include "cmd_util.h" +#include "efpg/efpg.h" +#include "console/console.h" + +/* + * efpg + * + * efpg get hosc + * efpg get boot + * efpg get dcxo + * efpg get pout + * efpg get mac + * efpg get chipid + */ + +static enum cmd_status cmd_efpg_start(void) +{ + const char *efpg_key = "efpgtest"; + + cmd_write_respond(CMD_STATUS_OK, "OK"); + + UART_ID uart_id = console_get_uart_id(); + if (uart_id == UART_NUM) { + CMD_ERR("get uart id failed\n"); + return CMD_STATUS_ACKED; + } + + if (efpg_start((uint8_t *)efpg_key, strlen(efpg_key), uart_id, console_disable, console_enable) < 0) { + CMD_ERR("efpg program failed\n"); + return CMD_STATUS_ACKED; + } + + return CMD_STATUS_ACKED; +} + +static void cmd_efpg_log(uint8_t *buf, uint8_t size) +{ + int i; + + CMD_SYSLOG("\n"); + for (i = 0; i < size; i++) + CMD_SYSLOG("[%02d] 0x%02x\n", i, buf[i]); + CMD_SYSLOG("\n"); +} + +static enum cmd_status cmd_efpg_get_exec(char *cmd) +{ + uint8_t buf[32] = {0}; + efpg_field_t field; + + if (cmd_strcmp(cmd, "hosc") == 0) { + field = EFPG_FIELD_HOSC; + } else if (cmd_strcmp(cmd, "boot") == 0) { + field = EFPG_FIELD_BOOT; + } else if (cmd_strcmp(cmd, "dcxo") == 0) { + field = EFPG_FIELD_DCXO; + } else if (cmd_strcmp(cmd, "pout") == 0) { + field = EFPG_FIELD_POUT; + } else if (cmd_strcmp(cmd, "mac") == 0) { + field = EFPG_FIELD_MAC; + } else if (cmd_strcmp(cmd, "chipid") == 0) { + field = EFPG_FIELD_CHIPID; + } else { + CMD_ERR("invalid arg '%s'\n", cmd); + return CMD_STATUS_INVALID_ARG; + } + + if (efpg_read(field, buf) != 0) { + CMD_ERR("efpg read failed, field %d\n", field); + return CMD_STATUS_FAIL; + } + + if (field == EFPG_FIELD_HOSC) { + cmd_efpg_log(buf, 1); + } else if (field == EFPG_FIELD_BOOT) { + cmd_efpg_log(buf, 32); + } else if (field == EFPG_FIELD_DCXO) { + cmd_efpg_log(buf, 1); + } else if (field == EFPG_FIELD_POUT) { + cmd_efpg_log(buf, 3); + } else if (field == EFPG_FIELD_MAC) { + cmd_efpg_log(buf, 6); + } else if (field == EFPG_FIELD_CHIPID) { + cmd_efpg_log(buf, 16); + } + + return CMD_STATUS_OK; +} + +static enum cmd_status cmd_efpg_read_ua_exec(char *cmd) +{ + uint8_t* buf; + int32_t cnt; + uint32_t start_addr, len_bit, len_byte; + + cnt = cmd_sscanf(cmd, "a=%u l=%u", &start_addr, &len_bit); + if (cnt != 2) { + CMD_ERR("invalid arg '%s'\n", cmd); + return CMD_STATUS_INVALID_ARG; + } + + len_byte = len_bit / 8; + if (len_bit % 8) + len_byte ++; + buf = cmd_malloc(len_byte); + if (efpg_read_ua(start_addr, len_bit, buf)) { + CMD_ERR("efpg read user area failed\n"); + return CMD_STATUS_FAIL; + } + CMD_DBG("data:\n"); + print_hex_dump_bytes(buf, len_byte); + cmd_free(buf); + + return CMD_STATUS_OK; +} + + +static struct cmd_data g_efpg_cmds[] = { + { "get", cmd_efpg_get_exec}, + { "read_user_area", cmd_efpg_read_ua_exec}, +}; + +enum cmd_status cmd_efpg_exec(char *cmd) +{ + if (cmd[0] == '\0') + return cmd_efpg_start(); + + return cmd_exec(cmd, g_efpg_cmds, cmd_nitems(g_efpg_cmds)); +} + diff --git a/platform/mcu/xr871/project/common/cmd/cmd_efpg.h b/platform/mcu/xr871/project/common/cmd/cmd_efpg.h new file mode 100644 index 0000000000..8031b36200 --- /dev/null +++ b/platform/mcu/xr871/project/common/cmd/cmd_efpg.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _CMD_EFPG_H_ +#define _CMD_EFPG_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +enum cmd_status cmd_efpg_exec(char *cmd); + +#ifdef __cplusplus +} +#endif + +#endif /* _CMD_EFPG_H_ */ \ No newline at end of file diff --git a/platform/mcu/xr871/project/common/cmd/cmd_etf.c b/platform/mcu/xr871/project/common/cmd/cmd_etf.c new file mode 100644 index 0000000000..12c2c27781 --- /dev/null +++ b/platform/mcu/xr871/project/common/cmd/cmd_etf.c @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "cmd_util.h" + +#include "sys/ota.h" + +enum cmd_status cmd_etf_exec(char *cmd) +{ + ota_cfg_t ota_cfg; + + cmd_write_respond(CMD_STATUS_OK, "%s", cmd); + + ota_cfg.image = OTA_IMAGE_2ND; + ota_cfg.state = OTA_STATE_VERIFIED; + ota_write_cfg(&ota_cfg); + ota_reboot(); + + return CMD_STATUS_ACKED; +} diff --git a/platform/mcu/xr871/project/common/cmd/cmd_etf.h b/platform/mcu/xr871/project/common/cmd/cmd_etf.h new file mode 100644 index 0000000000..afd85fffd8 --- /dev/null +++ b/platform/mcu/xr871/project/common/cmd/cmd_etf.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _CMD_ETF_H_ +#define _CMD_ETF_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +enum cmd_status cmd_etf_exec(char *cmd); + +#ifdef __cplusplus +} +#endif + +#endif /* _CMD_ETF_H_ */ diff --git a/platform/mcu/xr871/project/common/cmd/cmd_flash.c b/platform/mcu/xr871/project/common/cmd/cmd_flash.c new file mode 100644 index 0000000000..03945841d5 --- /dev/null +++ b/platform/mcu/xr871/project/common/cmd/cmd_flash.c @@ -0,0 +1,293 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "cmd_debug.h" +#include "cmd_util.h" +#include "cmd_flash.h" +#include "driver/chip/hal_flash.h" + +#define MFLASH 0 + +static enum cmd_status cmd_flash_start_exec(char *cmd) +{ + if (HAL_Flash_Open(MFLASH, 5000) != HAL_OK) + { + CMD_ERR("flash driver open failed\n"); + return CMD_STATUS_FAIL; + } + + return CMD_STATUS_OK; +} + +static enum cmd_status cmd_flash_stop_exec(char *cmd) +{ + /* deinie driver */ + if (HAL_Flash_Close(0) != HAL_OK) { + CMD_ERR("flash driver close failed\n"); + return CMD_STATUS_FAIL; + } + + return CMD_STATUS_OK; +} + +#define FLASH_TEST_BUF_SIZE (0x100) + +static enum cmd_status cmd_flash_erase_exec(char *cmd) +{ + int32_t cnt; + char size_str[8]; + uint32_t addr; + FlashEraseMode size_type; + int32_t size; + uint8_t buf[FLASH_TEST_BUF_SIZE]; + + /* get param */ + cnt = cmd_sscanf(cmd, "s=%7s a=0x%x", size_str, &addr); + + /* check param */ + if (cnt != 2) { + CMD_ERR("invalid param number %d\n", cnt); + return CMD_STATUS_INVALID_ARG; + } + + if (cmd_strcmp(size_str, "chip") == 0) { + size_type = FLASH_ERASE_CHIP; + size = 0; + } else if (cmd_strcmp(size_str, "64kb") == 0) { + size = 0x10000; + size_type = FLASH_ERASE_64KB; + } else if (cmd_strcmp(size_str, "32kb") == 0) { + size_type = FLASH_ERASE_32KB; + size = 0x8000; + } else if (cmd_strcmp(size_str, "4kb") == 0) { + size = 0x1000; + size_type = FLASH_ERASE_4KB; + } else { + CMD_ERR("invalid size %s\n", size_str); + return CMD_STATUS_INVALID_ARG; + } + + /* erase */ + HAL_Flash_MemoryOf(MFLASH, size_type, addr, &addr); + if (HAL_Flash_Erase(MFLASH, size_type, addr, 1) != HAL_OK) { + CMD_ERR("flash erase failed\n"); + return CMD_STATUS_FAIL; + } + + while (size > 0) { + int32_t tmp_size = (size < FLASH_TEST_BUF_SIZE) ? size : FLASH_TEST_BUF_SIZE; + +// CMD_DBG("tmp_size: %d\n", tmp_size); + + if (HAL_Flash_Read(MFLASH, addr, buf, tmp_size) != HAL_OK) { + CMD_ERR("flash read failed\n"); + return CMD_STATUS_FAIL; + } + + size -= tmp_size; + addr += tmp_size; + + while (--tmp_size >= 0) { + if ((uint8_t)(~(buf[tmp_size])) != 0) { + CMD_ERR("flash write failed: read data from flash != 0xFF, ~data = 0x%x, tmp_size = %d\n", (uint8_t)(~(buf[tmp_size])), tmp_size); + return CMD_STATUS_FAIL; + } + } + + } + + return CMD_STATUS_OK; +} + +static enum cmd_status cmd_flash_write_exec(char *cmd) +{ + uint32_t cnt; + uint32_t addr; + int32_t size; + uint8_t *wbuf; + uint8_t *rbuf; + + /* get param */ + cnt = cmd_sscanf(cmd, "a=0x%x s=%d", &addr, &size); + + /* check param */ + if (cnt != 2) { + CMD_ERR("invalid param number %d\n", cnt); + return CMD_STATUS_INVALID_ARG; + } + + wbuf = cmd_malloc(size); + if (wbuf == NULL) { + CMD_ERR("no memory\n"); + return CMD_STATUS_FAIL; + } + + rbuf = cmd_malloc(size); + if (rbuf == NULL) { + CMD_ERR("no memory\n"); + cmd_free(wbuf); + return CMD_STATUS_FAIL; + } + + + cmd_write_respond(CMD_STATUS_OK, "OK"); + + cmd_raw_mode_enable(); + cmd_raw_mode_read(wbuf, size, 30000); + + + /* write */ + if (HAL_Flash_Write(MFLASH, addr, wbuf, size) != HAL_OK) { + CMD_ERR("flash write failed\n"); + } + + if (HAL_Flash_Read(MFLASH, addr, rbuf, size) != HAL_OK) { + CMD_ERR("flash read failed\n"); + } + + cmd_raw_mode_write(rbuf, size); + cmd_raw_mode_disable(); + + cmd_free(wbuf); + cmd_free(rbuf); + + return CMD_STATUS_ACKED; +} + +static enum cmd_status cmd_flash_read_exec(char *cmd) +{ + int32_t cnt; + uint8_t *buf; + uint32_t addr; + uint32_t size; + + /* get param */ + cnt = cmd_sscanf(cmd, "a=0x%x s=%u", &addr, &size); + + /* check param */ + if (cnt != 2) { + CMD_ERR("invalid param number %d\n", cnt); + return CMD_STATUS_INVALID_ARG; + } + + + /* read */ + buf = cmd_malloc(size); + if (buf == NULL) { + CMD_ERR("no memory\n"); + cmd_free(buf); + return CMD_STATUS_FAIL; + } + + cmd_write_respond(CMD_STATUS_OK, "OK"); + + if (HAL_Flash_Read(MFLASH, addr, buf, size) != HAL_OK) { + CMD_ERR("spi driver read failed\n"); + } + + cmd_raw_mode_enable(); + cmd_msleep(1000); + cmd_raw_mode_write(buf, size); + cmd_raw_mode_disable(); + + cmd_free(buf); + return CMD_STATUS_ACKED; +} + +static enum cmd_status cmd_flash_overwrite_exec(char *cmd) +{ + uint32_t cnt; + uint32_t addr; + int32_t size; + uint8_t *wbuf; + int ret; + + /* get param */ + cnt = cmd_sscanf(cmd, "a=0x%x s=%d", &addr, &size); + + /* check param */ + if (cnt != 2) { + CMD_ERR("invalid param number %d\n", cnt); + return CMD_STATUS_INVALID_ARG; + } + + wbuf = cmd_malloc(size); + if (wbuf == NULL) { + CMD_ERR("no memory\n"); + return CMD_STATUS_FAIL; + } + + cmd_write_respond(CMD_STATUS_OK, "OK"); + + cmd_raw_mode_enable(); + cmd_raw_mode_read(wbuf, size, 30000); + + /* write */ + if ((ret = HAL_Flash_Overwrite(MFLASH, addr, wbuf, size)) != HAL_OK) { + CMD_ERR("flash write failed: %d\n", ret); + } + + if ((ret = HAL_Flash_Check(MFLASH, addr, wbuf, size)) != 0) { + CMD_ERR("flash write not success %d\n", ret); + } + + cmd_raw_mode_write(wbuf, size); + cmd_raw_mode_disable(); + + cmd_free(wbuf); + + return CMD_STATUS_ACKED; +} + +/* + * brief Flash Auto Test Command + * command start {spiNum} {csNum} {freq} {mode} + * stop + * config {ioMode}// -----need to be supported by Flash chip + * erase {size} {addr} + * write {addr} "{str}" + * read {str/hex} {addr} {size} // recommanded that size should not too large + */ +static struct cmd_data g_flash_cmds[] = { + { "start", cmd_flash_start_exec }, + { "stop", cmd_flash_stop_exec }, + { "erase", cmd_flash_erase_exec }, + { "write", cmd_flash_write_exec }, + { "read", cmd_flash_read_exec }, + { "overwrite", cmd_flash_overwrite_exec }, +}; + +enum cmd_status cmd_flash_exec(char *cmd) +{ + return cmd_exec(cmd, g_flash_cmds, cmd_nitems(g_flash_cmds)); +} + + + + diff --git a/platform/mcu/xr871/project/common/cmd/cmd_flash.h b/platform/mcu/xr871/project/common/cmd/cmd_flash.h new file mode 100644 index 0000000000..c0b79e939d --- /dev/null +++ b/platform/mcu/xr871/project/common/cmd/cmd_flash.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _CMD_FLASH_H_ +#define _CMD_FLASH_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +enum cmd_status cmd_flash_exec(char *cmd); + +#ifdef __cplusplus +} +#endif + +#endif /* _CMD_FLASH_H_ */ diff --git a/platform/mcu/xr871/project/common/cmd/cmd_fs.c b/platform/mcu/xr871/project/common/cmd/cmd_fs.c new file mode 100644 index 0000000000..13e3fd81b2 --- /dev/null +++ b/platform/mcu/xr871/project/common/cmd/cmd_fs.c @@ -0,0 +1,461 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "cmd_util.h" + +#include "fs/fatfs/ff.h" + +#define FS_TEST_LFN 1 + +#if FS_TEST_LFN +#define FS_TEST_LFN_STR "longlongLongLongName" +#else +#define FS_TEST_LFN_STR "" +#endif + +#define FS_TEST_DATA_BUF_SIZE 1024 +#define FS_TEST_PATH_MAX_SIZE 256 + +#define CMD_FS_VOL_NAME "" + +static FATFS cmd_fs; + +int fs_test_create_file(const char *file_path, int file_size, uint8_t *buf, int buf_size) +{ + int len; + uint32_t byte_w, left = file_size; + FRESULT res; + uint32_t start_tm; + + FIL *fp = cmd_malloc(sizeof(FIL)); + cmd_memset(fp, 0, sizeof(FIL)); + + start_tm = OS_GetTicks(); + res = f_open(fp, file_path, FA_WRITE | FA_CREATE_ALWAYS); + if (res != FR_OK) { + CMD_ERR("create file %s failed, return %d\n", file_path, res); + cmd_free(fp); + return -1; + } + + while (left > 0) { + len = left > buf_size ? buf_size : left; + res = f_write(fp, buf, len, &byte_w); + if (res != FR_OK || len != byte_w) { + CMD_ERR("write file failed, len %d, left %d, return %d, written %u\n", + len, left, res, byte_w); + break; + } + left -= byte_w; + } + + f_close(fp); + cmd_free(fp); + + CMD_DBG("create file %s %s, size %d B (%d KB, %d MB), cost %u ms\n", + file_path, (left > 0) ? "failed" : "success", + file_size, file_size / 1024, file_size / 1024 / 1024, + OS_GetTicks() - start_tm); + return (left > 0 ? -1 : 0); +} + +int fs_test_create_dir(const char *dir_path, int file_num, int file_size_unit) +{ + int i; + char *path; + uint8_t *buf; + FRESULT res; + + res = f_mkdir(dir_path); + if (res != FR_OK) { + CMD_ERR("create dir %s failed, return %d\n", dir_path, res); + return -1; + } else { + CMD_DBG("create dir %s success\n", dir_path); + } + + path = cmd_malloc(FS_TEST_PATH_MAX_SIZE); + buf = cmd_malloc(FS_TEST_DATA_BUF_SIZE); + for (i = 0; i < FS_TEST_DATA_BUF_SIZE; ++i) { + buf[i] = '0' + (i % 10); + } + + for (i = 0; i < file_num; ++i) { + cmd_snprintf(path, FS_TEST_PATH_MAX_SIZE, "%s/file-%s-%02d.dat", dir_path, FS_TEST_LFN_STR, i); + if (fs_test_create_file(path, i * file_size_unit, buf, FS_TEST_DATA_BUF_SIZE) < 0) { + break; + } + } + + cmd_free(buf); + cmd_free(path); + return (i < file_num ? -1 : 0); +} + +int fs_test_rm_dir(const char *dir_path, int do_rm_dir) +{ + int ret = -1; + FRESULT res; + DIR *dp = NULL; + FILINFO *entry = NULL; + char *path = NULL; + + path = cmd_malloc(FS_TEST_PATH_MAX_SIZE); + + dp = cmd_malloc(sizeof(DIR)); + cmd_memset(dp, 0, sizeof(DIR)); + + entry = cmd_malloc(sizeof(FILINFO)); + cmd_memset(entry, 0, sizeof(FILINFO)); + + res = f_opendir(dp, dir_path); + if (res != FR_OK) { + CMD_ERR("open dir %s failed, return %d\n", dir_path, res); + goto out; + } + + while (1) { + res = f_readdir(dp, entry); + if (res != FR_OK) { + CMD_ERR("read dir %s failed, return %d\n", dir_path, res); + break; + } + + if (entry->fname[0] == 0) { + CMD_DBG("delete dir %s files finish\n", dir_path); + ret = 0; + break; + } + + cmd_snprintf(path, FS_TEST_PATH_MAX_SIZE, "%s/%s", dir_path, entry->fname); + if (entry->fattrib & AM_DIR) { + if (fs_test_rm_dir(path, 1) < 0) { + break; + } + } else { + res = f_unlink(path); + CMD_DBG("delete file %s %s\n", path, + (res != FR_OK) ? "failed" : "success"); + if (res != FR_OK) { + break; + } + } + } + + f_closedir(dp); + if (ret == 0 && do_rm_dir) { + res = f_unlink(dir_path); + if (res != FR_OK) { + CMD_ERR("delete dir %s failed, return %d\n", dir_path, res); + ret = -1; + } else { + CMD_DBG("delete dir %s success\n", dir_path); + } + } + +out: + if (entry) + cmd_free(entry); + + if (dp) + cmd_free(dp); + + if (path) + cmd_free(path); + + return ret; +} + +int fs_test_cp_file(const char *file_dst, const char *file_src, uint8_t *buf, int len) +{ + int ret = -1; + uint32_t byte_r, byte_w; + FRESULT res; + uint32_t start_tm; + + start_tm = OS_GetTicks(); + + FIL *fp_r = cmd_malloc(sizeof(FIL)); + cmd_memset(fp_r, 0, sizeof(FIL)); + + FIL *fp_w = cmd_malloc(sizeof(FIL)); + cmd_memset(fp_w, 0, sizeof(FIL)); + + res = f_open(fp_r, file_src, FA_READ | FA_OPEN_EXISTING ); + if (res != FR_OK) { + CMD_ERR("open read file %s failed, return %d\n", file_src, res); + goto out; + } + + res = f_open(fp_w, file_dst, FA_WRITE | FA_CREATE_ALWAYS); + if (res != FR_OK) { + CMD_ERR("open write file %s failed, return %d\n", file_dst, res); + f_close(fp_r); + goto out; + } + + while (1) { + res = f_read(fp_r, buf, len, &byte_r); + if (res != FR_OK) { + CMD_ERR("read file %s failed\n", file_src); + break; + } + + if (byte_r == 0) { + ret = 0; + break; + } + + res = f_write(fp_w, buf, byte_r, &byte_w); + if (res != FR_OK || byte_r != byte_w) { + CMD_ERR("write file %s failed, len %d, return %d, written %u\n", + file_dst, byte_r, res, byte_w); + break; + } + + if (byte_r < len) { + ret = 0; + break; + } + } + + CMD_DBG("copy %s to %s %s, cost %u ms\n", file_src, file_dst, + (ret == 0) ? "success" : "failed", OS_GetTicks() - start_tm); + f_close(fp_w); + f_close(fp_r); + +out: + if (fp_w) + cmd_free(fp_w); + + if (fp_r) + cmd_free(fp_r); + + return ret; +} + +int fs_test_cp_dir( const char *dir_dst, const char *dir_src) +{ + int ret = -1; + FRESULT res; + DIR *dp = NULL; + FILINFO *entry = NULL; + char *path_src = NULL; + char *path_dst = NULL; + uint8_t *buf = NULL; + + res = f_mkdir(dir_dst); + if (res != FR_OK) { + CMD_ERR("create dir %s failed, return %d\n", dir_dst, res); + return -1; + } else { + CMD_DBG("create dir %s success\n", dir_dst); + } + + dp = cmd_malloc(sizeof(DIR)); + cmd_memset(dp, 0, sizeof(DIR)); + + entry = cmd_malloc(sizeof(FILINFO)); + cmd_memset(entry, 0, sizeof(FILINFO)); + + res = f_opendir(dp, dir_src); + if (res != FR_OK) { + CMD_ERR("open dir %s failed, return %d\n", dir_src, res); + goto out; + } + + path_src = cmd_malloc(FS_TEST_PATH_MAX_SIZE); + path_dst = cmd_malloc(FS_TEST_PATH_MAX_SIZE); + buf = cmd_malloc(FS_TEST_DATA_BUF_SIZE); + + while (1) { + res = f_readdir(dp, entry); + if (res != FR_OK) { + CMD_ERR("read dir %s failed, return %d\n", dir_src, res); + goto out; + } + + if (entry->fname[0] == 0) { + break; + } + + cmd_snprintf(path_src, FS_TEST_PATH_MAX_SIZE, "%s/%s", dir_src, entry->fname); + cmd_snprintf(path_dst, FS_TEST_PATH_MAX_SIZE, "%s/%s", dir_dst, entry->fname); + if (entry->fattrib & AM_DIR) { + if (fs_test_cp_dir(path_dst, path_src) < 0) { + goto out; + } + } + + if (fs_test_cp_file(path_dst, path_src, buf, FS_TEST_DATA_BUF_SIZE) < 0) { + goto out; + } + } + + f_closedir(dp); + ret = 0; + +out: + if (buf) + cmd_free(buf); + + if (path_dst) + cmd_free(path_dst); + + if (path_src) + cmd_free(path_src); + + if (entry) + cmd_free(entry); + + if (dp) + cmd_free(dp); + + CMD_DBG("copy dir from %s to %s %s\n", + dir_src, dir_dst, ret == 0 ? "success" : "failed"); + return ret; +} + +struct fs_test_param { + uint8_t task_idx; + uint8_t file_num; + int file_size_unit; +}; + +static void fs_test_task(void *arg) +{ + char dir_path[2][64]; + struct fs_test_param *param = arg; + + CMD_DBG("fatfs test task %d start ...\n", param->task_idx); + + CMD_DBG("*** create dir and file test ***\n"); + cmd_sprintf(dir_path[0], "%s/dir%d_0-%s", CMD_FS_VOL_NAME, param->task_idx, FS_TEST_LFN_STR); + fs_test_create_dir(dir_path[0], param->file_num, param->file_size_unit); + + CMD_DBG("*** copy dir test ***\n"); + cmd_sprintf(dir_path[1], "%s/dir%d_1-%s", CMD_FS_VOL_NAME, param->task_idx, FS_TEST_LFN_STR); + fs_test_cp_dir(dir_path[1], dir_path[0]); + + CMD_DBG("*** copy dir test ***\n"); + cmd_sprintf(dir_path[1], "%s/dir%d_2-%s", CMD_FS_VOL_NAME, param->task_idx, FS_TEST_LFN_STR); + fs_test_cp_dir(dir_path[1], dir_path[0]); + + CMD_DBG("*** remove dir test ***\n"); + cmd_sprintf(dir_path[0], "%s/dir%d_0-%s", CMD_FS_VOL_NAME, param->task_idx, FS_TEST_LFN_STR); + fs_test_rm_dir(dir_path[0], 1); + + CMD_DBG("fatfs test task %d finish\n", param->task_idx); + cmd_free(arg); + OS_ThreadDelete(NULL); +} + +static enum cmd_status cmd_fs_mount_exec(char *cmd) +{ + FRESULT res; + + res = f_mount(&cmd_fs, "", 1); + if (res != FR_OK) { + CMD_ERR("mount fatfs failed, return %d\n", res); + return CMD_STATUS_FAIL; + } + + CMD_DBG("mount fatfs success\n"); + return CMD_STATUS_OK; +} + +static enum cmd_status cmd_fs_unmount_exec(char *cmd) +{ + FRESULT res; + + res = f_mount(NULL, "", 1); + if (res != FR_OK) { + CMD_ERR("unmount fatfs failed, return %d\n", res); + return CMD_STATUS_FAIL; + } + + cmd_memset(&cmd_fs, 0, sizeof(cmd_fs)); + CMD_DBG("unmount fatfs success\n"); + return CMD_STATUS_OK; +} + +// fs test t=1, n=2, u=2097152 // 2MB +static enum cmd_status cmd_fs_test_exec(char *cmd) +{ + OS_Thread_t thread; + int i, thread_cnt, file_num, file_size_unit, cnt; + struct fs_test_param *param; + + cnt = cmd_sscanf(cmd, "t=%d, n=%d, u=%d", &thread_cnt, &file_num, &file_size_unit); + if (cnt != 3 || thread_cnt < 1) { + CMD_ERR("invalid argument %s\n", cmd); + return CMD_STATUS_FAIL; + } + + for (i = 0; i < thread_cnt; ++i) { + param = cmd_malloc(sizeof(struct fs_test_param)); + param->task_idx = i; + param->file_num = file_num; + param->file_size_unit = file_size_unit; + + OS_ThreadSetInvalid(&thread); + if (OS_ThreadCreate(&thread, + "", + fs_test_task, + (void *)param, + OS_THREAD_PRIO_APP, + 2 * 1024) != OS_OK) { + CMD_ERR("create fs test task %d failed\n", i); + return CMD_STATUS_FAIL; + } + OS_Sleep(1); + } + + return CMD_STATUS_OK; +} + +static enum cmd_status cmd_fs_emptydir_exec(char *cmd) +{ + if (fs_test_rm_dir(cmd, 0) < 0) + return CMD_STATUS_FAIL; + else + return CMD_STATUS_OK; +} + +static struct cmd_data g_fs_cmds[] = { + { "mount", cmd_fs_mount_exec }, + { "unmount", cmd_fs_unmount_exec }, + { "test", cmd_fs_test_exec }, + { "rmdir", cmd_fs_emptydir_exec }, +}; + +enum cmd_status cmd_fs_exec(char *cmd) +{ + return cmd_exec(cmd, g_fs_cmds, cmd_nitems(g_fs_cmds)); +} diff --git a/platform/mcu/xr871/project/common/cmd/cmd_fs.h b/platform/mcu/xr871/project/common/cmd/cmd_fs.h new file mode 100644 index 0000000000..6773e9f249 --- /dev/null +++ b/platform/mcu/xr871/project/common/cmd/cmd_fs.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _CMD_FS_H_ +#define _CMD_FS_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +enum cmd_status cmd_fs_exec(char *cmd); + +#ifdef __cplusplus +} +#endif + +#endif /* _CMD_FS_H_ */ diff --git a/platform/mcu/xr871/project/common/cmd/cmd_heap.c b/platform/mcu/xr871/project/common/cmd/cmd_heap.c new file mode 100644 index 0000000000..daa4f32c2a --- /dev/null +++ b/platform/mcu/xr871/project/common/cmd/cmd_heap.c @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "cmd_util.h" + +extern void heap_get_space(uint8_t **start, uint8_t **end, uint8_t **current); + +enum cmd_status cmd_heap_space_exec(char *cmd) +{ + uint8_t *start, *end, *current; + + heap_get_space(&start, &end, ¤t); + cmd_write_respond(CMD_STATUS_OK, "heap total %u, use %u, free %u, [%p, %p, %p)", + end - start, current - start, end - current, start, current, end); + return CMD_STATUS_ACKED; +} + +static struct cmd_data g_heap_cmds[] = { + { "space", cmd_heap_space_exec }, +}; + +enum cmd_status cmd_heap_exec(char *cmd) +{ + return cmd_exec(cmd, g_heap_cmds, cmd_nitems(g_heap_cmds)); +} diff --git a/platform/mcu/xr871/project/common/cmd/cmd_heap.h b/platform/mcu/xr871/project/common/cmd/cmd_heap.h new file mode 100644 index 0000000000..37dec35bff --- /dev/null +++ b/platform/mcu/xr871/project/common/cmd/cmd_heap.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _CMD_HEAP_H_ +#define _CMD_HEAP_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +enum cmd_status cmd_heap_exec(char *cmd); + +#ifdef __cplusplus +} +#endif + +#endif /* _CMD_HEAP_H_ */ diff --git a/platform/mcu/xr871/project/common/cmd/cmd_httpc.c b/platform/mcu/xr871/project/common/cmd/cmd_httpc.c new file mode 100644 index 0000000000..97928a9ed7 --- /dev/null +++ b/platform/mcu/xr871/project/common/cmd/cmd_httpc.c @@ -0,0 +1,509 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "cmd_util.h" +#include "net/HTTPClient/HTTPCUsr_api.h" +#include "net/mbedtls/mbedtls.h" + +static int HTTPC_checksum_check(void *url) +{ + if (strstr(url, "checksum") != NULL) + return 0; + return -1; +} +static char HTTPC_cal_checksum(void *buffer, int length) +{ + unsigned char *cal = (unsigned char *)buffer; + unsigned char result = 0; + while (length != 0) { + result += cal[--length]; + } + return result; +} + +static unsigned char checksum = 0; +static int checksum_flag = 0; +static int HTTPC_get_test(HTTPParameters *clientParams) +{ + int nRetCode = 0; + int recvSize = 0; + char *buf = NULL; + if ((buf = malloc(HTTP_CLIENT_BUFFER_SIZE)) == NULL) { + return -1; + } + do { + nRetCode = HTTPC_get(clientParams,buf, 4096,(INT32 *)&recvSize); + if (checksum_flag == 1) + checksum += HTTPC_cal_checksum(buf,recvSize); + + if (nRetCode != HTTP_CLIENT_SUCCESS) + break; + } while(1); + if (buf) { + free(buf); + buf = NULL; + } + if (nRetCode == HTTP_CLIENT_EOS) { + CMD_DBG("HTTP GET SUCCESS\n"); + nRetCode = HTTP_CLIENT_SUCCESS; + } + + return nRetCode; +} + +#ifdef HTTPC_CMD_REGISTER_USER_CALLBACK +security_client user_param; +extern const char mbedtls_test_cas_pem[]; +extern const size_t mbedtls_test_cas_pem_len; + +void* get_certs() +{ + memset(&user_param, 0, sizeof(user_param)); + user_param.pCa = (char *)mbedtls_test_cas_pem; + user_param.nCa = mbedtls_test_cas_pem_len; + return &user_param; +} +#endif + +static int HTTPC_get_test_fresh(HTTPParameters *clientParams) +{ + int ret = 0; + unsigned int toReadLength = 4096; + unsigned int Received = 0; + HTTP_CLIENT httpClient; + memset((void *)&httpClient, 0, sizeof(httpClient)); + char *buf = malloc(toReadLength); + if (buf == NULL) { + CMD_ERR("malloc pbuffer failed..\n"); + return -1; + } + + clientParams->HttpVerb = VerbGet; + +#ifdef HTTPC_CMD_REGISTER_USER_CALLBACK + HTTPC_Register_user_certs(get_certs); +#endif + + if (HTTPC_open(clientParams) != 0) { + CMD_ERR("http open err..\n"); + } else if (HTTPC_request(clientParams, NULL) != 0) { + CMD_ERR("http request err..\n"); + } else if (HTTPC_get_request_info(clientParams, &httpClient) != 0){ + CMD_ERR("http get request info err..\n"); + }else if (httpClient.TotalResponseBodyLength != 0) { + do { + if ((ret = HTTPC_read(clientParams, buf, toReadLength, (void *)&Received)) != 0) { + if (ret == 1000) { + CMD_DBG("The end..\n"); + } else + CMD_ERR("Transfer err...ret:%d\n",ret); + } else { + //CMD_DBG("get data :%d\n", Received); + } + + if (checksum_flag == 1) + checksum += HTTPC_cal_checksum(buf,Received); + + if (ret != 0) + break; + + } while (1); + + } + free(buf); + HTTPC_close(clientParams); + return ret; +} + +static int HTTPC_muti_get_test(HTTPParameters *clientParams,char *url0, char *url1) +{ + int ret = 0; + unsigned int toReadLength = 4096; + unsigned int Received = 0; + char *pUrl0 = NULL; + char *pUrl1 = NULL; + char *buf = malloc(toReadLength); + if (buf == NULL) { + CMD_ERR("malloc pbuffer failed..\n"); + return -1; + } + HTTP_CLIENT httpClient; + memset((void *)&httpClient, 0, sizeof(httpClient)); + clientParams->HttpVerb = VerbGet; + if ((pUrl0 = url0) == NULL) { + CMD_ERR("http request NULL url0..\n"); + ret = -1; + goto request_release; + } + if ((pUrl1 = url1) == NULL) { + CMD_ERR("http request NULL url..\n"); + ret = -1; + goto request_release; + } + strlcpy(clientParams->Uri, pUrl0,sizeof(clientParams->Uri)); + CMD_DBG("http test get url0..\n"); + if (HTTPC_open(clientParams) != 0) { + CMD_ERR("http open err..\n"); + ret = -1; + goto request_release; + } + pUrl0 = NULL; + goto direct_request; +set_url1: + HTTPC_reset_session(clientParams); + strlcpy(clientParams->Uri, pUrl1,sizeof(clientParams->Uri)); + CMD_DBG("http test get url1..\n"); + pUrl1 = NULL; +direct_request: + if (HTTPC_request(clientParams, NULL) != 0) { + CMD_ERR("http request err..\n"); + goto request_release; + } + if (HTTPC_get_request_info(clientParams, &httpClient) != 0){ + CMD_ERR("http get request info err..\n"); + }else if (httpClient.TotalResponseBodyLength != 0) { + do { + if ((ret = HTTPC_read(clientParams, buf, toReadLength, (void *)&Received)) != 0) { + if (ret == 1000) { + CMD_DBG("The end..\n"); + } else + CMD_ERR("Transfer err...ret:%d\n",ret); + } else { + //CMD_DBG("get data :%d\n", Received); + } + if (checksum_flag == 1) + checksum += HTTPC_cal_checksum(buf,Received); + if (ret != 0) + break; + } while (1); + + } + if (pUrl1 != NULL && checksum == 0xFF) { + checksum = 0; + goto set_url1; + } +request_release: + free(buf); + HTTPC_close(clientParams); + return ret; +} + +#if 0 +char *headers = "testkey0:testvalue0&testkey1:testvalue1"; +void* get_heads() +{ + return headers; +} +#endif +static int HTTPC_post_test(HTTPParameters *clientParams, char *credentials) +{ + int ret = 0; + unsigned int toReadLength = 4096; + unsigned int Received = 0; + char *buf = malloc(toReadLength); + if (buf == NULL) + CMD_ERR("malloc pbuffer failed..\n"); + HTTP_CLIENT httpClient; + memset((void *)&httpClient, 0, sizeof(httpClient)); + + memset(buf, 0, toReadLength); + clientParams->HttpVerb = VerbPost; + clientParams->pData = buf; + memcpy(buf, credentials, strlen(credentials)); + clientParams->pLength = strlen(credentials); +request: + if ((ret = HTTPC_open(clientParams)) != 0) { + CMD_ERR("http open err..\n"); + goto relese; + } + + if ((ret = HTTPC_request(clientParams, NULL)) != 0) { + CMD_ERR("http request err..\n"); + goto relese; + } + if ((ret = HTTPC_get_request_info(clientParams, &httpClient)) != 0) { + CMD_ERR("http get request info err..\n"); + goto relese; + } + if (httpClient.HTTPStatusCode != HTTP_STATUS_OK) { + if((httpClient.HTTPStatusCode == HTTP_STATUS_OBJECT_MOVED) || + (httpClient.HTTPStatusCode == HTTP_STATUS_OBJECT_MOVED_PERMANENTLY)) { + CMD_DBG("Redirect url..\n"); + HTTPC_close(clientParams); + memset(clientParams, 0, sizeof(*clientParams)); + clientParams->HttpVerb = VerbGet; + if (httpClient.RedirectUrl->nLength < sizeof(clientParams->Uri)) + strncpy(clientParams->Uri, httpClient.RedirectUrl->pParam, + httpClient.RedirectUrl->nLength); + else + goto relese; + CMD_DBG("go to request.\n"); + goto request; + + } else { + ret = -1; + CMD_DBG("get result not correct.\n"); + goto relese; + } + } + if (httpClient.TotalResponseBodyLength != 0 || (httpClient.HttpFlags & HTTP_CLIENT_FLAG_CHUNKED )) { + + do { + memset(buf, 0, 4096); + if ((ret = HTTPC_read(clientParams, buf, toReadLength, (void *)&Received)) != 0) { + //CMD_DBG("get data,Received:%d\n",Received); + if (ret == 1000) { + ret = 0; + CMD_DBG("The end..\n"); + } else + CMD_ERR("Transfer err...ret:%d\n",ret); + + if (checksum_flag == 1) + checksum += HTTPC_cal_checksum(buf,Received); + + break; + } else { + //CMD_DBG("get data,Received:%d\n",Received); + + } + if (checksum_flag == 1) + checksum += HTTPC_cal_checksum(buf,Received); + } while (1); + } +relese: + free(buf); + HTTPC_close(clientParams); + return ret; +} + +static int HTTPC_head_test(HTTPParameters *clientParams) +{ + return 0; +} +static int httpc_exec(char *cmd) +{ + //CMD_DBG("HTTPC TEST %s\n",cmd); + int ret = -1; + int argc = 0; + char *argv[6]; + + HTTPParameters *clientParams; + clientParams = malloc(sizeof(*clientParams)); + if (!clientParams) { + return -1; + } else{ + memset(clientParams,0,sizeof(HTTPParameters)); + } + + argc = cmd_parse_argv(cmd, argv, 6); + if (argc < 2) { + CMD_ERR("invalid httpc cmd, argc %d\n", argc); + ret = -1; + goto releaseParams; + } + + if (HTTPC_checksum_check(argv[1]) == 0) + checksum_flag = 1; + else + checksum_flag = 0; + checksum = 0; + + strcpy(clientParams->Uri,argv[1]); + if (cmd_strncmp(argv[0],"get",3) == 0) { + if ((ret = HTTPC_get_test(clientParams)) != 0) + goto releaseParams; + } else if (cmd_strncmp(argv[0], "post", 4) == 0) { + if (argc < 3) { + CMD_ERR("invalid httpc cmd(post), argc %d\n", argc); + ret = -1; + goto releaseParams; + } + if ((ret = HTTPC_post_test(clientParams, argv[2])) != 0) + goto releaseParams; + + } else if (cmd_strncmp(argv[0], "-get", 4) == 0) { + + if ((ret = HTTPC_get_test_fresh(clientParams)) != 0) + goto releaseParams; + + } else if (cmd_strncmp(argv[0], "head", 4) == 0) { + if ((ret = HTTPC_head_test(clientParams)) != 0) + goto releaseParams; + }else if (cmd_strncmp(argv[0], "auth-get", 8) == 0) { + // net httpc auth-get(0) url(1) test(id)(2) 12345678(key)(3) + if (argc < 4) { + CMD_ERR("invalid httpc cmd(auth-get), argc %d\n", argc); + ret = -1; + goto releaseParams; + } + cmd_memcpy(clientParams->UserName, argv[2], cmd_strlen(argv[2])); + cmd_memcpy(clientParams->Password, argv[3], cmd_strlen(argv[3])); + clientParams->AuthType = AuthSchemaDigest; + if ((ret = HTTPC_get_test_fresh(clientParams)) != 0) + goto releaseParams; + }else if (cmd_strncmp(argv[0], "ssl-get", 7) == 0) { + //net httpc ssl-get(0) url(1) + if ((ret = HTTPC_get_test_fresh(clientParams)) != 0) + goto releaseParams; + + }else if (cmd_strncmp(argv[0], "ssl-post", 8) == 0) { + //net httpc ssl-post(0) url(1) data(2) + if (argc < 3) { + CMD_ERR("invalid httpc cmd(ssl-post), argc %d\n", argc); + ret = -1; + goto releaseParams; + } + if ((ret = HTTPC_post_test(clientParams, argv[2])) != 0) + goto releaseParams; + }else if (cmd_strncmp(argv[0], "muti-get", 8) == 0) { + if (argc < 3) { + CMD_ERR("invalid httpc cmd(muti-get), argc %d\n", argc); + ret = -1; + goto releaseParams; + } + clientParams->Flags |= HTTP_CLIENT_FLAG_KEEP_ALIVE; + if ((ret = HTTPC_muti_get_test(clientParams,argv[1], argv[2])) != 0) + goto releaseParams; + }else if (cmd_strncmp(argv[0], "muti-post", 8) == 0) { + } + +releaseParams: + if (clientParams != NULL) { + free(clientParams); + clientParams = NULL; + } + if (checksum_flag == 1) { + printf("[httpc test]:checksum = %#x\n",checksum); + if (checksum != 0xff) + ret = -1; + else + ret = 0; + } + checksum_flag = 0; + checksum = 0; + return ret; +} + +#define HTTPC_THREAD_STACK_SIZE (4 * 1024) +static OS_Thread_t g_httpc_thread; + +void httpc_cmd_task(void *arg) +{ + char *cmd = (char *)arg; + CMD_LOG(1, " \n",cmd); + int ret = httpc_exec(cmd); + if (ret != 0 && ret != 1000) + CMD_LOG(1, " <%s>\n",cmd); + else { + CMD_LOG(1, " <%s>\n",cmd); + } + OS_ThreadDelete(&g_httpc_thread); +} + +enum cmd_status cmd_httpc_exec(char *cmd) +{ + if (OS_ThreadIsValid(&g_httpc_thread)) { + CMD_ERR("httpc task is running\n"); + return CMD_STATUS_FAIL; + } + if (OS_ThreadCreate(&g_httpc_thread, + "", + httpc_cmd_task, + (void *)cmd, + OS_THREAD_PRIO_APP, + HTTPC_THREAD_STACK_SIZE) != OS_OK) { + CMD_ERR("httpc task create failed\n"); + return CMD_STATUS_FAIL; + } + return CMD_STATUS_OK; +} + +#if 0 + if (cmd_strncmp(argv[0],"__post_m",8) == 0) { + + strcpy(ClientParams->Uri,argv[1]); + int conLength = 0; + unsigned int toReadLength = 4096; + unsigned int Received = 0; + char *pBuffer = malloc(toReadLength); + if (pBuffer == NULL) + printf("malloc pbuffer failed..\n"); + + memset(pBuffer, 0, toReadLength); + ClientParams->HttpVerb = VerbPost; + ClientParams->pData = pBuffer; + ClientParams->sData = client_credentials; + ClientParams->sLength = strlen(client_credentials); + + if (HTTPC_open(ClientParams) != 0) { + + printf("http open err..\n"); + + } + unsigned int times = 0x2; + while(times--) { + if (HTTPC_request(ClientParams,(void *)&conLength) != 0) { + printf("http request err..\n"); + + } else if (conLength != 0 || (((P_HTTP_SESSION)(ClientParams->pHTTP))->HttpFlags & HTTP_CLIENT_FLAG_CHUNKED )) { + + do { + int retValue = 0; + if ((retValue = HTTPC_read(ClientParams, pBuffer, toReadLength, (void *)&Received)) != 0) { + if (retValue == 1000) { + printf("The end..\n"); + } else + printf("Transfer err...retValue:%d\n",retValue); + break; + + + } else { + printf("get data :%d\n", Received); + } + + } while (1); + } + if (retValue != 1000 && retValue != 0) { + printf("httpc post err...\n"); + break; + + } else { + printf("*******httpc post success********\n"); + while(1){ + OS_Sleep(20); + } + ret = retValue; + } + OS_Sleep(2); + } + + free(pBuffer); + HTTPC_close(ClientParams); + } +#endif diff --git a/platform/mcu/xr871/project/common/cmd/cmd_httpc.h b/platform/mcu/xr871/project/common/cmd/cmd_httpc.h new file mode 100644 index 0000000000..8de0b728bb --- /dev/null +++ b/platform/mcu/xr871/project/common/cmd/cmd_httpc.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _CMD_HTTPC_H_ +#define _CMD_HTTPC_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +enum cmd_status cmd_httpc_exec(char *cmd); + +#ifdef __cplusplus +} +#endif + +#endif /* _CMD_HTTPC_H_ */ diff --git a/platform/mcu/xr871/project/common/cmd/cmd_httpd.c b/platform/mcu/xr871/project/common/cmd/cmd_httpd.c new file mode 100644 index 0000000000..1a21c28241 --- /dev/null +++ b/platform/mcu/xr871/project/common/cmd/cmd_httpd.c @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "cmd_util.h" +#include "cmd_httpd.h" + +static OS_Thread_t g_httpd_thread; +#define HTTPD_THREAD_STACK_SIZE (4 * 1024) +#define HTTPD_THREAD_EXIT OS_ThreadDelete + +extern int webserver_start(int argc, char *argv[]); +extern void webserver_stop(); + +void httpd_run(void *arg) +{ + webserver_start(0, NULL); + HTTPD_THREAD_EXIT(&g_httpd_thread); + +} + +int httpd_start() +{ + if (OS_ThreadIsValid(&g_httpd_thread)) { + CMD_ERR("HTTPD task is running\n"); + return -1; + } + + if (OS_ThreadCreate(&g_httpd_thread, + "", + httpd_run, + NULL, + OS_THREAD_PRIO_APP, + HTTPD_THREAD_STACK_SIZE) != OS_OK) { + CMD_ERR("httpd task create failed\n"); + return -1; + } + + return 0; +} +enum cmd_status cmd_httpd_exec(char *cmd) +{ + int argc; + char *argv[3]; + + argc = cmd_parse_argv(cmd, argv, 3); + if (argc < 1) { + CMD_ERR("invalid httpd cmd, argc %d\n", argc); + return CMD_STATUS_INVALID_ARG; + } + int enable = atoi(argv[0]); + if (enable == 1) + httpd_start(); + else + webserver_stop(); + + return CMD_STATUS_OK; +} diff --git a/platform/mcu/xr871/project/common/cmd/cmd_httpd.h b/platform/mcu/xr871/project/common/cmd/cmd_httpd.h new file mode 100644 index 0000000000..4177f59d08 --- /dev/null +++ b/platform/mcu/xr871/project/common/cmd/cmd_httpd.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _CMD_HTTPD_H_ +#define _CMD_HTTPD_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +enum cmd_status cmd_httpd_exec(char *cmd); + +#ifdef __cplusplus +} +#endif + +#endif /* _CMD_HTTPD_H_ */ diff --git a/platform/mcu/xr871/project/common/cmd/cmd_i2c.c b/platform/mcu/xr871/project/common/cmd/cmd_i2c.c new file mode 100644 index 0000000000..6c7c3ec2c5 --- /dev/null +++ b/platform/mcu/xr871/project/common/cmd/cmd_i2c.c @@ -0,0 +1,508 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "cmd_util.h" +#include "cmd_i2c.h" +#include "driver/chip/hal_gpio.h" +#include "driver/chip/hal_i2c.h" + + +#define DRV_TEST_I2C_NORM 0 +#define DRV_TEST_I2C_MEM 1 +#define DRV_TEST_I2C_SCCB 0 + +#define DRV_TEST_I2C_ADDR_10BIT 0 + +static enum cmd_status cmd_i2c_init_exec(char *cmd) +{ + int cnt; + uint32_t id, addr_mode, clock_freq; + I2C_InitParam i2c_param; + HAL_Status hal_status; + + cnt = cmd_sscanf(cmd, "i=%u a=%u c=%u", &id, &addr_mode, &clock_freq); + if (cnt != 3) { + CMD_ERR("cmd_sscanf return: cnt = %d\n", cnt); + return CMD_STATUS_INVALID_ARG; + } + + if (id >= I2C_NUM) { + CMD_ERR("invalid id %u\n", id); + return CMD_STATUS_INVALID_ARG; + } + +#if DRV_TEST_I2C_ADDR_10BIT + if ((addr_mode != 7) && (addr_mode != 10)) { +#else /* DRV_TEST_I2C_ADDR_10BIT */ + if (addr_mode != 7) { +#endif /* DRV_TEST_I2C_ADDR_10BIT */ + CMD_ERR("invalid addr mode %u\n", addr_mode); + return CMD_STATUS_INVALID_ARG; + } + + if ((clock_freq < 100000) || (clock_freq > 400000)) { + CMD_ERR("invalid clock freq %u\n", clock_freq); + return CMD_STATUS_INVALID_ARG; + } + +#if DRV_TEST_I2C_ADDR_10BIT + if (addr_mode == 10) + i2c_param.addrMode = I2C_ADDR_MODE_10BIT; + else +#endif /* DRV_TEST_I2C_ADDR_10BIT */ + i2c_param.addrMode = I2C_ADDR_MODE_7BIT; + i2c_param.clockFreq = clock_freq; + + hal_status = HAL_I2C_Init((I2C_ID)id, &i2c_param); + if (hal_status == HAL_OK) { + return CMD_STATUS_OK; + } else { + CMD_ERR("HAL_I2C_Init return: hal_status = %d\n", hal_status); + return CMD_STATUS_FAIL; + } +} + +static enum cmd_status cmd_i2c_deinit_exec(char *cmd) +{ + int cnt; + uint32_t id; + HAL_Status hal_status; + + cnt = cmd_sscanf(cmd, "i=%u", &id); + if (cnt != 1) { + CMD_ERR("cmd_sscanf return: cnt = %d\n", cnt); + return CMD_STATUS_INVALID_ARG; + } + + if (id >= I2C_NUM) { + CMD_ERR("invalid id %u\n", id); + return CMD_STATUS_INVALID_ARG; + } + + hal_status = HAL_I2C_DeInit((I2C_ID)id); + if (hal_status == HAL_OK) { + return CMD_STATUS_OK; + } else { + CMD_ERR("HAL_I2C_DeInit return: hal_status = %d\n", hal_status); + return CMD_STATUS_FAIL; + } +} + +#if DRV_TEST_I2C_NORM +static enum cmd_status cmd_i2c_transmit_norm_exec(char *cmd) +{ + int cnt; + uint32_t id, dev_addr, len; + uint8_t *buf; + int32_t size; + + cnt = cmd_sscanf(cmd, "i=%u d=%u l=%u", &id, &dev_addr, &len); + if (cnt != 3) { + CMD_ERR("cmd_sscanf return: cnt = %d\n", cnt); + return CMD_STATUS_INVALID_ARG; + } + + if (id >= I2C_NUM) { + CMD_ERR("invalid id %u\n", id); + return CMD_STATUS_INVALID_ARG; + } + +#if DRV_TEST_I2C_ADDR_10BIT + if ((dev_addr >> 10) != 0) { +#else /* DRV_TEST_I2C_ADDR_10BIT */ + if ((dev_addr >> 7) != 0) { +#endif /* DRV_TEST_I2C_ADDR_10BIT */ + CMD_ERR("invalid dev_addr %u\n", dev_addr); + return CMD_STATUS_INVALID_ARG; + } + + if ((len >> 31) != 0) { + CMD_ERR("invalid len %u\n", len); + return CMD_STATUS_INVALID_ARG; + } + + buf = (uint8_t *)cmd_malloc(len * sizeof(uint8_t)); + if (buf == NULL) { + CMD_ERR("cmd_malloc return NULL.\n"); + return CMD_STATUS_FAIL; + } + + cmd_write_respond(CMD_STATUS_OK, "OK"); + + cmd_raw_mode_enable(); + size = cmd_raw_mode_read(buf, (int32_t)len, 10000); + if (size != (int32_t)len) { + CMD_ERR("len = %u, but raw mode read size = %d\n", len, size); + cmd_free(buf); + cmd_raw_mode_write((uint8_t *)"FAIL", 4); + cmd_raw_mode_disable(); + return CMD_STATUS_ACKED; + } + + size = HAL_I2C_Master_Transmit_IT((I2C_ID)id, (uint16_t)dev_addr, buf, (int32_t)len); + if (size != (int32_t)len) { + CMD_ERR("len = %u, but I2C transmit size = %d\n", len, size); + cmd_free(buf); + cmd_raw_mode_write((uint8_t *)"FAIL", 4); + cmd_raw_mode_disable(); + return CMD_STATUS_ACKED; + } + + cmd_free(buf); + cmd_raw_mode_write((uint8_t *)"OK", 2); + cmd_raw_mode_disable(); + return CMD_STATUS_ACKED; +} + +static enum cmd_status cmd_i2c_receive_norm_exec(char *cmd) +{ + int cnt; + uint32_t id, dev_addr, len; + uint8_t *buf; + int32_t size; + + cnt = cmd_sscanf(cmd, "i=%u d=%u l=%u", &id, &dev_addr, &len); + if (cnt != 3) { + CMD_ERR("cmd_sscanf return: cnt = %d\n", cnt); + return CMD_STATUS_INVALID_ARG; + } + + if (id >= I2C_NUM) { + CMD_ERR("invalid id %u\n", id); + return CMD_STATUS_INVALID_ARG; + } + +#if DRV_TEST_I2C_ADDR_10BIT + if ((dev_addr >> 10) != 0) { +#else /* DRV_TEST_I2C_ADDR_10BIT */ + if ((dev_addr >> 7) != 0) { +#endif /* DRV_TEST_I2C_ADDR_10BIT */ + CMD_ERR("invalid dev_addr %u\n", dev_addr); + return CMD_STATUS_INVALID_ARG; + } + + if ((len >> 31) != 0) { + CMD_ERR("invalid len %u\n", len); + return CMD_STATUS_INVALID_ARG; + } + + buf = (uint8_t *)cmd_malloc(len * sizeof(uint8_t)); + if (buf == NULL) { + CMD_ERR("cmd_malloc return NULL.\n"); + return CMD_STATUS_FAIL; + } + cmd_memset(buf, 0, len); + + size = HAL_I2C_Master_Receive_IT((I2C_ID)id, (uint16_t)dev_addr, buf, (int32_t)len); + if (size != (int32_t)len) { + CMD_ERR("len = %u, but I2C receive size = %d\n", len, size); + cmd_free(buf); + return CMD_STATUS_FAIL; + } + + cmd_write_respond(CMD_STATUS_OK, "OK"); + + cmd_msleep(1000); + + cmd_raw_mode_enable(); + size = cmd_raw_mode_write(buf, (int32_t)len); + if (size != (int32_t)len) { + CMD_ERR("len = %u, but raw mode write size = %d\n", len, size); + } + + cmd_free(buf); + cmd_raw_mode_disable(); + return CMD_STATUS_ACKED; +} +#endif /* DRV_TEST_I2C_NORM */ + +#if DRV_TEST_I2C_MEM +static enum cmd_status cmd_i2c_transmit_mem_exec(char *cmd) +{ + int cnt; + uint32_t id, dev_addr, mem_addr, len; + uint8_t *buf; + int32_t size; + + cnt = cmd_sscanf(cmd, "i=%u d=%u m=%u l=%u", &id, &dev_addr, &mem_addr, &len); + if (cnt != 4) { + CMD_ERR("cmd_sscanf return: cnt = %d\n", cnt); + return CMD_STATUS_INVALID_ARG; + } + + if (id >= I2C_NUM) { + CMD_ERR("invalid id %u\n", id); + return CMD_STATUS_INVALID_ARG; + } + + if ((mem_addr >> 8) != 0) { + CMD_ERR("invalid mem_addr %u\n", mem_addr); + return CMD_STATUS_INVALID_ARG; + } + + if (len > 16) { + CMD_ERR("invalid len %u\n", len); + return CMD_STATUS_INVALID_ARG; + } + + buf = (uint8_t *)cmd_malloc(len * sizeof(uint8_t)); + if (buf == NULL) { + CMD_ERR("cmd_malloc return NULL.\n"); + return CMD_STATUS_FAIL; + } + + cmd_write_respond(CMD_STATUS_OK, "OK"); + + cmd_raw_mode_enable(); + size = cmd_raw_mode_read(buf, (int32_t)len, 10000); + if (size != (int32_t)len) { + CMD_ERR("len = %u, but raw mode read size = %d\n", len, size); + cmd_free(buf); + cmd_raw_mode_write((uint8_t *)"FAIL", 4); + cmd_raw_mode_disable(); + return CMD_STATUS_ACKED; + } + + size = HAL_I2C_Master_Transmit_Mem_IT((I2C_ID)id, (uint16_t)dev_addr, (uint8_t)mem_addr, buf, (int32_t)len); + if (size != (int32_t)len) { + CMD_ERR("len = %u, but I2C transmit size = %d\n", len, size); + cmd_free(buf); + cmd_raw_mode_write((uint8_t *)"FAIL", 4); + cmd_raw_mode_disable(); + return CMD_STATUS_ACKED; + } + + cmd_free(buf); + cmd_raw_mode_write((uint8_t *)"OK", 2); + cmd_raw_mode_disable(); + return CMD_STATUS_ACKED; +} + +static enum cmd_status cmd_i2c_receive_mem_exec(char *cmd) +{ + int cnt; + uint32_t id, dev_addr, mem_addr, len; + uint8_t *buf; + int32_t size; + + cnt = cmd_sscanf(cmd, "i=%u d=%u m=%u l=%u", &id, &dev_addr, &mem_addr, &len); + if (cnt != 4) { + CMD_ERR("cmd_sscanf return: cnt = %d\n", cnt); + return CMD_STATUS_INVALID_ARG; + } + + if (id >= I2C_NUM) { + CMD_ERR("invalid id %u\n", id); + return CMD_STATUS_INVALID_ARG; + } + + if ((mem_addr >> 8) != 0) { + CMD_ERR("invalid mem_addr %u\n", mem_addr); + return CMD_STATUS_INVALID_ARG; + } + + if (len > 2048) { + CMD_ERR("invalid len %u\n", len); + return CMD_STATUS_INVALID_ARG; + } + + buf = (uint8_t *)cmd_malloc(len * sizeof(uint8_t)); + if (buf == NULL) { + CMD_ERR("cmd_malloc return NULL.\n"); + return CMD_STATUS_FAIL; + } + cmd_memset(buf, 0, len); + + size = HAL_I2C_Master_Receive_Mem_IT((I2C_ID)id, (uint16_t)dev_addr, (uint8_t)mem_addr, buf, (int32_t)len); + if (size != (int32_t)len) { + CMD_ERR("len = %u, but I2C receive size = %d\n", len, size); + cmd_free(buf); + return CMD_STATUS_FAIL; + } + + cmd_write_respond(CMD_STATUS_OK, "OK"); + + cmd_msleep(1000); + + cmd_raw_mode_enable(); + size = cmd_raw_mode_write(buf, (int32_t)len); + if (size != (int32_t)len) { + CMD_ERR("len = %u, but raw mode write size = %d\n", len, size); + } + + cmd_free(buf); + cmd_raw_mode_disable(); + return CMD_STATUS_ACKED; +} +#endif /* DRV_TEST_I2C_MEM */ + +#if DRV_TEST_I2C_SCCB +static enum cmd_status cmd_i2c_transmit_sccb_exec(char *cmd) +{ + int cnt; + uint32_t id, dev_addr, sub_addr; + uint8_t *buf; + int32_t size; + + cnt = cmd_sscanf(cmd, "i=%u d=%u s=%u", &id, &dev_addr, &sub_addr); + if (cnt != 3) { + CMD_ERR("cmd_sscanf return: cnt = %d\n", cnt); + return CMD_STATUS_INVALID_ARG; + } + + if (id >= I2C_NUM) { + CMD_ERR("invalid id %u\n", id); + return CMD_STATUS_INVALID_ARG; + } + + if ((dev_addr >> 8) != 0) { + CMD_ERR("invalid dev_addr %u\n", dev_addr); + return CMD_STATUS_INVALID_ARG; + } + + if ((sub_addr >> 8) != 0) { + CMD_ERR("invalid sub_addr %u\n", sub_addr); + return CMD_STATUS_INVALID_ARG; + } + + buf = (uint8_t *)cmd_malloc(sizeof(uint8_t)); + if (buf == NULL) { + CMD_ERR("cmd_malloc return NULL.\n"); + return CMD_STATUS_FAIL; + } + + cmd_write_respond(CMD_STATUS_OK, "OK"); + + cmd_raw_mode_enable(); + size = cmd_raw_mode_read(buf, 1, 10000); + if (size != 1) { + CMD_ERR("len = 1, but raw mode read size = %d\n", size); + cmd_free(buf); + cmd_raw_mode_write((uint8_t *)"FAIL", 4); + cmd_raw_mode_disable(); + return CMD_STATUS_ACKED; + } + + size = HAL_I2C_SCCB_Master_Transmit_IT((I2C_ID)id, (uint8_t)dev_addr, (uint8_t)sub_addr, buf); + if (size != 1) { + CMD_ERR("len = 1, but I2C transmit size = %d\n", size); + cmd_free(buf); + cmd_raw_mode_write((uint8_t *)"FAIL", 4); + cmd_raw_mode_disable(); + return CMD_STATUS_ACKED; + } + + cmd_free(buf); + cmd_raw_mode_write((uint8_t *)"OK", 2); + cmd_raw_mode_disable(); + return CMD_STATUS_ACKED; +} + +static enum cmd_status cmd_i2c_receive_sccb_exec(char *cmd) +{ + int cnt; + uint32_t id, dev_addr, sub_addr; + uint8_t *buf; + int32_t size; + + cnt = cmd_sscanf(cmd, "i=%u d=%u s=%u", &id, &dev_addr, &sub_addr); + if (cnt != 3) { + CMD_ERR("cmd_sscanf return: cnt = %d\n", cnt); + return CMD_STATUS_INVALID_ARG; + } + + if (id >= I2C_NUM) { + CMD_ERR("invalid id %u\n", id); + return CMD_STATUS_INVALID_ARG; + } + + if ((dev_addr >> 8) != 0) { + CMD_ERR("invalid dev_addr %u\n", dev_addr); + return CMD_STATUS_INVALID_ARG; + } + + if ((sub_addr >> 8) != 0) { + CMD_ERR("invalid sub_addr %u\n", sub_addr); + return CMD_STATUS_INVALID_ARG; + } + + buf = (uint8_t *)cmd_malloc(sizeof(uint8_t)); + if (buf == NULL) { + CMD_ERR("cmd_malloc return NULL.\n"); + return CMD_STATUS_FAIL; + } + cmd_memset(buf, 0, 1); + + size = HAL_I2C_SCCB_Master_Receive_IT((I2C_ID)id, (uint8_t)dev_addr, (uint8_t)sub_addr, buf); + if (size != 1) { + CMD_ERR("len = 1, but I2C receive size = %d\n", size); + cmd_free(buf); + return CMD_STATUS_FAIL; + } + + cmd_write_respond(CMD_STATUS_OK, "OK"); + + cmd_msleep(1000); + + cmd_raw_mode_enable(); + size = cmd_raw_mode_write(buf, 1); + if (size != 1) { + CMD_ERR("len = 1, but raw mode write size = %d\n", size); + } + + cmd_free(buf); + cmd_raw_mode_disable(); + return CMD_STATUS_ACKED; +} +#endif /* DRV_TEST_I2C_SCCB */ + +static struct cmd_data g_i2c_cmds[] = { + { "init", cmd_i2c_init_exec }, + { "deinit", cmd_i2c_deinit_exec }, + +#if DRV_TEST_I2C_NORM + { "transmit-norm", cmd_i2c_transmit_norm_exec }, + { "receive-norm", cmd_i2c_receive_norm_exec }, +#endif /* DRV_TEST_I2C_NORM */ + +#if DRV_TEST_I2C_MEM + { "transmit-mem", cmd_i2c_transmit_mem_exec }, + { "receive-mem", cmd_i2c_receive_mem_exec }, +#endif /* DRV_TEST_I2C_MEM */ + +#if DRV_TEST_I2C_SCCB + { "transmit-sccb", cmd_i2c_transmit_sccb_exec }, + { "receive-sccb", cmd_i2c_receive_sccb_exec }, +#endif /* DRV_TEST_I2C_SCCB */ +}; + +enum cmd_status cmd_i2c_exec(char *cmd) +{ + return cmd_exec(cmd, g_i2c_cmds, cmd_nitems(g_i2c_cmds)); +} diff --git a/platform/mcu/xr871/project/common/cmd/cmd_i2c.h b/platform/mcu/xr871/project/common/cmd/cmd_i2c.h new file mode 100644 index 0000000000..6342a5473d --- /dev/null +++ b/platform/mcu/xr871/project/common/cmd/cmd_i2c.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _CMD_I2C_H_ +#define _CMD_I2C_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +enum cmd_status cmd_i2c_exec(char *cmd); + +#ifdef __cplusplus +} +#endif + +#endif /* _CMD_I2C_H_ */ diff --git a/platform/mcu/xr871/project/common/cmd/cmd_ifconfig.c b/platform/mcu/xr871/project/common/cmd/cmd_ifconfig.c new file mode 100644 index 0000000000..71da0165f1 --- /dev/null +++ b/platform/mcu/xr871/project/common/cmd/cmd_ifconfig.c @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "lwip/inet.h" + +#include "cmd_util.h" + +#include "common/framework/net_ctrl.h" + +enum cmd_status cmd_ifconfig_exec(char *cmd) +{ + struct netif *nif = g_wlan_netif; + + if (nif == NULL) { + return CMD_STATUS_FAIL; + } + + if (cmd_strcmp(cmd, "status") == 0) { + if (netif_is_up(nif) && netif_is_link_up(nif)) { + char address[16]; + char gateway[16]; + char netmask[16]; + inet_ntoa_r(nif->ip_addr, address, sizeof(address)); + inet_ntoa_r(nif->gw, gateway, sizeof(gateway)); + inet_ntoa_r(nif->netmask, netmask, sizeof(netmask)); + cmd_write_respond(CMD_STATUS_OK, "%c%c%d up, " + "address:%s gateway:%s netmask:%s", + nif->name[0], nif->name[1], nif->num, + address, gateway, netmask); + } else { + cmd_write_respond(CMD_STATUS_OK, "%c%c%d down", + nif->name[0], nif->name[1], nif->num); + } + return CMD_STATUS_ACKED; + } else if (cmd_strcmp(cmd, "up") == 0) { + net_config(nif, 1); + } else if (cmd_strcmp(cmd, "down") == 0) { + net_config(nif, 0); + } else { + CMD_ERR("invalid argument '%s'\n", cmd); + return CMD_STATUS_INVALID_ARG; + } + + return CMD_STATUS_OK; +} diff --git a/platform/mcu/xr871/project/common/cmd/cmd_ifconfig.h b/platform/mcu/xr871/project/common/cmd/cmd_ifconfig.h new file mode 100644 index 0000000000..2dd1ca95ae --- /dev/null +++ b/platform/mcu/xr871/project/common/cmd/cmd_ifconfig.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _CMD_IFCONFIG_H_ +#define _CMD_IFCONFIG_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +enum cmd_status cmd_ifconfig_exec(char *cmd); + +#ifdef __cplusplus +} +#endif + +#endif /* _CMD_IFCONFIG_H_ */ diff --git a/platform/mcu/xr871/project/common/cmd/cmd_iperf.c b/platform/mcu/xr871/project/common/cmd/cmd_iperf.c new file mode 100644 index 0000000000..c6b7d7201a --- /dev/null +++ b/platform/mcu/xr871/project/common/cmd/cmd_iperf.c @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "cmd_util.h" + +#include "common/iperf/iperf.h" +#include "common/framework/net_ctrl.h" + +static const char *iperf_mode_str[IPERF_MODE_NUM] = { + "udp-send", + "udp-recv", + "tcp-send", + "tcp-recv", +}; /* index by enum IPERF_MODE */ +#define IPERF_STOP_SIGNAL "stop" +enum cmd_status cmd_iperf_exec(char *cmd) +{ + int argc, i; + char *argv[5]; + struct iperf_data idata; + cmd_memset(&idata, 0, sizeof(idata)); + argc = cmd_parse_argv(cmd, argv, 5); + if (cmd_strcmp(argv[0], IPERF_STOP_SIGNAL) == 0) { + if (iperf_stop(NULL) == 0) + return CMD_STATUS_OK; + else + return CMD_STATUS_FAIL; + } + if (argc < 3) { + CMD_ERR("invalid iperf cmd, argc %d\n", argc); + return CMD_STATUS_INVALID_ARG; + } + + for (i = 0; i < IPERF_MODE_NUM; ++i) { + if (cmd_strcmp(argv[0], iperf_mode_str[i]) == 0) { + idata.mode = (enum IPERF_MODE)i; + break; + } + } + + if (i >= IPERF_MODE_NUM) { + CMD_ERR("invalid iperf mode '%s'\n", argv[0]); + return CMD_STATUS_INVALID_ARG; + } + + cmd_strlcpy(idata.remote_ip, argv[1], 16); + idata.run_time = cmd_atoi(argv[2]); + if (argc == 4) + idata.port = cmd_atoi(argv[3]); + + if (iperf_start(g_wlan_netif, &idata) == 0) + return CMD_STATUS_OK; + else + return CMD_STATUS_FAIL; +} diff --git a/platform/mcu/xr871/project/common/cmd/cmd_iperf.h b/platform/mcu/xr871/project/common/cmd/cmd_iperf.h new file mode 100644 index 0000000000..23809bc6ec --- /dev/null +++ b/platform/mcu/xr871/project/common/cmd/cmd_iperf.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _CMD_IPERF_H_ +#define _CMD_IPERF_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +enum cmd_status cmd_iperf_exec(char *cmd); + +#ifdef __cplusplus +} +#endif + +#endif /* _CMD_IPERF_H_ */ diff --git a/platform/mcu/xr871/project/common/cmd/cmd_irrx.c b/platform/mcu/xr871/project/common/cmd/cmd_irrx.c new file mode 100644 index 0000000000..9ae2018dc2 --- /dev/null +++ b/platform/mcu/xr871/project/common/cmd/cmd_irrx.c @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "cmd_util.h" +#include "cmd_irrx.h" +#include "driver/chip/hal_irrx.h" + +enum cmd_irrx_action { + CMD_IRRX_ACTION_DECONFIG, + CMD_IRRX_ACTION_VALUE, +}; + +static uint32_t irrx_addr, irrx_key; +static IRRX_HandleTypeDef *irrx; + +static void irrx_rxcplt_callback(uint32_t addr, uint32_t key) +{ + irrx_addr = addr; + irrx_key = key; + + printf("received ir code addr:0x%02x key:0x%02x\n", addr, key); +} + +/* + * drv irrx config p= + */ +static enum cmd_status cmd_irrx_config_exec(char *cmd) +{ + IRRX_InitTypeDef irrx_param; + int32_t cnt; + char PulsePolarity[8]; + + cnt = cmd_sscanf(cmd, "p=%6s", PulsePolarity); + if (cnt != 1) { + return CMD_STATUS_INVALID_ARG; + } + + if (cmd_strcmp(PulsePolarity, "none") == 0) { + irrx_param.PulsePolariyInvert = IRRX_RPPI_NONE; + } else if (cmd_strcmp(PulsePolarity, "invert") == 0) { + irrx_param.PulsePolariyInvert = IRRX_RPPI_INVERT; + } else { + CMD_ERR("invalid PulsePolarity:%s\n", PulsePolarity); + return CMD_STATUS_INVALID_ARG; + } + + irrx_param.rxCpltCallback = &irrx_rxcplt_callback; + + irrx = HAL_IRRX_Init(&irrx_param); + + return CMD_STATUS_OK; +} + +/* + * drv irrx + */ +static enum cmd_status cmd_irrx_action_exec(char *cmd, enum cmd_irrx_action action) +{ + enum cmd_status status; + + status = CMD_STATUS_OK; + switch (action) { + case CMD_IRRX_ACTION_DECONFIG: + HAL_IRRX_DeInit(irrx); + break; + case CMD_IRRX_ACTION_VALUE: + cmd_write_respond(CMD_STATUS_OK, "addr=0x%02x key=0x%02x", irrx_addr, irrx_key); + status = CMD_STATUS_ACKED; + break; + default: + status = CMD_STATUS_INVALID_ARG; + break; + } + return status; +} + +/* + * drv irrx deconfig + */ +static enum cmd_status cmd_irrx_deconfig_exec(char *cmd) +{ + return cmd_irrx_action_exec(cmd, CMD_IRRX_ACTION_DECONFIG); +} + +/* + * drv irrx value + */ +static enum cmd_status cmd_irrx_value_exec(char *cmd) +{ + return cmd_irrx_action_exec(cmd, CMD_IRRX_ACTION_VALUE); +} + +static struct cmd_data g_irrx_cmds[] = { + { "config", cmd_irrx_config_exec }, + { "deconfig", cmd_irrx_deconfig_exec }, + { "value", cmd_irrx_value_exec }, +}; + +enum cmd_status cmd_irrx_exec(char *cmd) +{ + return cmd_exec(cmd, g_irrx_cmds, cmd_nitems(g_irrx_cmds)); +} diff --git a/platform/mcu/xr871/project/common/cmd/cmd_irrx.h b/platform/mcu/xr871/project/common/cmd/cmd_irrx.h new file mode 100644 index 0000000000..9c711f64bf --- /dev/null +++ b/platform/mcu/xr871/project/common/cmd/cmd_irrx.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _CMD_IRRX_H_ +#define _CMD_IRRX_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/* example: + * 1. init irrx use: drv irrx config p=invert + * use "p=none" when tx rx gpio direct connect in test mode. + * 2. get received addr and key use: drv irrx value 0 + * 3. deinit irrx use: drv irrx deconfig 0 + */ + +enum cmd_status cmd_irrx_exec(char *cmd); + +#ifdef __cplusplus +} +#endif + +#endif /* _CMD_IRRX_H_ */ diff --git a/platform/mcu/xr871/project/common/cmd/cmd_irtx.c b/platform/mcu/xr871/project/common/cmd/cmd_irtx.c new file mode 100644 index 0000000000..3380b0957b --- /dev/null +++ b/platform/mcu/xr871/project/common/cmd/cmd_irtx.c @@ -0,0 +1,184 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "cmd_util.h" +#include "cmd_irtx.h" +#include "driver/chip/hal_clock.h" +#include "driver/chip/hal_irtx.h" +#include "driver/chip/ir_nec.h" + +enum cmd_irtx_action { + CMD_IRTX_ACTION_DECONFIG, + CMD_IRTX_ACTION_SEND, +}; + +static IRTX_HandleTypeDef *irtx; + +/* + * drv irtx config p= d= t= + * c= m= r= + */ +static enum cmd_status cmd_irtx_config_exec(char *cmd) +{ + char PulsePolarity[8], ModulateDutyLevel[4], SendModeType[8], Modulation[4], Protocol[8]; + int32_t cnt; + int32_t CyclicalCnt; + IRTX_InitTypeDef irtx_param; + + cnt = cmd_sscanf(cmd, "p=%6s d=%2s t=%5s c=%u m=%3s r=%3s", + PulsePolarity, ModulateDutyLevel, SendModeType, &CyclicalCnt, Modulation, Protocol); + if (cnt != 6) { + return CMD_STATUS_INVALID_ARG; + } + + if (cmd_strcmp(PulsePolarity, "none") == 0) { + irtx_param.PulsePolarity = IRTX_TPPI_NONE; + } else if (cmd_strcmp(PulsePolarity, "invert") == 0) { + irtx_param.PulsePolarity = IRTX_TPPI_INVERT; + } else { + CMD_ERR("invalid PulsePolarity:%s\n", PulsePolarity); + return CMD_STATUS_INVALID_ARG; + } + + if (cmd_strcmp(ModulateDutyLevel, "t1") == 0) { + irtx_param.ModulateDutyLevel = IRTX_DRMC_TIME_1; + } else if (cmd_strcmp(ModulateDutyLevel, "t2") == 0) { + irtx_param.ModulateDutyLevel = IRTX_DRMC_TIME_2; + } else if (cmd_strcmp(ModulateDutyLevel, "t3") == 0) { + irtx_param.ModulateDutyLevel = IRTX_DRMC_TIME_3; + } else { + CMD_ERR("invalid ModulateDutyLevel:%s\n", ModulateDutyLevel); + return CMD_STATUS_INVALID_ARG; + } + + if (cmd_strcmp(SendModeType, "none") == 0) { /* IRTX_TTS_NONE for signal mode */ + irtx_param.SendModeType = IRTX_TTS_NONE; + } else if (cmd_strcmp(SendModeType, "cycle") == 0) { /* IRTX_TTS_CYCLICAL for cycle mode */ + irtx_param.SendModeType = IRTX_TTS_CYCLICAL; + } else { + CMD_ERR("invalid SendModeType:%s\n", SendModeType); + return CMD_STATUS_INVALID_ARG; + } + + + if (cmd_strcmp(Modulation, "dis") == 0) { /* IRTX_TTS_NONE for signal mode */ + irtx_param.InternalModulation = IRTX_IMS_DISABLE; + } else if (cmd_strcmp(Modulation, "en") == 0) { /* IRTX_TTS_CYCLICAL for cycle mode */ + irtx_param.InternalModulation = IRTX_IMS_ENABLE; + } else { + CMD_ERR("invalid InternalModulation:%s\n", Modulation); + return CMD_STATUS_INVALID_ARG; + } + + irtx_param.CyclicalCnt = CyclicalCnt; /* send CyclicalCnt times if cycle mode */ + + if (CyclicalCnt > 20) { + CMD_ERR("invalid CyclicalCnt:%d\n", CyclicalCnt); + return CMD_STATUS_INVALID_ARG; + } + if (cmd_strcmp(Protocol, "nec") == 0) { +#if defined (IR_CLK_32K_USED) + irtx_param.IdleDurationCnt = IRTX_32K_NEC_IDC_VALUE; +#else + uint32_t clk = HAL_GetHFClock(); + + if (clk == HOSC_CLOCK_26M) { + irtx_param.IdleDurationCnt = IRTX_26M_NEC_IDC_VALUE; + } else if (clk == HOSC_CLOCK_24M) { + irtx_param.IdleDurationCnt = IRTX_24M_NEC_IDC_VALUE; + } else { + CMD_ERR("%s unknow clk type(%d)!\n", __func__, clk); + } +#endif + } else { + CMD_ERR("invalid protocol:%s\n", Protocol); + return CMD_STATUS_INVALID_ARG; + } + + irtx = HAL_IRTX_Init(&irtx_param); + + return CMD_STATUS_OK; +} + +/* + * drv irtx + */ +static enum cmd_status cmd_irtx_action_exec(char *cmd, enum cmd_irtx_action action) +{ + enum cmd_status status; + + status = CMD_STATUS_OK; + switch (action) { + case CMD_IRTX_ACTION_DECONFIG: + HAL_IRTX_DeInit(irtx); + break; + case CMD_IRTX_ACTION_SEND: { + int32_t addr, key; + int32_t cnt; + + cnt = cmd_sscanf(cmd, "a=0x%x k=0x%x", &addr, &key); + if (cnt != 2) { + return CMD_STATUS_INVALID_ARG; + } + HAL_IRTX_Transmit(irtx, IRTX_NEC_PROTO, IR_NEC_CODE(addr, key)); + break; + } + default: + status = CMD_STATUS_INVALID_ARG; + break; + } + return status; +} + +/* + * drv irtx deconfig + */ +static enum cmd_status cmd_irtx_deconfig_exec(char *cmd) +{ + return cmd_irtx_action_exec(cmd, CMD_IRTX_ACTION_DECONFIG); +} + +/* + * drv irtx send + */ +static enum cmd_status cmd_irtx_send_exec(char *cmd) +{ + return cmd_irtx_action_exec(cmd, CMD_IRTX_ACTION_SEND); +} + +static struct cmd_data g_irtx_cmds[] = { + { "config", cmd_irtx_config_exec }, + { "deconfig", cmd_irtx_deconfig_exec }, + { "send", cmd_irtx_send_exec }, +}; + +enum cmd_status cmd_irtx_exec(char *cmd) +{ + return cmd_exec(cmd, g_irtx_cmds, cmd_nitems(g_irtx_cmds)); +} diff --git a/platform/mcu/xr871/project/common/cmd/cmd_irtx.h b/platform/mcu/xr871/project/common/cmd/cmd_irtx.h new file mode 100644 index 0000000000..c77725beb3 --- /dev/null +++ b/platform/mcu/xr871/project/common/cmd/cmd_irtx.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _CMD_IRTX_H_ +#define _CMD_IRTX_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/* example: + * 1. init irtx mode use + * single: drv irtx config p=none d=t1 t=none c=0 m=en r=nec + * cycle : drv irtx config p=none d=t1 t=cycle c=3 m=en r=nec + * use "m=dis" when tx rx gpio direct connect in test mode. + * 2. send addr and key use: drv irtx send a=0x0 k=0x18 + * 3. deinit irtx use: drv irtx deconfig 0 + */ + +enum cmd_status cmd_irtx_exec(char *cmd); + +#ifdef __cplusplus +} +#endif + +#endif /* _CMD_IRTX_H_ */ diff --git a/platform/mcu/xr871/project/common/cmd/cmd_mem.c b/platform/mcu/xr871/project/common/cmd/cmd_mem.c new file mode 100644 index 0000000000..be4b18124c --- /dev/null +++ b/platform/mcu/xr871/project/common/cmd/cmd_mem.c @@ -0,0 +1,226 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "cmd_util.h" + +/* + * $ mem r8 + * $ mem r16 + * $ mem r32 + * $ mem w8 + * $ mem w16 + * $ mem w32 + */ + +#define MEM_LOG(fmt, arg...) printf(fmt, ##arg) + +enum mem_rw_mode { + MEM_RW_MODE_8BIT = 0, + MEM_RW_MODE_16BIT, + MEM_RW_MODE_32BIT, + MEM_RW_MODE_NUM +}; + +struct mem_dump_param { + const char *hint_fmt; + const char *data_fmt; + uint8_t byte_align; +}; + +#define MEM_DUMP_BYTES_PER_LINE 16 + +static const struct mem_dump_param g_mem_dump_param[MEM_RW_MODE_NUM] = { +// "12345678: 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F" + { "address 0 1 2 3 4 5 6 7 8 9 A B C D E F", " %02x", 1 }, + +// "12345678: 0100 0302 0504 0706 0908 0B0A 0D0C 0F0E" + { "address 0 2 4 6 8 A C E", " %04x", 2 }, + +// "12345678: 03020100 07060504 0B0A0908 0F0E0D0C" + { "address 0 4 8 C", " %08x", 4 } +}; + +static int mem_dump(uint32_t addr, uint32_t len, enum mem_rw_mode mode) +{ + uint32_t i, byte_align, item_cnt, item_per_line; + const char *data_fmt; + const struct mem_dump_param *param; + + if (mode >= MEM_RW_MODE_NUM) { + MEM_LOG("invalid mem rw mode %d\n", mode); + return -1; + } + + param = &g_mem_dump_param[mode]; + byte_align = param->byte_align; + if ((addr & (byte_align - 1)) != 0) { + MEM_LOG("addr 0x%08x not align with %u\n", addr, byte_align); + return -1; + } + + item_cnt = len / byte_align; + item_per_line = MEM_DUMP_BYTES_PER_LINE / byte_align; + data_fmt = param->data_fmt; + + MEM_LOG(param->hint_fmt); + switch (mode) { + case MEM_RW_MODE_8BIT: + for (i = 0; i < item_cnt; ++i) { + if (i % item_per_line == 0) { + MEM_LOG("\n%08x:", addr); + } + MEM_LOG(data_fmt, *((uint8_t *)addr)); + addr += byte_align; + } + break; + case MEM_RW_MODE_16BIT: + for (i = 0; i < item_cnt; ++i) { + if (i % item_per_line == 0) { + MEM_LOG("\n%08x:", addr); + } + MEM_LOG(data_fmt, *((uint16_t *)addr)); + addr += byte_align; + } + break; + case MEM_RW_MODE_32BIT: + for (i = 0; i < item_cnt; ++i) { + if (i % item_per_line == 0) { + MEM_LOG("\n%08x:", addr); + } + MEM_LOG(data_fmt, *((uint32_t *)addr)); + addr += byte_align; + } + break; + default: + break; + } + MEM_LOG("\n"); + + return 0; +} + +static enum mem_rw_mode cmd_mem_parse_arg(char *cmd, uint32_t *addr, uint32_t *val) +{ + int argc; + char *argv[3]; + uint32_t bits; + enum mem_rw_mode mode; + + argc = cmd_parse_argv(cmd, argv, cmd_nitems(argv)); + if (argc != 3) { + return MEM_RW_MODE_NUM; + } + + if (cmd_sscanf(argv[0], "%u", &bits) != 1) { + return MEM_RW_MODE_NUM; + } + + switch (bits) { + case 8: + mode = MEM_RW_MODE_8BIT; + break; + case 16: + mode = MEM_RW_MODE_16BIT; + break; + case 32: + mode = MEM_RW_MODE_32BIT; + break; + default: + return MEM_RW_MODE_NUM; + } + + if (cmd_sscanf(argv[1], "%x", addr) != 1) { + return MEM_RW_MODE_NUM; + } + + if (cmd_strncasecmp(argv[2], "0x", 2) == 0) { + if (cmd_sscanf(argv[2], "%x", val) != 1) { + return MEM_RW_MODE_NUM; + } + } else { + if (cmd_sscanf(argv[2], "%u", val) != 1) { + return MEM_RW_MODE_NUM; + } + } + return mode; +} + +static enum cmd_status cmd_mem_read_exec(char *cmd) +{ + uint32_t addr, len; + enum mem_rw_mode mode; + + mode = cmd_mem_parse_arg(cmd, &addr, &len); + if (mode >= MEM_RW_MODE_NUM) { + return CMD_STATUS_INVALID_ARG; + } + + mem_dump(addr, len, mode); + return CMD_STATUS_ACKED; +} + +static enum cmd_status cmd_mem_write_exec(char *cmd) +{ + uint32_t addr, value; + enum mem_rw_mode mode; + + mode = cmd_mem_parse_arg(cmd, &addr, &value); + if (mode >= MEM_RW_MODE_NUM) { + return CMD_STATUS_INVALID_ARG; + } + + switch (mode) { + case MEM_RW_MODE_8BIT: + *((volatile uint8_t *)addr) = (uint8_t)value; + MEM_LOG("write 0x%02x -> 0x%08x\n", (uint8_t)value, addr); + break; + case MEM_RW_MODE_16BIT: + *((volatile uint16_t *)addr) = (uint16_t)value; + MEM_LOG("write 0x%04x -> 0x%08x\n", (uint16_t)value, addr); + break; + case MEM_RW_MODE_32BIT: + *((volatile uint32_t *)addr) = (uint32_t)value; + MEM_LOG("write 0x%08x -> 0x%08x\n", (uint32_t)value, addr); + break; + default: + break; + } + + return CMD_STATUS_ACKED; +} + +static struct cmd2_data g_mem_cmds[] = { + { "r", 1, cmd_mem_read_exec }, + { "w", 1, cmd_mem_write_exec }, +}; + +enum cmd_status cmd_mem_exec(char *cmd) +{ + return cmd2_exec(cmd, g_mem_cmds, cmd_nitems(g_mem_cmds)); +} diff --git a/platform/mcu/xr871/project/common/cmd/cmd_mem.h b/platform/mcu/xr871/project/common/cmd/cmd_mem.h new file mode 100644 index 0000000000..e89a5b4634 --- /dev/null +++ b/platform/mcu/xr871/project/common/cmd/cmd_mem.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _CMD_MEM_H_ +#define _CMD_MEM_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +enum cmd_status cmd_mem_exec(char *cmd); + +#ifdef __cplusplus +} +#endif + +#endif /* _CMD_MEM_H_ */ diff --git a/platform/mcu/xr871/project/common/cmd/cmd_mqtt.c b/platform/mcu/xr871/project/common/cmd/cmd_mqtt.c new file mode 100644 index 0000000000..9ee64e3a51 --- /dev/null +++ b/platform/mcu/xr871/project/common/cmd/cmd_mqtt.c @@ -0,0 +1,559 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "cmd_util.h" +#include "cmd_mqtt.h" +#include "net/mqtt/MQTTClient-C/MQTTClient.h" + + +static MQTTPacket_connectData connectData = MQTTPacket_connectData_initializer; +static char *client_name; +static uint8_t *send_buf; +static uint8_t *recv_buf; +static Client client; +static Network network; + +#define MAX_SUB_TOPICS 5 +static char *sub_topic[MAX_SUB_TOPICS]; + +#define CMD_MQTT_BG_THREAD_STACK_SIZE (1024) +static OS_Thread_t mqtt_bg_thread; + +static OS_Mutex_t lock; + + +static enum cmd_status cmd_mqtt_init_exec(char *cmd) +{ + int32_t cnt; + uint32_t buf_size; + uint32_t alive_interval; + uint32_t clean; + char *tmp; + + /* get param */ + cnt = cmd_sscanf(cmd, "bufsize=%u alive=%u clean=%u", + &buf_size, &alive_interval, &clean); + + /* check param */ + if (cnt != 3) { + CMD_ERR("invalid param number %d\n", cnt); + return CMD_STATUS_INVALID_ARG; + } + + if (clean > 1) { + CMD_ERR("invalid clean %d\n", clean); + return CMD_STATUS_INVALID_ARG; + } + + if (OS_MutexCreate(&lock) != OS_OK) + return CMD_STATUS_FAIL;; + + if ((tmp = cmd_strrchr(cmd, '\"')) == NULL) + return CMD_STATUS_INVALID_ARG; + *tmp = '\0'; + if ((tmp = cmd_strchr(cmd, '\"')) == NULL) + return CMD_STATUS_INVALID_ARG; + tmp++; + + client_name = cmd_malloc(cmd_strlen(tmp) + 1); + + cmd_memcpy(client_name, tmp, cmd_strlen(tmp) + 1); + CMD_DBG("client name = %s\n", client_name); + + connectData.clientID.cstring = client_name; + connectData.keepAliveInterval = alive_interval; + connectData.cleansession = clean; + + send_buf = cmd_malloc(buf_size); + if (send_buf == NULL) { + CMD_ERR("no memory\n"); + OS_MutexDelete(&lock); + return CMD_STATUS_FAIL; + } + recv_buf = cmd_malloc(buf_size); + if (recv_buf == NULL) { + cmd_free(send_buf); + CMD_ERR("no memory\n"); + OS_MutexDelete(&lock); + return CMD_STATUS_FAIL; + } + + NewNetwork(&network); + MQTTClient(&client, &network, 6000, send_buf, buf_size, recv_buf, buf_size); + + + + return CMD_STATUS_OK; +} + +static enum cmd_status cmd_mqtt_will_exec(char *cmd) +{ + int32_t cnt; + char *topic; + uint8_t *buf; + char *tmp; + uint32_t qos; + uint32_t retain; + uint32_t size; + + /* get param */ + cnt = cmd_sscanf(cmd, "qos=%u retain=%u", + &qos, &retain); + + if ((tmp = cmd_strrchr(cmd, '\"')) == NULL) + return CMD_STATUS_INVALID_ARG; + *tmp = '\0'; + cnt += cmd_sscanf(tmp + 2, "size=%u", &size); + if ((tmp = cmd_strchr(cmd, '\"')) == NULL) + return CMD_STATUS_INVALID_ARG; + tmp++; + + /* check param */ + if (cnt != 3) { + CMD_ERR("invalid param number %d\n", cnt); + return CMD_STATUS_INVALID_ARG; + } + + if (qos > 2) { + CMD_ERR("invalid qos %d\n", qos); + return CMD_STATUS_INVALID_ARG; + } + + if (retain > 1) { + CMD_ERR("invalid retain %d\n", retain); + return CMD_STATUS_INVALID_ARG; + } + + topic = tmp; + CMD_DBG("topic name = %s\n", topic); + + buf = cmd_malloc(size); + if (buf == NULL) { + CMD_ERR("no memory\n"); + return CMD_STATUS_FAIL; + } + + + cmd_write_respond(CMD_STATUS_OK, "OK"); + cmd_raw_mode_enable(); + cmd_raw_mode_read(buf, size, 30000); + cmd_raw_mode_disable(); + + //will function + connectData.willFlag = 1; + connectData.will.message.lenstring.data = (char *)buf; + connectData.will.message.lenstring.len = size; + connectData.will.qos = qos; + connectData.will.retained = retain; + connectData.will.topicName.cstring = topic; + + cmd_free(buf); + + return CMD_STATUS_ACKED; +} + +static enum cmd_status cmd_mqtt_user_exec(char *cmd) +{ + char *tmp; + uint32_t size; + + if ((tmp = cmd_strrchr(cmd, '\"')) == NULL) + return CMD_STATUS_INVALID_ARG; + *tmp = '\0'; + if ((tmp = cmd_strchr(cmd, '\"')) == NULL) + return CMD_STATUS_INVALID_ARG; + tmp++; + + if ((size = cmd_strlen(tmp)) == 0) + return CMD_STATUS_INVALID_ARG; + size++; + + connectData.username.lenstring.data = cmd_malloc(size); + connectData.username.lenstring.len = size; + memcpy(connectData.username.lenstring.data, tmp, size); + + return CMD_STATUS_OK; +} + +static enum cmd_status cmd_mqtt_password_exec(char *cmd) +{ + char *tmp; + uint32_t size; + + if ((tmp = cmd_strrchr(cmd, '\"')) == NULL) + return CMD_STATUS_INVALID_ARG; + *tmp = '\0'; + if ((tmp = cmd_strchr(cmd, '\"')) == NULL) + return CMD_STATUS_INVALID_ARG; + tmp++; + + if ((size = cmd_strlen(tmp)) == 0) + return CMD_STATUS_INVALID_ARG; + size++; + + connectData.password.lenstring.data = cmd_malloc(size); + connectData.password.lenstring.len = size; + memcpy(connectData.password.lenstring.data, tmp, size); + + return CMD_STATUS_OK; +} + + +static void cmd_mqtt_bg(void *arg) +{ + int rc; + + while (1) { + OS_MutexLock(&lock, 0xffffffffU); + if ((rc = MQTTYield(&client, 3000)) != 0) + CMD_WRN("Return code from yield is %d\n", rc); + OS_MutexUnlock(&lock); + } + +} + +static enum cmd_status cmd_mqtt_connect_exec(char *cmd) +{ + int32_t cnt; + char addr_str[15]; + uint32_t port; + int32_t rc; + uint32_t i; + + /* get param */ + cnt = cmd_sscanf(cmd, "server=%15s port=%u", + addr_str, &port); + + /* check param */ + if (cnt != 2) { + CMD_ERR("invalid param number %d\n", cnt); + return CMD_STATUS_INVALID_ARG; + } + + i = 10; + char* address = addr_str; + while ((rc = ConnectNetwork(&network, address, port)) != 0) + { + CMD_WRN("Return code from network connect is %d\n", rc); + cmd_msleep(2000); + if (!(i--)) + return CMD_STATUS_FAIL; + } + + if ((rc = MQTTConnect(&client, &connectData)) != 0) { + CMD_ERR("Return code from MQTT connect is %d\n", rc); + return CMD_STATUS_FAIL; + } + else + CMD_DBG("MQTT Connected\n"); + + + + if (OS_ThreadCreate(&mqtt_bg_thread, + "", + cmd_mqtt_bg, + NULL, + OS_PRIORITY_NORMAL, + CMD_MQTT_BG_THREAD_STACK_SIZE) != OS_OK) { + CMD_ERR("create mqtt background failed\n"); + return CMD_STATUS_FAIL; + } + + + return CMD_STATUS_OK; +} + +void mqtt_msg_cb(MessageData* data) +{ + cmd_write_event(CMD_EVENT_MQTT_MSG_RECV, "[topic: %.*s] %.*s", data->topicName->lenstring.len, + data->topicName->lenstring.data, data->message->payloadlen, + (char *)data->message->payload); +} + +static enum cmd_status cmd_mqtt_subscribe_exec(char *cmd) +{ + int32_t cnt; + uint32_t i; + char *tmp; + uint32_t qos; + int rc; + + /* get param */ + cnt = cmd_sscanf(cmd, "qos=%u", + &qos); + + if ((tmp = cmd_strrchr(cmd, '\"')) == NULL) + return CMD_STATUS_INVALID_ARG; + *tmp = '\0'; + if ((tmp = cmd_strchr(cmd, '\"')) == NULL) + return CMD_STATUS_INVALID_ARG; + tmp++; + + + /* check param */ + if (cnt != 1) { + CMD_ERR("invalid param number %d\n", cnt); + return CMD_STATUS_INVALID_ARG; + } + + if (qos > 2) { + CMD_ERR("invalid qos %d\n", qos); + return CMD_STATUS_INVALID_ARG; + } + + i = 0; + while (sub_topic[i] != NULL) { + i++; + if (i >= MAX_SUB_TOPICS) { + CMD_WRN("Subscribe topics is max\n"); + return CMD_STATUS_FAIL; + } + } + + sub_topic[i] = cmd_malloc(cmd_strlen(tmp) + 1); + cmd_memcpy(sub_topic[i], tmp, cmd_strlen(tmp) + 1); + + if (OS_MutexLock(&lock, 60000) == OS_E_TIMEOUT) + return CMD_STATUS_FAIL; + + if ((rc = MQTTSubscribe(&client, sub_topic[i], qos, mqtt_msg_cb)) != 0) { + CMD_ERR("Return code from MQTT subscribe is %d\n", rc); + return CMD_STATUS_FAIL; + } + + OS_MutexUnlock(&lock); + + return CMD_STATUS_OK; +} + +static enum cmd_status cmd_mqtt_unsubscribe_exec(char *cmd) +{ + uint32_t i; + char *tmp; + int rc; + + /* get param */ + if ((tmp = cmd_strrchr(cmd, '\"')) == NULL) + return CMD_STATUS_INVALID_ARG; + *tmp = '\0'; + if ((tmp = cmd_strchr(cmd, '\"')) == NULL) + return CMD_STATUS_INVALID_ARG; + tmp++; + + i = 0; + while (i < MAX_SUB_TOPICS) { + if ((sub_topic[i] != NULL) && (!cmd_memcmp(sub_topic[i], tmp, cmd_strlen(tmp) + 1))) + break; + i++; + } + + if (OS_MutexLock(&lock, 60000) == OS_E_TIMEOUT) + return CMD_STATUS_FAIL; + + if ((rc = MQTTUnsubscribe(&client, tmp/*sub_topic[i]*/)) != 0) { + CMD_ERR("Return code from MQTT unsubscribe is %d\n", rc); + return CMD_STATUS_FAIL; + } + + if (i == MAX_SUB_TOPICS) + CMD_WRN("Unsubscribe topics is inexist\n"); + else { + cmd_free(sub_topic[i]); + sub_topic[i] = NULL; + } + + OS_MutexUnlock(&lock); + + return CMD_STATUS_OK; +} + +static enum cmd_status cmd_mqtt_publish_exec(char *cmd) +{ + int32_t cnt; + MQTTMessage message; + char *topic; + uint8_t *buf; + char *tmp; + uint32_t qos; + uint32_t retain; + uint32_t size; + int rc; + + /* get param */ + cnt = cmd_sscanf(cmd, "qos=%u retain=%u", + &qos, &retain); + + if ((tmp = cmd_strrchr(cmd, '\"')) == NULL) + return CMD_STATUS_INVALID_ARG; + *tmp = '\0'; + cnt += cmd_sscanf(tmp + 2, "size=%u", &size); + if ((tmp = cmd_strchr(cmd, '\"')) == NULL) + return CMD_STATUS_INVALID_ARG; + tmp++; + + + /* check param */ + if (cnt != 3) { + CMD_ERR("invalid param number %d\n", cnt); + return CMD_STATUS_INVALID_ARG; + } + + if (qos > 2) { + CMD_ERR("invalid qos %d\n", qos); + return CMD_STATUS_INVALID_ARG; + } + + if (retain > 1) { + CMD_ERR("invalid retain %d\n", retain); + return CMD_STATUS_INVALID_ARG; + } + + topic = tmp; + CMD_DBG("topic name = %s\n", topic); + + buf = cmd_malloc(size); + if (buf == NULL) { + CMD_ERR("no memory\n"); + return CMD_STATUS_FAIL; + } + + + cmd_write_respond(CMD_STATUS_OK, "OK"); + cmd_raw_mode_enable(); + cmd_raw_mode_read(buf, size, 30000); + + message.qos = qos; + message.retained = retain; + message.payload = buf; + message.payloadlen = size; + + if (OS_MutexLock(&lock, 60000) == OS_E_TIMEOUT) + return CMD_STATUS_FAIL; + + if ((rc = MQTTPublish(&client, topic, &message)) != 0) + CMD_ERR("Return code from MQTT publish is %d\n", rc); + else + CMD_DBG("MQTT publish is success\n"); + + OS_MutexUnlock(&lock); + + cmd_raw_mode_write((uint8_t *)&rc, sizeof(rc)); + cmd_raw_mode_disable(); + + cmd_free(buf); + + return CMD_STATUS_ACKED; + +} + +static enum cmd_status cmd_mqtt_disconnect_exec(char *cmd) +{ + int32_t cnt; + uint32_t tcponly; + int rc; + uint32_t i; + + /* get param */ + cnt = cmd_sscanf(cmd, "tcponly=%u", + &tcponly); + + /* check param */ + if (cnt != 1) { + CMD_ERR("invalid param number %d\n", cnt); + return CMD_STATUS_INVALID_ARG; + } + + if (tcponly > 1) { + CMD_ERR("invalid tcponly %d\n", tcponly); + return CMD_STATUS_INVALID_ARG; + } + + + OS_ThreadDelete(&mqtt_bg_thread); + + i = 0; + while (i < MAX_SUB_TOPICS) { + if (sub_topic[i] != NULL) { + cmd_free(sub_topic[i]); + sub_topic[i] = NULL; + } + i++; + } + + if (tcponly == 0) { + if ((rc = MQTTDisconnect(&client)) != 0){ + CMD_ERR("Return code from MQTT disconnect is %d\n", rc); + network.disconnect(&network); + return CMD_STATUS_FAIL; + } + } else { + network.disconnect(&network); + } + + return CMD_STATUS_OK; +} + +static enum cmd_status cmd_mqtt_deinit_exec(char *cmd) +{ + + + + if (connectData.username.lenstring.len != 0) + cmd_free(connectData.username.lenstring.data); + if (connectData.password.lenstring.len != 0) + cmd_free(connectData.password.lenstring.data); + + cmd_free(send_buf); + cmd_free(recv_buf); + cmd_free(client_name); + + OS_MutexDelete(&lock); + + connectData = (MQTTPacket_connectData)MQTTPacket_connectData_initializer; + + return CMD_STATUS_OK; +} + +static struct cmd_data g_mqtt_cmds[] = { + { "init", cmd_mqtt_init_exec }, + { "will", cmd_mqtt_will_exec }, + { "user", cmd_mqtt_user_exec }, + { "password", cmd_mqtt_password_exec }, + { "connect", cmd_mqtt_connect_exec }, + { "subscribe", cmd_mqtt_subscribe_exec }, + { "unsubscribe", cmd_mqtt_unsubscribe_exec }, + { "publish", cmd_mqtt_publish_exec }, + { "disconnect", cmd_mqtt_disconnect_exec }, + { "deinit", cmd_mqtt_deinit_exec }, +}; + +enum cmd_status cmd_mqtt_exec(char *cmd) +{ + return cmd_exec(cmd, g_mqtt_cmds, cmd_nitems(g_mqtt_cmds)); +} diff --git a/platform/mcu/xr871/project/common/cmd/cmd_mqtt.h b/platform/mcu/xr871/project/common/cmd/cmd_mqtt.h new file mode 100644 index 0000000000..051f0f92bc --- /dev/null +++ b/platform/mcu/xr871/project/common/cmd/cmd_mqtt.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _CMD_MQTT_H_ +#define _CMD_MQTT_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +enum cmd_status cmd_mqtt_exec(char *cmd); + +#ifdef __cplusplus +} +#endif + +#endif /* _CMD_MQTT_H_ */ diff --git a/platform/mcu/xr871/project/common/cmd/cmd_netcmd.c b/platform/mcu/xr871/project/common/cmd/cmd_netcmd.c new file mode 100644 index 0000000000..c8e9949a7f --- /dev/null +++ b/platform/mcu/xr871/project/common/cmd/cmd_netcmd.c @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "cmd_util.h" +#include "sys/ducc/ducc_app.h" + +enum cmd_status cmd_netcmd_exec(char *cmd) +{ + cmd_write_respond(CMD_STATUS_OK, cmd_get_status_desc(CMD_STATUS_OK)); + ducc_app_ioctl(DUCC_APP_CMD_CONSOLE_EXEC, cmd); + return CMD_STATUS_ACKED; +} diff --git a/platform/mcu/xr871/project/common/cmd/cmd_netcmd.h b/platform/mcu/xr871/project/common/cmd/cmd_netcmd.h new file mode 100644 index 0000000000..2fba08e957 --- /dev/null +++ b/platform/mcu/xr871/project/common/cmd/cmd_netcmd.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _CMD_NETCMD_H_ +#define _CMD_NETCMD_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +enum cmd_status cmd_netcmd_exec(char *cmd); + +#ifdef __cplusplus +} +#endif + +#endif /* _CMD_NETCMD_H_ */ diff --git a/platform/mcu/xr871/project/common/cmd/cmd_nopoll.c b/platform/mcu/xr871/project/common/cmd/cmd_nopoll.c new file mode 100644 index 0000000000..e28ec180a3 --- /dev/null +++ b/platform/mcu/xr871/project/common/cmd/cmd_nopoll.c @@ -0,0 +1,892 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "cmd_util.h" +#include "net/nopoll/nopoll.h" + +static const char g_root_pem[] = +"-----BEGIN CERTIFICATE-----\r\n" +"MIID2TCCAsGgAwIBAgIJAM55lRNEqQ/FMA0GCSqGSIb3DQEBCwUAMIGCMQswCQYD\r\n" +"VQQGEwJFUzEPMA0GA1UECAwGTWFkcmlkMQ8wDQYDVQQHDAZNYWRyaWQxDTALBgNV\r\n" +"BAoMBEFTUEwxEzARBgNVBAsMClRJIFN1cHBvcnQxDTALBgNVBAMMBFJvb3QxHjAc\r\n" +"BgkqhkiG9w0BCQEWD3NvcG9ydGVAYXNwbC5lczAeFw0xNzA4MTMwNDMzNTdaFw0y\r\n" +"MDA2MDIwNDMzNTdaMIGCMQswCQYDVQQGEwJFUzEPMA0GA1UECAwGTWFkcmlkMQ8w\r\n" +"DQYDVQQHDAZNYWRyaWQxDTALBgNVBAoMBEFTUEwxEzARBgNVBAsMClRJIFN1cHBv\r\n" +"cnQxDTALBgNVBAMMBFJvb3QxHjAcBgkqhkiG9w0BCQEWD3NvcG9ydGVAYXNwbC5l\r\n" +"czCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAPKbeM0iCe0u/IaZcDAA\r\n" +"8uWRGgKQZaXLOZmC4i6DaARX93UWyqY/BL5iTgDhW2hEVd+IKHg6gfqT8M4MhZAN\r\n" +"HplrZC+5kB8zBebhsTOy0xlTc7TZUGGN32IlJL7yX6uG0dAezEHiLLwbnszd6LxK\r\n" +"qv9+bI2dXcbpUyPpgSoPwEM+DYmmzaHvJYOjh23P9Qqsb9BxIvQMgQnevo7mKV7+\r\n" +"jem4J6zZURFgoSjqGAKX9DKmU73WUpf+aGByqCYA/c7Mq+Zc0zcMv42Z/GF34iST\r\n" +"8PA98LnhvzsA/XvDJ9Ilw5xuSx/AwX3luyrq/3h9r3XGH4IfPh74i/R253nR5gz1\r\n" +"fRcCAwEAAaNQME4wHQYDVR0OBBYEFO/TotXSWOl7YvpaGlS2fz5SvrJ4MB8GA1Ud\r\n" +"IwQYMBaAFO/TotXSWOl7YvpaGlS2fz5SvrJ4MAwGA1UdEwQFMAMBAf8wDQYJKoZI\r\n" +"hvcNAQELBQADggEBAM1F0Rs8y40qAHxE0NGAbhueoD7FydD12tn1Iwctox958/9k\r\n" +"UKZpHKZLpK7r4o9ysEJm5achMSNjOuVP7E5GeCONvrlIMB0ofW+5ilN23X4cQQpb\r\n" +"6CciICtYN00PR0RhpljvVqssv5m7OZA2ak2+mmTcxki0ZfBpkiID8EFEygPXkOgS\r\n" +"noTZmeh73OhQxHRQmmoqcZMmelcE0Mkygw/MVmdFhwM9kjkQ6agXXeWLJ83ORVCm\r\n" +"5bOt2dFsDvGwb+qo0EedNsBswthPlx66NRHD3+ULwQdNFIlE9MFVezwIWRyI+F+D\r\n" +"+5hCmkR6W8fWKxXLJqR501AsSqJCloKa3EOUCxQ=\r\n" +"-----END CERTIFICATE-----\r\n" +"-----BEGIN RSA PRIVATE KEY-----\r\n" +"MIIEpAIBAAKCAQEA8pt4zSIJ7S78hplwMADy5ZEaApBlpcs5mYLiLoNoBFf3dRbK\r\n" +"pj8EvmJOAOFbaERV34goeDqB+pPwzgyFkA0emWtkL7mQHzMF5uGxM7LTGVNztNlQ\r\n" +"YY3fYiUkvvJfq4bR0B7MQeIsvBuezN3ovEqq/35sjZ1dxulTI+mBKg/AQz4NiabN\r\n" +"oe8lg6OHbc/1Cqxv0HEi9AyBCd6+juYpXv6N6bgnrNlREWChKOoYApf0MqZTvdZS\r\n" +"l/5oYHKoJgD9zsyr5lzTNwy/jZn8YXfiJJPw8D3wueG/OwD9e8Mn0iXDnG5LH8DB\r\n" +"feW7Kur/eH2vdcYfgh8+HviL9HbnedHmDPV9FwIDAQABAoIBAQDO6vtlOhrtSJ3o\r\n" +"zwV4a9a/JbrhZPbNKT+/RnpS4ZDd44kTPJUqpaUOHBobdhnYHDCBrkeWA5DIf1Vq\r\n" +"6BMigY4PnCCe882QTpxCB0xzo5mYiCyvmomMKszTkIp+sYoXaa2ZrCraJmWxKn2O\r\n" +"jdXqgmTF9LXxMr7A4FAg2Q9pKHX64lDcYQKinB6ehuCa5TB/I4KZUdhck7zGYy4o\r\n" +"vaFM57tWo36Cl9VWC/kpwBnzecSIASm/EI+Tro0shybLzdBzbgC4AmMHl/s0sVmy\r\n" +"nh6LbDrLW/HLDyIqreGpkg5WL7sforz62v05BLN3yRNhf4wXJr+yvy9+pl39SA34\r\n" +"TcqW79ABAoGBAPoWc8fF+/qyxnv75WqmjaPqfIyFAzEGin0ftyAGjFPuiBV7qLWf\r\n" +"F5UaKuCPwJKXziAoOpC9eCLnKNa9b4x3lMUoUAQaswePUSKZetjygg165NZcKbaU\r\n" +"VRe7ZomHfoZCMmCuPxDcPGvzkqWILggSUwT706dxP8FKzUT9GMHG/nUBAoGBAPhX\r\n" +"v24Llj5xSFQ9T96tv05ZbDUzJhF/D5KJQPGgze8Y+yPcc0qXwvNDWv2deP956ozD\r\n" +"izPR0BiTif7BLYtGl1QCqVsHH1bXNL41zDoQyd0MYbnvgMtO63JzEnXgjeEPf9MB\r\n" +"URGrzTtybH0QbjjjyOHDYN2ov6ibo4BDqIff1voXAoGAQ5S5qOyZkT+qX2rMritf\r\n" +"70G4coinOiJYXkOpyt+6IBw/bf7CsoHRb5toptJx6atYKlx0pxRsWJI6+PysHjta\r\n" +"WoTwSoukgZB7DmyHRAUUm+vv1vWE2C/xZr5fkR6qVEGjN3ke1cGOGmXx5QWN2uSI\r\n" +"xrn+kisKXYOHQPNcM1FBBgECgYBYTaMktvwCUfd89Jwcur0GV+eQ9xH3MK3R1foA\r\n" +"sMPo24IZGz7nbBUGuJF3EnI+rRQIX1N05orO4k80BW5zUnNmjNe5JUwwba80Xx6y\r\n" +"GtwcPFXXbmDmabZwaJhMz77DIua5mYNA/EyqcaI/ygLXOa9pDza3OlSD2GuHpQcc\r\n" +"4SuomwKBgQCb9TSOwLBvhUQuJeLnKOFJ2BNseYUVMWGq1+Kkg/47UfA/fPH8dk8q\r\n" +"8OuTKucLMi8AiBb9LKlnF87/019E1HReOuUIsrBfgBWwfsZUIypc8lGQHvoBjj1D\r\n" +"G98fdnnfXT6Xs5YwKCGi+DF7ZKq402GsjTa+XByAN95PdxEKqCUQnw==\r\n" +"-----END RSA PRIVATE KEY-----\r\n"; + +static const char g_server_pem[] = +"-----BEGIN CERTIFICATE-----\r\n" +"MIIDkzCCAnsCCQDpTEc56X02UDANBgkqhkiG9w0BAQsFADCBgjELMAkGA1UEBhMC\r\n" +"RVMxDzANBgNVBAgMBk1hZHJpZDEPMA0GA1UEBwwGTWFkcmlkMQ0wCwYDVQQKDARB\r\n" +"U1BMMRMwEQYDVQQLDApUSSBTdXBwb3J0MQ0wCwYDVQQDDARSb290MR4wHAYJKoZI\r\n" +"hvcNAQkBFg9zb3BvcnRlQGFzcGwuZXMwHhcNMTcwODEzMDQzMzU3WhcNMjAwNjAy\r\n" +"MDQzMzU3WjCBkzELMAkGA1UEBhMCRVMxDzANBgNVBAgMBk1hZHJpZDEPMA0GA1UE\r\n" +"BwwGTWFkcmlkMQ0wCwYDVQQKDARBU1BMMRMwEQYDVQQLDApUSSBTdXBwb3J0MR4w\r\n" +"HAYDVQQDDBVzZXJ2ZXIubm9wb2xsLmFzcGwuZXMxHjAcBgkqhkiG9w0BCQEWD3Nv\r\n" +"cG9ydGVAYXNwbC5lczCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALgr\r\n" +"2XCOc1Ee/O8QnMV2aWwqarsQDlEjwtn6uvwz7MWM42iKgPfbA+eJzhdqCmbnwNsJ\r\n" +"K6aC8Xt0P/OjVlC8UNgGk6IcGhceoteyQXVEYF3ccVMDOX37xwAAcVbRIXXA0sAc\r\n" +"H+kXOPnjuJiQyaZa4K/Wv6WFQKVZ5tefg1dcn1TiS/1gbLNhqgGBPEueNH5+S0/2\r\n" +"2LiG9+IN8hA+Xag604d7ryscmhim1JpwHx57jd96a4WiZ744vz/V8rQB3Gx0iwJV\r\n" +"wj+6NEkxvDmDp0N5IFwQNgEqVTZg900w1I1aNVn1tP4kiNtT3mzyrMYjvKTbunOt\r\n" +"UfRvdND3U6y+7VnZG4MCAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAKoxmoX+DNLyA\r\n" +"2Wj/WO/HGY38tQboQYhFUftWLyNxFIXL+BibIPGuPcdKHJI56V+pXRDopdZ4s8Wm\r\n" +"zv+UW2/EjKS1QsGl7C780OzRFFUgOJfRM2WpdvY2DhBpyiKOG3+n9N1Yawk1bWe9\r\n" +"sjL34kcNdSwU48BQsSLhhdqhnXrJOwM6khIHoWcWrDexBD6cYkqzB1vJIuRx/mEy\r\n" +"LzV6vLARttavNljLr9m3jCI4jl/xrVVn1enhoBoR+EaX/NRC25RTnu4NpVNIZsjH\r\n" +"QG7o4N84J5qiLhzSVwsMFaOgZFboEtsIopdn54GuxLPuW8Lj96pQwI7Zn7BcGqqj\r\n" +"epOMHKhd3w==\r\n" +"-----END CERTIFICATE-----\r\n" +"-----BEGIN RSA PRIVATE KEY-----\r\n" +"MIIEpAIBAAKCAQEAuCvZcI5zUR787xCcxXZpbCpquxAOUSPC2fq6/DPsxYzjaIqA\r\n" +"99sD54nOF2oKZufA2wkrpoLxe3Q/86NWULxQ2AaTohwaFx6i17JBdURgXdxxUwM5\r\n" +"ffvHAABxVtEhdcDSwBwf6Rc4+eO4mJDJplrgr9a/pYVApVnm15+DV1yfVOJL/WBs\r\n" +"s2GqAYE8S540fn5LT/bYuIb34g3yED5dqDrTh3uvKxyaGKbUmnAfHnuN33prhaJn\r\n" +"vji/P9XytAHcbHSLAlXCP7o0STG8OYOnQ3kgXBA2ASpVNmD3TTDUjVo1WfW0/iSI\r\n" +"21PebPKsxiO8pNu6c61R9G900PdTrL7tWdkbgwIDAQABAoIBAHl0GWH73xppGB5D\r\n" +"0075Js65LkeMcBScQ7xid/sn9wXUQofHhwNtAKcpnqqcOJ3iSbFo28tkYQGsCRyP\r\n" +"47U8awf7VlK6u43xmywplrdRz0c2v9khsEFfz701jYjWQ32XGqSsJ3Mc6GsK/MpH\r\n" +"+WNmXPQ454utU5N3tR8PR5tOLpjXwItT0QvtbojP58vvvdRAmEbG0m3jSV8vhxuu\r\n" +"7/SRtWZhDk/aHYv47bdwyb8xQYneAjXjF5CINQWKEs6qnZRlvGWiiGgZR+zyoFQ6\r\n" +"H8jN8zYsEnLbII+ODDRkCXkVeEmJ9bZl7fARpYXQoCraYra1Tyk4RkkVYC8hjUji\r\n" +"ruf4LnECgYEA58lvPw/dTs6AXAnQz8A9s4743REvSy2ivR5zFzsPkfcpplMNmhBH\r\n" +"JsolBrRBsFzd1O+Wh8uENFmez7l2mnGlpn/n8dDBzN7Iy9sJ+o9dQhRRQX3mtKr7\r\n" +"80MBmTMxiGCgNk2Zc1/K4Ze8B/1gQlyIWcwj1A/bQEpDBYA/mU9jVkkCgYEAy2kN\r\n" +"7AQKZfRcmFM+SZi8xyr4ulI4Wb7wbrIVIROv9l3ea0jAz1etozt/HOShpj0s3jYm\r\n" +"5nXEGAwu7MOaSKMwRG323KA3+pRHUoyISA4OWeY0dVJbmi3HYT8eqkWFupXwbgBi\r\n" +"x5dFQ0a0EfK9J3THs1Q6mhwF8XWX6ZTWPIoHs2sCgYEAihhjZUkIa5lwtGVMuVop\r\n" +"6L732o4QhVPtL4viuiaAAlElyWiWJAIqwhuRr8rZkD/kEqekIcfJPn9etRgkZ6Pq\r\n" +"M6blBBeCP5NEQuB/s36S63m7Z6+hMmbksWxsmV/81Onsyi21jR23fGywpHn7Tc5f\r\n" +"llCf0AqacFVOVQqx2Z+I6bECgYEAwbueEeNLUA2sPzxRf+EkM4G/Ah6bIwcTkiq0\r\n" +"qxtIJZsD4ySY1yJH88cP6yHqwB1V5ZKn/CnACbNY4hHumS4sI5CkZT+H8XZWc837\r\n" +"Yr1Yd9Ekt3UoX7EoS3vFpiCvHKYAJ5Tgf/6Ybloh2VJbTAASz77yTaNRGceJV2R1\r\n" +"o5VHfl8CgYB3ZZMaMTP7f27CtKFqk5x/nreBPaT0KWe3nI06AO7o0odY5BnkjBxB\r\n" +"tRj/fbqi8CLMcD4shbUr6RSAbf8lsmmiUgsDUYZA7e6wspn4S1f2z9uREGzfUopm\r\n" +"3i20zlGmTOFFr96ny6qx/meiZBvVy7Kl5kWKRM1B6Pu6X4mtxi0hvA==\r\n" +"-----END RSA PRIVATE KEY-----\r\n"; + +static const char g_client_pem[] = +"-----BEGIN CERTIFICATE-----\r\n" +"MIIDkzCCAnsCCQDpTEc56X02UTANBgkqhkiG9w0BAQsFADCBgjELMAkGA1UEBhMC\r\n" +"RVMxDzANBgNVBAgMBk1hZHJpZDEPMA0GA1UEBwwGTWFkcmlkMQ0wCwYDVQQKDARB\r\n" +"U1BMMRMwEQYDVQQLDApUSSBTdXBwb3J0MQ0wCwYDVQQDDARSb290MR4wHAYJKoZI\r\n" +"hvcNAQkBFg9zb3BvcnRlQGFzcGwuZXMwHhcNMTcwODEzMDQzMzU4WhcNMjAwNjAy\r\n" +"MDQzMzU4WjCBkzELMAkGA1UEBhMCRVMxDzANBgNVBAgMBk1hZHJpZDEPMA0GA1UE\r\n" +"BwwGTWFkcmlkMQ0wCwYDVQQKDARBU1BMMRMwEQYDVQQLDApUSSBTdXBwb3J0MR4w\r\n" +"HAYDVQQDDBVjbGllbnQubm9wb2xsLmFzcGwuZXMxHjAcBgkqhkiG9w0BCQEWD3Nv\r\n" +"cG9ydGVAYXNwbC5lczCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALqc\r\n" +"fTM7ADZT3TNLX5dojfpmwh8nxcQjM9/JeC5aPa027hds9js80JlQVeoAcgWNuJjD\r\n" +"V9ISOnAEED6taOrz4cNMRweHbniYUJAC0ADaGJNVjhl7Y1TsQ2v+Ja/Pypj8tSJp\r\n" +"14sKH8qFJz5wm0e8F0x2ZpoMxkhp7G1lFW/3CpjZEcpIwzh0BL6AhngIPgohAmQt\r\n" +"v+SkKhooZhy3aDh87ex1Al4BjQ7z2pckK/mYLKOnZFZS9KRxLC1nB2H+SdwzRd3b\r\n" +"woP5V+OkjdoLFPvPLH+AFlvrYMRj/Rhc5cHSTyqM1DO6sMpCuKOuXClmkjURAXiA\r\n" +"Q7CJTPZs/FurvHbgiSkCAwEAATANBgkqhkiG9w0BAQsFAAOCAQEArXstlNlqZgYx\r\n" +"2qImPdZeKBMGS9ymWcb7Vh3JgqMcERZzwjq5cYiY6+CTL+oAyZ0iWGRqTAUQkuZN\r\n" +"iQ18j5norcGJTTC3hmJqJTDE0P60f/0L84vd/T+nJ9wmwIPcfkK+KcdKlfEdSXNg\r\n" +"dDzLb1nBTyDnb6+uPZIxA3ujO+szSBqD1c7fUh//+DhjAZoUD6EMFixTRmjnD90c\r\n" +"gEemQl++Z/6f1T3XzSiszo/+K1rJISyG/nQ08jo3uH0qBIPhjziNqoZ6LvZ8BAwr\r\n" +"Bvvz7zkPtCWoxEiHq+nhzS4lS6fSiHJ5Zeu9tUeljhbE7Zg0y6T2jqqE22npcYRZ\r\n" +"E2Z//IOQnA==\r\n" +"-----END CERTIFICATE-----\r\n" +"-----BEGIN RSA PRIVATE KEY-----\r\n" +"MIIEpAIBAAKCAQEAupx9MzsANlPdM0tfl2iN+mbCHyfFxCMz38l4Llo9rTbuF2z2\r\n" +"OzzQmVBV6gByBY24mMNX0hI6cAQQPq1o6vPhw0xHB4dueJhQkALQANoYk1WOGXtj\r\n" +"VOxDa/4lr8/KmPy1ImnXiwofyoUnPnCbR7wXTHZmmgzGSGnsbWUVb/cKmNkRykjD\r\n" +"OHQEvoCGeAg+CiECZC2/5KQqGihmHLdoOHzt7HUCXgGNDvPalyQr+Zgso6dkVlL0\r\n" +"pHEsLWcHYf5J3DNF3dvCg/lX46SN2gsU+88sf4AWW+tgxGP9GFzlwdJPKozUM7qw\r\n" +"ykK4o65cKWaSNREBeIBDsIlM9mz8W6u8duCJKQIDAQABAoIBADj34wTLw0HSklnB\r\n" +"bdpEEevaiPKH6sj40jJESAsmg2OovECsg/MLmsbjDodUQXrOjAcew2WfKPXfV7I6\r\n" +"sCfh8g7z708uGhx4q0d7hFJuQkhgOvQuheF8Cw0h1hF5BulzZDywQRvyKEhkI9nc\r\n" +"+JqKojY2gxYORk3/AC1ZSZUXvsVwIWrDzx14y2fiHixwgrudBVD3V+cNXtivGV3N\r\n" +"dEt3Q/taTLUGLm9z0iRu0fY5O4Q7NGt7tBWmeKqd+tSQQf+IZIuv4JSETnnKMBFz\r\n" +"fdG1V379eBt9G60k31DyLwdnNXZ1UXwF8Yiuh3FWoTdF47EIjUvhY3W79eamcxQ0\r\n" +"ZG0OEk0CgYEA5lizFcihcYx2ABSnQaUBfntpEiRJ9aXMugJIxctJtvg/0LPvrMBI\r\n" +"wz/ISjRTNXQfzgvFK2ho9Z9ZCn5gACgg9TajoJXhiEW3YJmv+bQCkwHqvmlfrYhQ\r\n" +"CHskWAcC2wG4Eai4mhrquBZWC0P407FkrIPqXR5VuP0Lnt968YP7xa8CgYEAz2Tg\r\n" +"NoL6hi/BvkjiJEbtx6ysDIxfmxy/c3v8zeTbSnjUa7NfpDlmHZ7foeHJwsTvB9Es\r\n" +"iGhpolfewPyBJTNRaKTO7KG0h9h4ZNOzswUepE+y9sQYXzqWLrVIa59Fd/OD1DjX\r\n" +"tyvRWnE+vlMi2jr90oMoQ6gtxgW8f5OqEs1/rKcCgYEAmt0VgdbCW9HgX4uDK4oh\r\n" +"KAUWjgDhzIpsyr3QaVKUO7RJSyo+P/mCsepG4USukKiC3YdiRx3KOyCnv3tZVaGl\r\n" +"DtduSP+bgn/EmuiYvBt/A5DtXpj/n26eyCTqOVh1XgKY2x6BBfKEzcEOlkJbnw+L\r\n" +"rMR/o51KarWNw4Fiac+fTLcCgYBcBk7W4Vd4o0g8vupZXn+z0hKlnsPtd9wbyjBq\r\n" +"y9niOatH/te5bx64lf51PlLgcbH+rfosrOfyeTsGNwS3FQZSgI6wwS/x90O0uZtL\r\n" +"HEJ0dkja5jhKSRzKrPXti0av6wXb7T0Ksg67KvTETu9wFiz6c+gx3TYyX+k8UrFs\r\n" +"lnLIkQKBgQCLTxcJcf48uBQOBJ3yBZJWI9R/1ZQnaA4BcLj9VuC0iMK0URyV/uc4\r\n" +"hZ7F+/4URAuobYENLFJ6dagHW4JDFlUTdTT+CLqQLzEhglGMXiUIqz31o6QQft1j\r\n" +"lQrSLp9Di/IQhMs2r78NCbHbkopvqCjO186ufJHvE+e3eDefBQZ/BQ==\r\n" +"-----END RSA PRIVATE KEY-----\r\n"; + +#define CMD_NOPOLL_MSG_EXIT (0) +#define CMD_NOPOLL_MSG_OPEN (1) +#define CMD_NOPOLL_MSG_SEND (2) +#define CMD_NOPOLL_MSG_RECV (3) +#define CMD_NOPOLL_MSG_CLOSE (4) + +struct cmd_nopoll_open_data { + uint32_t tls; + uint32_t verify; + uint32_t cer; +}; + +struct cmd_nopoll_send_data { + uint8_t *buf; + uint32_t size; +}; + +struct cmd_nopoll_msg { + uint32_t type; + void *data; +}; + +#define CMD_NOPOLL_QUEUE_WAIT_TIME (5000) +#define CMD_NOPOLL_THREAD_STACK_SIZE (4 * 1024) +static OS_Thread_t g_nopoll_server_thread; +static OS_Thread_t g_nopoll_client_thread; + +static char g_server_host[32] = {0}; +static char g_server_port[8] = {0}; +static noPollCtx *g_server_ctx = NULL; +static noPollConn *g_listener = NULL; +static noPollConnOpts *g_listener_opts = NULL; + +static OS_Queue_t g_client_queue; +static char g_client_host[32] = {0}; +static char g_client_port[8] = {0}; +static noPollCtx *g_client_ctx = NULL; +static noPollConn *g_conn = NULL; +static noPollConnOpts *g_conn_opts = NULL; + +static void cmd_nopoll_server_task(void *arg) +{ + nopoll_loop_wait(g_server_ctx, 0); + + CMD_DBG("%s() end\n", __func__); + OS_ThreadDelete(&g_nopoll_server_thread); +} + +static void cmd_nopoll_server_on_close(noPollCtx *ctx, noPollConn *conn, noPollPtr user_data) +{ + CMD_LOG(1, "called connection close\n"); +} + +static nopoll_bool cmd_nopoll_server_on_open(noPollCtx *ctx, noPollConn *conn, noPollPtr user_data) +{ + CMD_LOG(1, "called connection open\n"); + + nopoll_conn_set_on_close(conn, cmd_nopoll_server_on_close, NULL); + + if (!nopoll_conn_set_sock_block(nopoll_conn_socket(conn), nopoll_false)) { + CMD_ERR("ERROR: failed to configure non-blocking state to connection..\n"); + return nopoll_false; + } + + return nopoll_true; +} + +static void cmd_nopoll_server_on_message(noPollCtx *ctx, noPollConn *conn, noPollMsg *msg, noPollPtr user_data) +{ + int size = nopoll_msg_get_payload_size(msg); + const char *content = (const char *)nopoll_msg_get_payload(msg); + + CMD_LOG(1, "server receive message:\n"); + CMD_LOG(1, "%s\n", content); + + nopoll_conn_send_text(conn, content, size); + + if (nopoll_cmp(content, "nopoll test stop running")) { + nopoll_loop_stop(g_server_ctx); + } +} + +static enum cmd_status cmd_nopoll_server_init_exec(char *cmd) +{ + int cnt; + uint32_t dbg; + + cnt = cmd_sscanf(cmd, "d=%u", &dbg); + if (cnt != 1) { + CMD_ERR("cmd_sscanf return: cnt = %d\n", cnt); + return CMD_STATUS_INVALID_ARG; + } + + if (g_server_ctx != NULL) { + CMD_ERR("already init\n"); + return CMD_STATUS_FAIL; + } + + g_server_ctx = nopoll_ctx_new(); + if (g_server_ctx == NULL) { + CMD_ERR("nopoll_ctx_new failed\n"); + return CMD_STATUS_FAIL; + } + + if (dbg == 0) + nopoll_log_enable(g_server_ctx, nopoll_false); + else + nopoll_log_enable(g_server_ctx, nopoll_true); + + /* set on open */ + nopoll_ctx_set_on_open(g_server_ctx, cmd_nopoll_server_on_open, NULL); + + /* set on message received */ + nopoll_ctx_set_on_msg(g_server_ctx, cmd_nopoll_server_on_message, NULL); + + OS_ThreadSetInvalid(&g_nopoll_server_thread); + + return CMD_STATUS_OK; +} + +static enum cmd_status cmd_nopoll_server_open_exec(char *cmd) +{ + int cnt; + uint32_t tls; + uint32_t verify; + + cnt = cmd_sscanf(cmd, "h=%s p=%s t=%u v=%u", g_server_host, g_server_port, &tls, &verify); + if (cnt != 4) { + CMD_ERR("cmd_sscanf return: cnt = %d\n", cnt); + return CMD_STATUS_INVALID_ARG; + } + + if (g_server_ctx == NULL) { + CMD_ERR("without init\n"); + return CMD_STATUS_FAIL; + } + + if ((g_listener != NULL) || (g_listener_opts != NULL)) { + CMD_ERR("already open\n"); + return CMD_STATUS_FAIL; + } + + g_listener_opts = nopoll_conn_opts_new(); + if (g_listener_opts == NULL) { + CMD_ERR("nopoll_conn_opts_new failed\n"); + return CMD_STATUS_FAIL; + } + + if (tls == 0) + g_listener = nopoll_listener_new_opts(g_server_ctx, g_listener_opts, g_server_host, g_server_port); + else { + if (!nopoll_conn_opts_set_ssl_certs(g_listener_opts, + g_server_pem, sizeof(g_server_pem), + g_server_pem, sizeof(g_server_pem), + NULL, 0, + g_root_pem, sizeof(g_root_pem))) { + CMD_ERR("nopoll_conn_opts_set_ssl_certs failed\n"); + nopoll_conn_opts_free(g_listener_opts); + g_listener_opts = NULL; + return CMD_STATUS_FAIL; + } + + if (verify == 0) + nopoll_conn_opts_ssl_peer_verify (g_listener_opts, nopoll_false); + else + nopoll_conn_opts_ssl_peer_verify (g_listener_opts, nopoll_true); + + g_listener = nopoll_listener_tls_new_opts(g_server_ctx, g_listener_opts, g_server_host, g_server_port); + } + + if (g_listener == NULL) { + CMD_ERR("nopoll_listener(_tls)_new_opts failed\n"); + nopoll_conn_opts_free(g_listener_opts); + g_listener_opts = NULL; + return CMD_STATUS_FAIL; + } + + return CMD_STATUS_OK; +} + +static enum cmd_status cmd_nopoll_server_start_exec(char *cmd) +{ + if ((g_listener == NULL) || (g_listener_opts == NULL)) { + CMD_ERR("without open\n"); + return CMD_STATUS_FAIL; + } + + if (OS_ThreadIsValid(&g_nopoll_server_thread)) { + CMD_ERR("already start\n"); + return CMD_STATUS_FAIL; + } + + if (OS_ThreadCreate(&g_nopoll_server_thread, + "nopoll test server", + cmd_nopoll_server_task, + NULL, + OS_THREAD_PRIO_CONSOLE, + CMD_NOPOLL_THREAD_STACK_SIZE) != OS_OK) { + CMD_ERR("thread create failed\n"); + return CMD_STATUS_FAIL; + } + + return CMD_STATUS_OK; +} + +static enum cmd_status cmd_nopoll_server_stop_exec(char *cmd) +{ + if (!OS_ThreadIsValid(&g_nopoll_server_thread)) { + CMD_ERR("without start\n"); + return CMD_STATUS_FAIL; + } + + nopoll_loop_stop(g_server_ctx); + + return CMD_STATUS_OK; +} + +static enum cmd_status cmd_nopoll_server_close_exec(char *cmd) +{ + if ((g_listener == NULL) || (g_listener_opts == NULL)) { + CMD_ERR("without open\n"); + return CMD_STATUS_FAIL; + } + + if (OS_ThreadIsValid(&g_nopoll_server_thread)) { + CMD_ERR("without stop\n"); + return CMD_STATUS_FAIL; + } + + nopoll_conn_close(g_listener); + g_listener = NULL; + g_listener_opts = NULL; + + cmd_memset(g_server_host, 0, sizeof(g_server_host)); + cmd_memset(g_server_port, 0, sizeof(g_server_port)); + + return CMD_STATUS_OK; +} + +static enum cmd_status cmd_nopoll_server_deinit_exec(char *cmd) +{ + if (g_server_ctx == NULL) { + CMD_ERR("without init\n"); + return CMD_STATUS_FAIL; + } + + nopoll_ctx_unref(g_server_ctx); + nopoll_cleanup_library(); + g_server_ctx = NULL; + + return CMD_STATUS_OK; +} + +static void cmd_nopoll_client_open_task(struct cmd_nopoll_open_data *data) +{ + g_conn_opts = nopoll_conn_opts_new(); + if (g_conn_opts == NULL) { + CMD_ERR("nopoll_conn_opts_new failed\n"); + goto open_task_exit; + } + + if (data->tls == 0) + g_conn = nopoll_conn_new_opts(g_client_ctx, g_conn_opts, g_client_host, g_client_port, NULL, NULL, NULL, NULL); + else { + if (data->cer != 0) { + if (!nopoll_conn_opts_set_ssl_certs(g_conn_opts, + g_client_pem, sizeof(g_client_pem), + g_client_pem, sizeof(g_client_pem), + NULL, 0, + g_root_pem, sizeof(g_root_pem))) { + CMD_ERR("nopoll_conn_opts_set_ssl_certs failed\n"); + nopoll_conn_opts_free(g_conn_opts); + g_conn_opts = NULL; + goto open_task_exit; + } + } + + if (data->verify == 0) + nopoll_conn_opts_ssl_peer_verify (g_conn_opts, nopoll_false); + else + nopoll_conn_opts_ssl_peer_verify (g_conn_opts, nopoll_true); + + g_conn = nopoll_conn_tls_new(g_client_ctx, g_conn_opts, g_client_host, g_client_port, NULL, NULL, NULL, NULL); + } + + if (g_conn == NULL) { + CMD_ERR("nopoll_conn(_tls)_new(_opts) failed\n"); + goto open_task_exit; + } + + if (nopoll_conn_wait_until_connection_ready(g_conn, 10)) + CMD_LOG(1, "the connection is ready\n"); + else + CMD_ERR("connection timeout\n"); + +open_task_exit: + cmd_free(data); + data = NULL; + return; +} + +static void cmd_nopoll_client_send_task(struct cmd_nopoll_send_data *data) +{ + int ret = nopoll_conn_send_text(g_conn, (char *)data->buf, data->size); + if (ret != (int)data->size) + CMD_ERR("size = %u, but nopoll_conn_send_text ret = %d\n", data->size, ret); + + cmd_free(data->buf); + data->buf = NULL; + cmd_free(data); + data = NULL; + return; +} + +static void cmd_nopoll_client_recv_task(void *data) +{ + uint32_t msec = 10 * 1000; + noPollMsg *msg; + const char *content; + + while ((msg = nopoll_conn_get_msg(g_conn)) == NULL) { + if (!nopoll_conn_is_ok(g_conn)) { + CMD_ERR("received websocket connection close\n"); + return; + } + + if (msec == 0) { + CMD_ERR("get message timeout\n"); + return; + } + + nopoll_sleep(100000); + msec -= 100; + } + + content = (const char *)nopoll_msg_get_payload(msg); + CMD_LOG(1, "receive message:\n"); + CMD_LOG(1, "%s\n", content); + + nopoll_msg_unref (msg); + return; +} + +static void cmd_nopoll_client_close_task(void *data) +{ + nopoll_conn_close(g_conn); + g_conn = NULL; + g_conn_opts = NULL; + + cmd_memset(g_client_host, 0, sizeof(g_client_host)); + cmd_memset(g_client_port, 0, sizeof(g_client_port)); + + return; +} + +static void cmd_nopoll_client_task(void *arg) +{ + uint8_t task_exit = 0; + struct cmd_nopoll_msg *msg; + + while (task_exit == 0) { + + if (OS_MsgQueueReceive(&g_client_queue, (void **)&msg, OS_WAIT_FOREVER) != OS_OK) { + CMD_ERR("msg queue receive failed\n"); + break; + } + + CMD_DBG("recv msg type %u\n", msg->type); + + switch (msg->type) { + case CMD_NOPOLL_MSG_EXIT: + task_exit = 1; + break; + case CMD_NOPOLL_MSG_OPEN: + cmd_nopoll_client_open_task(msg->data); + break; + case CMD_NOPOLL_MSG_SEND: + cmd_nopoll_client_send_task(msg->data); + break; + case CMD_NOPOLL_MSG_RECV: + cmd_nopoll_client_recv_task(msg->data); + break; + case CMD_NOPOLL_MSG_CLOSE: + cmd_nopoll_client_close_task(msg->data); + break; + default: + CMD_WRN("unknown msg\n"); + break; + } + + cmd_free(msg); + msg = NULL; + } + + CMD_DBG("%s() end\n", __func__); + OS_ThreadDelete(&g_nopoll_client_thread); +} + +static enum cmd_status cmd_nopoll_client_init_exec(char *cmd) +{ + int cnt; + uint32_t dbg; + + cnt = cmd_sscanf(cmd, "d=%u", &dbg); + if (cnt != 1) { + CMD_ERR("cmd_sscanf return: cnt = %d\n", cnt); + return CMD_STATUS_INVALID_ARG; + } + + if (g_client_ctx != NULL) { + CMD_ERR("already init\n"); + return CMD_STATUS_FAIL; + } + + g_client_ctx = nopoll_ctx_new(); + if (g_client_ctx == NULL) { + CMD_ERR("nopoll_ctx_new failed\n"); + return CMD_STATUS_FAIL; + } + + if (dbg == 0) + nopoll_log_enable(g_client_ctx, nopoll_false); + else + nopoll_log_enable(g_client_ctx, nopoll_true); + + OS_ThreadSetInvalid(&g_nopoll_client_thread); + + if (OS_MsgQueueCreate(&g_client_queue, 1) != OS_OK) { + CMD_ERR("msg queue create failed\n"); + return CMD_STATUS_FAIL; + } + + if (OS_ThreadCreate(&g_nopoll_client_thread, + "nopoll test client", + cmd_nopoll_client_task, + NULL, + OS_THREAD_PRIO_CONSOLE, + CMD_NOPOLL_THREAD_STACK_SIZE) != OS_OK) { + CMD_ERR("thread create failed\n"); + return CMD_STATUS_FAIL; + } + + return CMD_STATUS_OK; +} + +static enum cmd_status cmd_nopoll_client_open_exec(char *cmd) +{ + int cnt; + uint32_t tls; + uint32_t verify; + uint32_t cer; + struct cmd_nopoll_open_data *data; + struct cmd_nopoll_msg *msg; + + cnt = cmd_sscanf(cmd, "h=%s p=%s t=%u v=%u c=%u", g_client_host, g_client_port, &tls, &verify, &cer); + if (cnt != 5) { + CMD_ERR("cmd_sscanf return: cnt = %d\n", cnt); + return CMD_STATUS_INVALID_ARG; + } + + if (g_client_ctx == NULL) { + CMD_ERR("without init\n"); + return CMD_STATUS_FAIL; + } + + if ((g_conn != NULL) || (g_conn_opts != NULL)) { + CMD_ERR("already open\n"); + return CMD_STATUS_FAIL; + } + + msg = cmd_malloc(sizeof(struct cmd_nopoll_msg)); + data = cmd_malloc(sizeof(struct cmd_nopoll_open_data)); + if ((msg == NULL) || (data == NULL)) { + CMD_ERR("malloc failed\n"); + if (msg) + cmd_free(msg); + if (data) + cmd_free(data); + return CMD_STATUS_FAIL; + } + + data->tls = tls; + data->verify = verify; + data->cer = cer; + + msg->type = CMD_NOPOLL_MSG_OPEN; + msg->data = data; + + if (OS_MsgQueueSend(&g_client_queue, msg, CMD_NOPOLL_QUEUE_WAIT_TIME) != OS_OK) { + CMD_ERR("msg queue send failed\n"); + if (msg) + cmd_free(msg); + if (data) + cmd_free(data); + return CMD_STATUS_FAIL; + } + + return CMD_STATUS_OK; +} + +static enum cmd_status cmd_nopoll_client_send_exec(char *cmd) +{ + int cnt; + int ret; + uint32_t size; + uint8_t *buf; + struct cmd_nopoll_send_data *data; + struct cmd_nopoll_msg *msg; + + cnt = cmd_sscanf(cmd, "s=%u", &size); + if (cnt != 1) { + CMD_ERR("cmd_sscanf return: cnt = %d\n", cnt); + return CMD_STATUS_INVALID_ARG; + } + + if (size == 0) { + CMD_ERR("size = 0\n"); + return CMD_STATUS_INVALID_ARG; + } + + if ((g_conn == NULL) || (g_conn_opts == NULL)) { + CMD_ERR("without open\n"); + return CMD_STATUS_FAIL; + } + + msg = cmd_malloc(sizeof(struct cmd_nopoll_msg)); + data = cmd_malloc(sizeof(struct cmd_nopoll_send_data)); + buf = cmd_malloc(size); + if ((msg == NULL) || (data == NULL) || (buf == NULL)) { + CMD_ERR("malloc failed\n"); + if (msg) + cmd_free(msg); + if (data) + cmd_free(data); + if (buf) + cmd_free(buf); + return CMD_STATUS_FAIL; + } + + cmd_write_respond(CMD_STATUS_OK, "OK"); + + cmd_raw_mode_enable(); + ret = cmd_raw_mode_read(buf, size, 10000); + if (ret != (int)size) { + CMD_ERR("size = %u, but cmd_raw_mode_read ret = %d\n", size, ret); + cmd_free(msg); + cmd_free(data); + cmd_free(buf); + cmd_raw_mode_write((uint8_t *)"FAIL\n", 5); + cmd_raw_mode_disable(); + return CMD_STATUS_ACKED; + } + cmd_raw_mode_disable(); + + data->buf = buf; + data->size = size; + + msg->type = CMD_NOPOLL_MSG_SEND; + msg->data = data; + + if (OS_MsgQueueSend(&g_client_queue, msg, CMD_NOPOLL_QUEUE_WAIT_TIME) != OS_OK) { + CMD_ERR("msg queue send failed\n"); + if (msg) + cmd_free(msg); + if (data) + cmd_free(data); + if (buf) + cmd_free(buf); + return CMD_STATUS_ACKED; + } + + return CMD_STATUS_ACKED; +} + +static enum cmd_status cmd_nopoll_client_recv_exec(char *cmd) +{ + struct cmd_nopoll_msg *msg; + + if ((g_conn == NULL) || (g_conn_opts == NULL)) { + CMD_ERR("without open\n"); + return CMD_STATUS_FAIL; + } + + msg = cmd_malloc(sizeof(struct cmd_nopoll_msg)); + if (msg == NULL) { + CMD_ERR("msg queue send failed\n"); + return CMD_STATUS_FAIL; + } + + msg->type = CMD_NOPOLL_MSG_RECV; + msg->data = NULL; + + if (OS_MsgQueueSend(&g_client_queue, msg, CMD_NOPOLL_QUEUE_WAIT_TIME) != OS_OK) { + CMD_ERR("msg queue send failed\n"); + if (msg) + cmd_free(msg); + return CMD_STATUS_FAIL; + } + + return CMD_STATUS_OK; +} + +static enum cmd_status cmd_nopoll_client_close_exec(char *cmd) +{ + struct cmd_nopoll_msg *msg; + + if ((g_conn == NULL) || (g_conn_opts == NULL)) { + CMD_ERR("without open\n"); + return CMD_STATUS_FAIL; + } + + msg = cmd_malloc(sizeof(struct cmd_nopoll_msg)); + if (msg == NULL) { + CMD_ERR("msg queue send failed\n"); + return CMD_STATUS_FAIL; + } + + msg->type = CMD_NOPOLL_MSG_CLOSE; + msg->data = NULL; + + if (OS_MsgQueueSend(&g_client_queue, msg, CMD_NOPOLL_QUEUE_WAIT_TIME) != OS_OK) { + CMD_ERR("msg queue send failed\n"); + if (msg) + cmd_free(msg); + return CMD_STATUS_FAIL; + } + + return CMD_STATUS_OK; +} + +static enum cmd_status cmd_nopoll_client_deinit_exec(char *cmd) +{ + struct cmd_nopoll_msg *msg; + + if (g_client_ctx == NULL) { + CMD_ERR("without init\n"); + return CMD_STATUS_FAIL; + } + + nopoll_ctx_unref(g_client_ctx); + g_client_ctx = NULL; + + msg = cmd_malloc(sizeof(struct cmd_nopoll_msg)); + if (msg == NULL) { + CMD_ERR("msg queue send failed\n"); + return CMD_STATUS_FAIL; + } + + msg->type = CMD_NOPOLL_MSG_EXIT; + msg->data = NULL; + + if (OS_MsgQueueSend(&g_client_queue, msg, CMD_NOPOLL_QUEUE_WAIT_TIME) != OS_OK) { + CMD_ERR("msg queue send failed\n"); + if (msg) + cmd_free(msg); + return CMD_STATUS_FAIL; + } + + OS_MSleep(100); + + if (OS_MsgQueueDelete(&g_client_queue) != OS_OK) { + CMD_ERR("msg queue delete failed\n"); + return CMD_STATUS_FAIL; + } + + return CMD_STATUS_OK; +} + +static struct cmd_data g_nopoll_server_cmds[] = { + { "init", cmd_nopoll_server_init_exec }, + { "open", cmd_nopoll_server_open_exec }, + { "start", cmd_nopoll_server_start_exec }, + { "stop", cmd_nopoll_server_stop_exec }, + { "close", cmd_nopoll_server_close_exec }, + { "deinit", cmd_nopoll_server_deinit_exec }, +}; + +static struct cmd_data g_nopoll_client_cmds[] = { + { "init", cmd_nopoll_client_init_exec }, + { "open", cmd_nopoll_client_open_exec }, + { "send", cmd_nopoll_client_send_exec }, + { "recv", cmd_nopoll_client_recv_exec }, + { "close", cmd_nopoll_client_close_exec }, + { "deinit", cmd_nopoll_client_deinit_exec }, +}; + +static enum cmd_status cmd_nopoll_server_exec(char *cmd) +{ + return cmd_exec(cmd, g_nopoll_server_cmds, cmd_nitems(g_nopoll_server_cmds)); +} + +static enum cmd_status cmd_nopoll_client_exec(char *cmd) +{ + return cmd_exec(cmd, g_nopoll_client_cmds, cmd_nitems(g_nopoll_client_cmds)); +} + +static struct cmd_data g_nopoll_cmds[] = { + { "srv", cmd_nopoll_server_exec }, + { "cli", cmd_nopoll_client_exec }, +}; + +enum cmd_status cmd_nopoll_exec(char *cmd) +{ + return cmd_exec(cmd, g_nopoll_cmds, cmd_nitems(g_nopoll_cmds)); +} + diff --git a/platform/mcu/xr871/project/common/cmd/cmd_nopoll.h b/platform/mcu/xr871/project/common/cmd/cmd_nopoll.h new file mode 100644 index 0000000000..b32f2dba3a --- /dev/null +++ b/platform/mcu/xr871/project/common/cmd/cmd_nopoll.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _CMD_NOPOLL_H_ +#define _CMD_NOPOLL_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +enum cmd_status cmd_nopoll_exec(char *cmd); + +#ifdef __cplusplus +} +#endif + +#endif /* _CMD_NOPOLL_H_ */ diff --git a/platform/mcu/xr871/project/common/cmd/cmd_ota.c b/platform/mcu/xr871/project/common/cmd/cmd_ota.c new file mode 100644 index 0000000000..5db6cb72d6 --- /dev/null +++ b/platform/mcu/xr871/project/common/cmd/cmd_ota.c @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "cmd_util.h" +#include "sys/ota.h" + +/* + * ota file + * ota http + */ + +enum cmd_status cmd_ota_file_exec(char *cmd) +{ + if (cmd[0] == '\0') { + CMD_ERR("OTA empty file url\n"); + return CMD_STATUS_INVALID_ARG; + } + + cmd_write_respond(CMD_STATUS_OK, "OK"); + + if (ota_get_image(OTA_PROTOCOL_FILE, cmd) != OTA_STATUS_OK) { + CMD_ERR("OTA file get image failed\n"); + return CMD_STATUS_ACKED; + } + + if (ota_verify_image(OTA_VERIFY_NONE, NULL) != OTA_STATUS_OK) { + CMD_ERR("OTA file verify image failed\n"); + return CMD_STATUS_ACKED; + } + + ota_reboot(); + + return CMD_STATUS_ACKED; +} + +enum cmd_status cmd_ota_http_exec(char *cmd) +{ + if (cmd[0] == '\0') { + CMD_ERR("OTA empty http url\n"); + return CMD_STATUS_INVALID_ARG; + } + + cmd_write_respond(CMD_STATUS_OK, "OK"); + + if (ota_get_image(OTA_PROTOCOL_HTTP, cmd) != OTA_STATUS_OK) { + CMD_ERR("OTA http get image failed\n"); + return CMD_STATUS_ACKED; + } + + if (ota_verify_image(OTA_VERIFY_NONE, NULL) != OTA_STATUS_OK) { + CMD_ERR("OTA http verify image failed\n"); + return CMD_STATUS_ACKED; + } + + ota_reboot(); + + return CMD_STATUS_ACKED; +} + +static struct cmd_data g_ota_cmds[] = { + { "file", cmd_ota_file_exec}, + { "http", cmd_ota_http_exec}, +}; + +enum cmd_status cmd_ota_exec(char *cmd) +{ + return cmd_exec(cmd, g_ota_cmds, cmd_nitems(g_ota_cmds)); +} diff --git a/platform/mcu/xr871/project/common/cmd/cmd_ota.h b/platform/mcu/xr871/project/common/cmd/cmd_ota.h new file mode 100644 index 0000000000..cefed2de55 --- /dev/null +++ b/platform/mcu/xr871/project/common/cmd/cmd_ota.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _CMD_OTA_H_ +#define _CMD_OTA_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +enum cmd_status cmd_ota_exec(char *cmd); + +#ifdef __cplusplus +} +#endif + +#endif /* _CMD_OTA_H_ */ diff --git a/platform/mcu/xr871/project/common/cmd/cmd_ping.c b/platform/mcu/xr871/project/common/cmd/cmd_ping.c new file mode 100644 index 0000000000..0c166e53d0 --- /dev/null +++ b/platform/mcu/xr871/project/common/cmd/cmd_ping.c @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "cmd_util.h" +#include "cmd_ping.h" +#include "lwip/netdb.h" +#include "net/ping/ping.h" + +struct ping_data pdata; +static OS_Thread_t g_ping_thread; +#define PING_THREAD_STACK_SIZE (1 * 1024) +#define PING_THREAD_EXIT OS_ThreadDelete + +void ping_run(void *arg) +{ + struct ping_data *data = (struct ping_data *)arg; + ping(data); + PING_THREAD_EXIT(&g_ping_thread); + +} + +int ping_start(struct ping_data *data) +{ + if (OS_ThreadIsValid(&g_ping_thread)) { + CMD_ERR("PING task is running\n"); + return -1; + } + + if (OS_ThreadCreate(&g_ping_thread, + "", + ping_run, + (void *)data, + OS_THREAD_PRIO_APP, + PING_THREAD_STACK_SIZE) != OS_OK) { + CMD_ERR("PING task create failed\n"); + return -1; + } + + return 0; +} + +int ping_get_host_by_name(char *name, unsigned int *address) +{ + struct hostent *host_entry; + + host_entry = gethostbyname(name); + if(host_entry) { + *(address) = *((u_long*)host_entry->h_addr_list[0]); + return 0; // OK + } else { + return 1; // Error + } +} + +enum cmd_status cmd_ping_exec(char *cmd) +{ + int argc; + char *argv[3]; + //struct ping_data pdata; + memset((void*) &pdata, 0, sizeof(pdata)); + + argc = cmd_parse_argv(cmd, argv, 3); + if (argc < 1 || argc > 2) { + CMD_ERR("invalid ping cmd, argc %d\n", argc); + return CMD_STATUS_INVALID_ARG; + } + unsigned int address = 0; + + if (ping_get_host_by_name(argv[0], &address) != 0) { + CMD_ERR("invalid ping host.\n"); + return CMD_STATUS_INVALID_ARG; + } + + pdata.sin_addr.addr = address; + if (argc > 1) + pdata.count = atoi(argv[1]); + else + pdata.count = 3; + + if (ping_start(&pdata) == 0) + return CMD_STATUS_OK; + else + return CMD_STATUS_FAIL; +} diff --git a/platform/mcu/xr871/project/common/cmd/cmd_ping.h b/platform/mcu/xr871/project/common/cmd/cmd_ping.h new file mode 100644 index 0000000000..4f091c0632 --- /dev/null +++ b/platform/mcu/xr871/project/common/cmd/cmd_ping.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _CMD_PING_H_ +#define _CMD_PING_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +enum cmd_status cmd_ping_exec(char *cmd); + +#ifdef __cplusplus +} +#endif + +#endif /* _CMD_PING_H_ */ diff --git a/platform/mcu/xr871/project/common/cmd/cmd_pm.c b/platform/mcu/xr871/project/common/cmd/cmd_pm.c new file mode 100644 index 0000000000..082423e8b3 --- /dev/null +++ b/platform/mcu/xr871/project/common/cmd/cmd_pm.c @@ -0,0 +1,161 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "cmd_util.h" +#include "cmd_pm.h" +#include "pm/pm.h" +#include "driver/chip/hal_wakeup.h" + +#ifdef CONFIG_PM +/* pm config l= d= + * : TEST_NONE ~ TEST_DEVICES. + * : based on ms. + */ +static enum cmd_status cmd_pm_config_exec(char *cmd) +{ + int32_t cnt; + uint32_t level, delayms; + + cnt = cmd_sscanf(cmd, "l=%d d=%d", &level, &delayms); + if (cnt != 2 || level > __TEST_AFTER_LAST || delayms > 100) { + CMD_ERR("err cmd:%s, expect: l= d=\n", cmd); + return CMD_STATUS_INVALID_ARG; + } + + pm_set_test_level(level); + pm_set_debug_delay_ms(delayms); + + return CMD_STATUS_OK; +} + +/* pm wk_timer + * : seconds. + */ +static enum cmd_status cmd_pm_wakeuptimer_exec(char *cmd) +{ + int32_t cnt; + uint32_t wakeup_time; + + cnt = cmd_sscanf(cmd, "%d", &wakeup_time); + if (cnt != 1 || wakeup_time < 1) { + CMD_ERR("err cmd:%s, expect: \n", cmd); + return CMD_STATUS_INVALID_ARG; + } + + HAL_Wakeup_SetTimer_mS(wakeup_time * 1000); + + return CMD_STATUS_OK; +} + +/* pm wk_io p= m= + * : 0 ~ 9 + * : 0: negative edge, 1: positive edge, 2: disable this port as wakeup io. + */ +static enum cmd_status cmd_pm_wakeupio_exec(char *cmd) +{ + int32_t cnt; + uint32_t io_num, mode; + + cnt = cmd_sscanf(cmd, "p=%d m=%d", &io_num, &mode); + if (cnt != 2 || io_num > 9 || mode > 2) { + CMD_ERR("err cmd:%s, expect: p= m=\n", cmd); + return CMD_STATUS_INVALID_ARG; + } + + if (mode < 2) { + HAL_Wakeup_SetIO(io_num, mode); + } else { + HAL_Wakeup_ClrIO(io_num); + } + + return CMD_STATUS_OK; +} + +/* pm wk_event + */ +static enum cmd_status cmd_pm_wakeupevent_exec(char *cmd) +{ + uint32_t event; + + event = HAL_Wakeup_GetEvent(); + CMD_LOG(1, "wakeup event:%x\n", event); + + return CMD_STATUS_OK; +} + +static enum cmd_status cmd_pm_sleep_exec(char *cmd) +{ + pm_enter_mode(PM_MODE_SLEEP); + + return CMD_STATUS_OK; +} + +static enum cmd_status cmd_pm_standby_exec(char *cmd) +{ + pm_enter_mode(PM_MODE_STANDBY); + + return CMD_STATUS_OK; +} + +static enum cmd_status cmd_pm_hibernation_exec(char *cmd) +{ + pm_enter_mode(PM_MODE_HIBERNATION); + + return CMD_STATUS_OK; +} + +static enum cmd_status cmd_pm_poweroff_exec(char *cmd) +{ + pm_enter_mode(PM_MODE_POWEROFF); + + return CMD_STATUS_OK; +} + +static struct cmd_data g_pm_cmds[] = { + { "config", cmd_pm_config_exec }, + { "wk_timer", cmd_pm_wakeuptimer_exec }, + { "wk_io", cmd_pm_wakeupio_exec }, + { "wk_event", cmd_pm_wakeupevent_exec }, + { "sleep", cmd_pm_sleep_exec }, + { "standby", cmd_pm_standby_exec }, + { "hibernation", cmd_pm_hibernation_exec }, + { "poweroff", cmd_pm_poweroff_exec }, + { "shutdown", cmd_pm_poweroff_exec }, +}; + +enum cmd_status cmd_pm_exec(char *cmd) +{ + return cmd_exec(cmd, g_pm_cmds, cmd_nitems(g_pm_cmds)); +} +#else /* CONFIG_PM */ +enum cmd_status cmd_pm_exec(char *cmd) +{ + return CMD_STATUS_FAIL; +} +#endif /* CONFIG_PM */ diff --git a/platform/mcu/xr871/project/common/cmd/cmd_pm.h b/platform/mcu/xr871/project/common/cmd/cmd_pm.h new file mode 100644 index 0000000000..4e9ccf9b61 --- /dev/null +++ b/platform/mcu/xr871/project/common/cmd/cmd_pm.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _CMD_PM_H_ +#define _CMD_PM_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +enum cmd_status cmd_pm_exec(char *cmd); + +#ifdef __cplusplus +} +#endif + +#endif /* _CMD_PM_H_ */ diff --git a/platform/mcu/xr871/project/common/cmd/cmd_pwm.c b/platform/mcu/xr871/project/common/cmd/cmd_pwm.c new file mode 100644 index 0000000000..116b841fc4 --- /dev/null +++ b/platform/mcu/xr871/project/common/cmd/cmd_pwm.c @@ -0,0 +1,704 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "cmd_util.h" +#include "cmd_pwm.h" +#include "driver/chip/hal_gpio.h" +#include "driver/chip/hal_pwm.h" + +typedef enum { + PWM_COMPLE, + PWM_PLUSE, + PWM_CYCLE, + PWM_CAPTURE, + PWM_DEADZONE, + PWM_MODENUM, +}Cmd_PwmMode; + +typedef struct { + uint32_t high_time; + uint32_t low_time; + uint16_t num; + uint16_t count; + uint8_t d_value; + uint8_t d_hz; + uint8_t input_ch; +} Cmd_Capinfo; + +static Cmd_Capinfo private_cap[8]; + +static Cmd_PwmMode cmd_pwm_mode_analytic(char *data) +{ + if (cmd_strcmp(data, "comple") == 0) { + return PWM_COMPLE; + } else if (cmd_strcmp(data, "pluse") == 0) { + return PWM_PLUSE; + } else if (cmd_strcmp(data, "cycle") == 0) { + return PWM_CYCLE; + } else if (cmd_strcmp(data, "capture") == 0) { + return PWM_CAPTURE; + } else if (cmd_strcmp(data, "deadzone") == 0) { + return PWM_DEADZONE; + } + return PWM_MODENUM; +} + +typedef struct { + PWM_GROUP_ID group; + PWM_CH_ID ch; +}PWM_IoInfo; + +static int cmd_pwm_init(Cmd_PwmMode mode, PWM_IoInfo info, uint32_t hz) +{ + HAL_Status ret = HAL_ERROR; + PWM_ClkParam clk_cfg; + PWM_CompInitParam comp_cfg; + PWM_ChInitParam ch_cfg; + int cycle = 0; + + clk_cfg.clk = PWM_CLK_HOSC; + clk_cfg.div = PWM_SRC_CLK_DIV_1; + + ret = HAL_PWM_GroupClkCfg(info.group, &clk_cfg); + if (ret != HAL_OK) + return -1; + + switch(mode) { + case PWM_COMPLE: + comp_cfg.hz = hz; + comp_cfg.polarity = PWM_HIGHLEVE; + + cycle = HAL_PWM_ComplementaryInit(info.group, &comp_cfg); + if (cycle == -1) + return -1; + + ret = HAL_PWM_ComplementarySetDutyRatio(info.group, cycle / 4); + if (ret != HAL_OK) + return -1; + + break; + case PWM_CYCLE: + ch_cfg.hz = hz; + ch_cfg.mode = PWM_CYCLE_MODE; + ch_cfg.polarity = PWM_HIGHLEVE; + + cycle = HAL_PWM_ChInit(info.ch, &ch_cfg); + if (cycle == -1) + return -1; + + ret = HAL_PWM_ChSetDutyRatio(info.ch, cycle / 2); + if (ret != HAL_OK) + return -1; + + break; + case PWM_PLUSE: + ch_cfg.hz = hz; + ch_cfg.mode = PWM_PLUSE_MODE; + ch_cfg.polarity = PWM_HIGHLEVE; + + cycle = HAL_PWM_ChInit(info.ch, &ch_cfg); + if (cycle == -1) + return -1; + break; + case PWM_CAPTURE: + ch_cfg.hz = hz; + ch_cfg.mode = PWM_CAPTURE_MODE; + ch_cfg.polarity = PWM_HIGHLEVE; + + cycle = HAL_PWM_ChInit(info.ch, &ch_cfg); + if (cycle == -1) + return -1; + + break; + default : + CMD_DBG("invalid PWM mode\n"); + return -1; + } + return 0; +} + +static PWM_IoInfo cmd_pwm_channel_analytic(Cmd_PwmMode mode, uint32_t channel) +{ + PWM_IoInfo info; + if (mode == PWM_COMPLE || mode == PWM_DEADZONE) { + if (channel > 3) { + info.ch = PWM_CH_NULL; + info.group = PWM_GROUP_NULL; + return info; + } + info.group = channel; + info.ch = PWM_CH_NULL; + return info; + } else if (channel > 7) { + info.ch = PWM_CH_NULL; + info.group = PWM_GROUP_NULL; + return info; + } + + info.ch = channel; + info.group = channel / 2; + + return info; +} + +static enum cmd_status cmd_pwm_config_exec(char *cmd) +{ + uint32_t hz; + uint32_t channel; + char mode_char[8]; + int cnt; + + cnt = cmd_sscanf(cmd, "c=%u m=%s h=%u", &channel, mode_char, &hz); + + if (cnt != 3) { + return CMD_STATUS_INVALID_ARG; + } + + Cmd_PwmMode mode = cmd_pwm_mode_analytic(mode_char); + if (mode >= PWM_MODENUM) { + CMD_ERR("invalid pwm mode %u\n", mode); + return CMD_STATUS_INVALID_ARG; + } + if (hz < 2 || hz > 240000) { + CMD_ERR("pwm hz out of range %u\n", hz); + return CMD_STATUS_FAIL; + } + + PWM_IoInfo info = cmd_pwm_channel_analytic(mode, channel); + + if (info.group >= PWM_GROUP_NULL) + return CMD_STATUS_INVALID_ARG; + + if (cmd_pwm_init(mode, info, hz) == -1) + return CMD_STATUS_FAIL; + + return CMD_STATUS_OK; +} + +enum cmd_status cmd_pwm_deinit_exec(char *cmd) +{ + int cnt = 0; + uint32_t channel; + char mode_char[8]; + HAL_Status ret = HAL_ERROR; + + cnt = cmd_sscanf(cmd, "c=%u m=%s", &channel, mode_char); + if (cnt != 2) + return CMD_STATUS_INVALID_ARG; + + Cmd_PwmMode mode = cmd_pwm_mode_analytic(mode_char); + if (mode >= PWM_MODENUM) { + CMD_ERR("invalid pwm mode %u\n", mode); + return CMD_STATUS_INVALID_ARG; + } + + PWM_IoInfo info = cmd_pwm_channel_analytic(mode, channel); + if (info.group == PWM_GROUP_NULL) { + CMD_ERR("invalid pwm channel\n"); + return CMD_STATUS_INVALID_ARG; + } + + if (mode == PWM_COMPLE) { + ret = HAL_PWM_ComplementaryDeInit(info.group); + if (ret != HAL_OK) + return CMD_STATUS_FAIL; + } else { + ret = HAL_PWM_ChDeinit(info.ch); + if (ret != HAL_OK) + return CMD_STATUS_FAIL; + } + return CMD_STATUS_OK; +} + +enum cmd_status cmd_pwm_start_exec(char *cmd) +{ + HAL_Status ret; + char mode_char[8]; + uint32_t channel; + int cnt; + PWM_IrqParam irq_cfg; + + cnt = cmd_sscanf(cmd, "c=%u m=%s", &channel, mode_char); + if (cnt != 2) + return CMD_STATUS_INVALID_ARG; + + Cmd_PwmMode mode = cmd_pwm_mode_analytic(mode_char); + if (mode >= PWM_MODENUM) { + CMD_ERR("invalid pwm mode %u\n", mode); + return CMD_STATUS_INVALID_ARG; + } + + PWM_IoInfo info = cmd_pwm_channel_analytic(mode, channel); + if (info.group == PWM_GROUP_NULL) { + CMD_DBG("invalid pwm channel\n"); + return CMD_STATUS_INVALID_ARG; + } + + switch (mode) { + case PWM_COMPLE: + ret = HAL_PWM_EnableComplementary(info.group, 1); + if (ret != HAL_OK) { + CMD_ERR("Enable comple error\n"); + return CMD_STATUS_FAIL; + } + break; + case PWM_PLUSE: + ret = HAL_PWM_EnableCh(info.ch, PWM_PLUSE_MODE, 1); + if (ret != HAL_OK) { + CMD_ERR("Enable channel error\n"); + return CMD_STATUS_FAIL; + } + + ret = HAL_PWM_OutputPluse(info.ch); + if (ret != HAL_OK) { + CMD_ERR("start pluse fail, it's busy\n"); + return CMD_STATUS_FAIL; + } + break; + case PWM_CYCLE: + ret = HAL_PWM_EnableCh(info.ch, PWM_CYCLE_MODE, 1); + if (ret != HAL_OK) { + CMD_ERR("Enable channel error\n"); + return CMD_STATUS_FAIL; + } + break; + case PWM_CAPTURE: + ret = HAL_PWM_EnableCh(info.ch, PWM_CAPTURE_MODE, 1); + if (ret != HAL_OK) { + CMD_ERR("Enable channel error\n"); + return CMD_STATUS_FAIL; + } + + irq_cfg.arg = NULL; + irq_cfg.callback = NULL; + irq_cfg.event = PWM_IRQ_BOTHEDGE; + + ret = HAL_PWM_EnableIRQ(info.ch, &irq_cfg); + if (ret != HAL_OK) { + CMD_ERR("Enable irq error\n"); + return CMD_STATUS_FAIL; + } + break; + case PWM_DEADZONE: + ret = HAL_PWM_EnableDeadZone(info.group, 1); + if (ret != HAL_OK) + CMD_ERR("Enable dead zone error\n"); + return CMD_STATUS_FAIL; + + break; + default: + CMD_DBG("invalid pwm mode %u\n", mode); + return CMD_STATUS_INVALID_ARG; + } + return CMD_STATUS_OK; +} + +enum cmd_status cmd_pwm_set_exec(char *cmd) +{ + uint32_t value; + uint32_t channel; + char function[10]; + int cnt; + PWM_IoInfo info; + cnt = cmd_sscanf(cmd, "c=%u m=%s v=%u", &channel, function, &value); + + if (cnt != 3) + return CMD_STATUS_INVALID_ARG; + + if (cmd_strcmp(function, "comple") == 0 || + cmd_strcmp(function, "deadzone") == 0 ) + info = cmd_pwm_channel_analytic(PWM_COMPLE, channel); + else + info = cmd_pwm_channel_analytic(PWM_CYCLE, channel); + + if (info.group == PWM_GROUP_NULL) { + CMD_ERR("invalid pwm channel\n"); + return CMD_STATUS_INVALID_ARG; + } + + if (cmd_strcmp(function, "deadzone") == 0) { + if (value > 255) { + CMD_ERR("deadzone value out of range\n"); + return CMD_STATUS_INVALID_ARG; + } + HAL_PWM_SetDeadZoneTime(info.group, value); + } else if (cmd_strcmp(function, "comple") == 0) { + __IO uint32_t *reg; + + if (value > 0) { + info.ch = info.group * 2; + + reg = &PWM->CH_REG[info.ch].PPR; + value = value * ((*reg & PWM_PPR_ENTIER_CYCLE) >> 16) / 10000; + if (value <= 1) + value = 2; + } + + HAL_PWM_ComplementarySetDutyRatio(info.group, value); + } else if (cmd_strcmp(function, "cycle") == 0) { + __IO uint32_t *reg; + + if (value > 0) { + reg = &PWM->CH_REG[info.ch].PPR; + value = value * ((*reg & PWM_PPR_ENTIER_CYCLE) >> 16) / 10000; + if (value <= 1) + value = 2; + } + + HAL_PWM_ChSetDutyRatio(info.ch, value); + } else if (cmd_strcmp(function, "pluse") == 0) { + __IO uint32_t *reg; + + if (value > 0) { + reg = &PWM->CH_REG[info.ch].PPR; + value = value * ((*reg & PWM_PPR_ENTIER_CYCLE) >> 16) / 10000; + if (value <= 1) + value = 2; + } + + HAL_PWM_ChSetDutyRatio(info.ch, value); + } else + return CMD_STATUS_INVALID_ARG; + + return CMD_STATUS_OK; +} + + +static int PWM_DeadTime(uint8_t chGroup) +{ + + if (chGroup >= PWM_GROUP_NUM) + return -1; + + uint32_t p = PWM->PDZCR[chGroup]; + p &= PWM_CH_DZ_INV; + return (p >> 8); +} + +static int PWM_ReadActCycle(uint8_t ch) +{ + if (ch >= PWM_CH_NUM) + return -1; + int p = (int)PWM->CH_REG[ch].PPR; + return p; +} + +static PWM_Polarity _PWM_Polarity(uint8_t ch) +{ + if ((PWM->CH_REG[ch].PCR & PWM_PCR_ACT_STA) > 0) + return PWM_HIGHLEVE; + + return PWM_LOWLEVE; + +} + +static Cmd_PwmMode PWM_ChMode(uint8_t ch) +{ + __IO uint32_t *reg = NULL; + reg = &PWM->CH_REG[ch].PCR; + + if ((*reg & PWM_PCR_MODE) > 0) + return PWM_PLUSE_MODE; + + return PWM_CYCLE_MODE; +} + +int PWM_DeadZoneEnable(PWM_GROUP_ID chGroup) +{ + if (chGroup >= PWM_GROUP_NUM) + return -1; + + return PWM->PDZCR[chGroup]&PWM_CH_DZ_EN; +} + + +#define CAPTURE_TASK_THREAD_STACK_SIZE (2 * 1024) +static OS_Thread_t g_capture_thread_t; + +void cmd_capture_task(void *arg) +{ + int i = 0; + + if (PWM->CIER == 0) { + OS_ThreadDelete(&g_capture_thread_t); + return; + } + + PWM_CapResult result; + + while (PWM->CIER) { + Cmd_Capinfo *info = &private_cap[i]; + + if (info->count < info->num) { + while (info->count < info->num) { + if ( PWM_ChMode(info->input_ch) == PWM_PLUSE) + result = HAL_PWM_CaptureResult(PWM_CAP_PLUSE, i); + else + result = HAL_PWM_CaptureResult(PWM_CAP_CYCLE, i); + + if (result.highLevelTime) { + info->count += 1; + if (info->count == 1) + continue; + + info->high_time += result.highLevelTime; + info->low_time += result.lowLevelTime; + } + } + } else + OS_MSleep(10); + + i ++; + if (i >= 8) + i = 0; + } + OS_ThreadDelete(&g_capture_thread_t); +} + +enum cmd_status cmd_pwm_get_exec(char *cmd) +{ + uint32_t channel; + uint32_t src_signal; + char function[8]; + uint32_t num = 0; + uint32_t value_deviation = 0; + uint32_t hz_deviation = 0; + int cnt; + + cnt = cmd_sscanf(cmd, "c=%u m=%s n=%u input_ch=%u dv=%u dh=%u", &channel, function, &num, + &src_signal, &value_deviation, &hz_deviation); + if (cnt != 6) { + CMD_ERR("miss param, %d\n", cnt); + return CMD_STATUS_INVALID_ARG; + } + + if (num > 1000) { + CMD_ERR("The n value out of range\n"); + return CMD_STATUS_INVALID_ARG; + } + + if (value_deviation > 100 || hz_deviation > 100) { + CMD_ERR("The value out of range\n"); + return CMD_STATUS_FAIL; + } + + PWM_IoInfo info; + info = cmd_pwm_channel_analytic(PWM_CYCLE, src_signal); + if (info.group== PWM_GROUP_NULL) { + CMD_ERR("invalid src_signal\n"); + return CMD_STATUS_INVALID_ARG; + } + + info = cmd_pwm_channel_analytic(PWM_CAPTURE, channel); + if (info.group== PWM_GROUP_NULL) { + CMD_ERR("invalid ch \n"); + return CMD_STATUS_INVALID_ARG; + } + Cmd_Capinfo *cap_info = private_cap; + + cap_info[channel].num = num; + cap_info[channel].count = 0; + cap_info[channel].d_hz = hz_deviation; + cap_info[channel].d_value = value_deviation; + cap_info[channel].input_ch = src_signal; + cap_info[channel].high_time = 0; + cap_info[channel].low_time = 0; + + if (!OS_ThreadIsValid(&g_capture_thread_t)) { + if (OS_ThreadCreate(&g_capture_thread_t, + "", + cmd_capture_task, + NULL, + OS_PRIORITY_ABOVE_NORMAL, + CAPTURE_TASK_THREAD_STACK_SIZE) != OS_OK) { + CMD_ERR("create sys ctrl task failed\n"); + } + } + + return CMD_STATUS_OK; +} + + +typedef enum { + CAP_ERROR, + CAP_BUSY, + CAP_OK, +}CAP_RESULT; + +CAP_RESULT cmd_CapResult(uint8_t ch) { + CAP_RESULT ret; + + uint16_t input_right_period = 0, input_right_h_time = 0; + uint8_t deadzone_time = 0; + Cmd_Capinfo *info = &private_cap[ch]; + + if (PWM_DeadZoneEnable(info->input_ch/ 2)) + deadzone_time = PWM_DeadTime(info->input_ch / 2); + + if (info->count == info->num) { + uint32_t cap_period = 0; + uint32_t cap_h_time = 0; + + __IO uint32_t *reg = &PWM->CH_REG[info->input_ch].PPR; + + input_right_period = (*reg & PWM_PPR_ENTIER_CYCLE) >> 16; + input_right_h_time = PWM_ReadActCycle(info->input_ch); + + if (_PWM_Polarity(info->input_ch)== PWM_LOWLEVE) + input_right_h_time = input_right_period- input_right_h_time; + + if (deadzone_time > 0) { + input_right_h_time -= deadzone_time; + } + + cap_h_time = info->high_time / (info->num - 1); + cap_period = cap_h_time + info->low_time / (info->num - 1); + + if ( PWM_ChMode(info->input_ch) == PWM_CYCLE) { + if (abs(cap_h_time - input_right_h_time) <= info->d_value && + abs(cap_period - input_right_period) <= info->d_hz) + ret = CAP_OK; + else + ret = CAP_ERROR; + } else { + if (abs(cap_h_time - input_right_h_time) <= info->d_value) + ret = CAP_OK; + else + ret = CAP_ERROR; + } + + memset(info, 0, sizeof(Cmd_Capinfo)); + + return ret; + } + + return CAP_BUSY; +} + + +enum cmd_status cmd_pwm_get_result_exec(char *cmd) +{ + uint32_t channel; + int cnt; + + cnt = cmd_sscanf(cmd, "c=%u ", &channel); + if (cnt != 1) + return CMD_STATUS_INVALID_ARG; + + PWM_IoInfo info = cmd_pwm_channel_analytic(PWM_CAPTURE, channel); + + if (info.group== PWM_GROUP_NULL) { + CMD_ERR("invalid pwm channel\n"); + return CMD_STATUS_INVALID_ARG; + } + + CAP_RESULT ret = cmd_CapResult(channel); + + if (ret == CAP_BUSY) { + CMD_ERR("PWM%d Capture BUSY\n", info.ch); + return CMD_STATUS_ERROR_MIN; + } else if (ret == CAP_ERROR) { + CMD_ERR("PWM%d Capture ERROR\n",info.ch); + return CMD_STATUS_FAIL; + } else if (ret == CAP_OK) { + return CMD_STATUS_OK; + } + + return CMD_STATUS_OK; +} + +enum cmd_status cmd_pwm_stop_exec(char *cmd) +{ + char mode_char[8]; + uint32_t channel; + int cnt; + + cnt = cmd_sscanf(cmd, "c=%u m=%s", &channel, mode_char); + if (cnt != 2) + return CMD_STATUS_INVALID_ARG; + + Cmd_PwmMode mode = cmd_pwm_mode_analytic(mode_char); + if (mode >= PWM_MODENUM) { + CMD_ERR("invalid pwm mode %u\n", mode); + return CMD_STATUS_INVALID_ARG; + } + + PWM_IoInfo info = cmd_pwm_channel_analytic(mode, channel); + if (info.group== PWM_GROUP_NULL) { + CMD_ERR("invalid pwm channel\n"); + return CMD_STATUS_INVALID_ARG; + } + + + + switch(mode) { + case PWM_COMPLE: + HAL_PWM_EnableComplementary(info.group, 0); + HAL_PWM_SetDeadZoneTime(info.group, 0); + HAL_PWM_EnableDeadZone(info.group, 0); + break; + case PWM_PLUSE: + HAL_PWM_EnableCh(info.ch, PWM_PLUSE_MODE, 0); + break; + case PWM_CYCLE: + HAL_PWM_EnableCh(info.ch, PWM_CYCLE_MODE, 0); + break; + case PWM_CAPTURE: + HAL_PWM_EnableCh(info.ch, PWM_CAPTURE_MODE, 0); + HAL_PWM_DisableIRQ(info.ch); + break; + case PWM_DEADZONE: + HAL_PWM_SetDeadZoneTime(info.group, 0); + HAL_PWM_EnableDeadZone(info.group, 0); + break; + default : + CMD_ERR("invalid pwm mode\n"); + return CMD_STATUS_INVALID_ARG; + } + return CMD_STATUS_OK; +} + +/* + * driver commands + */ +static struct cmd_data g_pwm_cmds[] = { + { "deinit", cmd_pwm_deinit_exec }, + { "config", cmd_pwm_config_exec }, + { "start", cmd_pwm_start_exec }, + { "set", cmd_pwm_set_exec }, + { "get", cmd_pwm_get_exec }, + { "get_result", cmd_pwm_get_result_exec }, + { "stop", cmd_pwm_stop_exec }, +}; + +enum cmd_status cmd_pwm_exec(char *cmd) +{ + return cmd_exec(cmd, g_pwm_cmds, cmd_nitems(g_pwm_cmds)); +} + diff --git a/platform/mcu/xr871/project/common/cmd/cmd_pwm.h b/platform/mcu/xr871/project/common/cmd/cmd_pwm.h new file mode 100644 index 0000000000..e6f3d21909 --- /dev/null +++ b/platform/mcu/xr871/project/common/cmd/cmd_pwm.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _CMD_PWM_H_ +#define _CMD_PWM_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +enum cmd_status cmd_pwm_exec(char *cmd); + +#ifdef __cplusplus +} +#endif + +#endif /* _CMD_PWM_H_ */ diff --git a/platform/mcu/xr871/project/common/cmd/cmd_rtc.c b/platform/mcu/xr871/project/common/cmd/cmd_rtc.c new file mode 100644 index 0000000000..e25a6081c2 --- /dev/null +++ b/platform/mcu/xr871/project/common/cmd/cmd_rtc.c @@ -0,0 +1,282 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "cmd_util.h" +#include "cmd_rtc.h" +#include "driver/chip/hal_rtc.h" +#include + +#define CMD_YEAR0 1900 /* the first year for struct tm::tm_year */ +#define CMD_IS_LEAP_YEAR(year) (!((year) % 4) && (((year) % 100) || !((year) % 400))) + +/* due to time_t is singed long, we will get problems on 2038-01-19 03:14:08 */ +#define CMD_YEAR_MIN 1970 +#define CMD_YEAR_MAX 2037 + +#define CMD_WDAY_TM2RTC(wday) (((wday) + 6) % 7) +#define CMD_WDAY_RTC2TM(wday) (((wday) + 1) % 7) + +enum cmd_rtc_alarm { + CMD_RTC_ALARM_SECOND = 0, + CMD_RTC_ALARM_WDAY +}; + +const int8_t g_cmd_mday[2][12] = { + { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }, /* normal year */ + { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 } /* leap year */ +}; + +const char *g_cmd_wday_str[] = { + "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" +}; + +/* + * drv rtc set + */ +static enum cmd_status cmd_rtc_set_exec(char *cmd) +{ + int year, month, mday, hour, minute, second; + int32_t cnt; + struct tm t; + time_t clock; + int leap; + + cnt = cmd_sscanf(cmd, "%d-%d-%d,%d:%d:%d", + &year, &month, &mday, &hour, &minute, &second); + if (cnt != 6) { + return CMD_STATUS_INVALID_ARG; + } + + if (year < CMD_YEAR_MIN || year > CMD_YEAR_MAX) { + CMD_ERR("invalid year %u\n", year); + return CMD_STATUS_INVALID_ARG; + } + leap = CMD_IS_LEAP_YEAR(year); + + if (month < 1 || month > 12) { + CMD_ERR("invalid month %u\n", month); + return CMD_STATUS_INVALID_ARG; + } + month -= 1; + + if (mday < 1 || mday > g_cmd_mday[leap][month]) { + CMD_ERR("invalid mday %u\n", mday); + return CMD_STATUS_INVALID_ARG; + } + + if (hour < 0 || hour > 23) { + CMD_ERR("invalid hour %u\n", hour); + return CMD_STATUS_INVALID_ARG; + } + + if (minute < 0 || minute > 59) { + CMD_ERR("invalid minute %u\n", minute); + return CMD_STATUS_INVALID_ARG; + } + + if (second < 0 || second > 59) { + CMD_ERR("invalid second %u\n", second); + return CMD_STATUS_INVALID_ARG; + } + + cmd_memset(&t, 0, sizeof(t)); + t.tm_year = year - CMD_YEAR0; + t.tm_mon = month; + t.tm_mday = mday; + t.tm_hour = hour; + t.tm_min = minute; + t.tm_sec = second; + clock = mktime(&t); + gmtime_r(&clock, &t); + + t.tm_mon += 1; + HAL_RTC_SetYYMMDD(leap, t.tm_year, t.tm_mon, t.tm_mday); + HAL_RTC_SetDDHHMMSS(CMD_WDAY_TM2RTC(t.tm_wday), t.tm_hour, t.tm_min, t.tm_sec); + + CMD_DBG("%d-%02d-%02d %s %02d:%02d:%02d, leap %d\n", + t.tm_year + CMD_YEAR0, t.tm_mon, t.tm_mday, + g_cmd_wday_str[t.tm_wday], t.tm_hour, t.tm_min, t.tm_sec, leap); + + return CMD_STATUS_OK; +} + +/* + * drv rtc get + */ +static enum cmd_status cmd_rtc_get_exec(char *cmd) +{ + uint8_t leap, year, month, mday, hour, minute, second; + RTC_WeekDay wday; + + HAL_RTC_GetYYMMDD(&leap, &year, &month, &mday); + HAL_RTC_GetDDHHMMSS(&wday, &hour, &minute, &second); + + cmd_write_respond(CMD_STATUS_OK, "%d-%02d-%02d %s %02d:%02d:%02d", + year + CMD_YEAR0, month, mday, + g_cmd_wday_str[CMD_WDAY_RTC2TM(wday)], + hour, minute, second); + + return CMD_STATUS_ACKED; +} + +static void cmd_rtc_alarm_callback(void *arg) +{ + uint8_t leap, year, month, mday, hour, minute, second; + RTC_WeekDay wday; + enum cmd_rtc_alarm alarm = (enum cmd_rtc_alarm)arg; + + HAL_RTC_GetYYMMDD(&leap, &year, &month, &mday); + HAL_RTC_GetDDHHMMSS(&wday, &hour, &minute, &second); + + cmd_write_event(CMD_EVENT_RTC_NOTIFY, "rtc %s-alarm notify at " + "%d-%02d-%02d %s %02d:%02d:%02d", + alarm == CMD_RTC_ALARM_SECOND ? "sec" : "wday", + year + CMD_YEAR0, month, mday, + g_cmd_wday_str[CMD_WDAY_RTC2TM(wday)], + hour, minute, second); +} + +/* + * drv rtc alarm sec start + */ +static enum cmd_status cmd_rtc_sec_alarm_start_exec(char *cmd) +{ + uint32_t second; + int32_t cnt; + RTC_SecAlarmStartParam param; + + cnt = cmd_sscanf(cmd, "%u", &second); + if (cnt != 1) { + return CMD_STATUS_INVALID_ARG; + } + + param.alarmSeconds = second; + param.callback = cmd_rtc_alarm_callback; + param.arg = (void *)CMD_RTC_ALARM_SECOND; + HAL_RTC_StartSecAlarm(¶m); + return CMD_STATUS_OK; +} + +/* + * drv rtc alarm sec stop + */ +static enum cmd_status cmd_rtc_sec_alarm_stop_exec(char *cmd) +{ + HAL_RTC_StopSecAlarm(); + return CMD_STATUS_OK; +} + +/* + * drv rtc alarm wday start ... + */ +static enum cmd_status cmd_rtc_wday_alarm_start_exec(char *cmd) +{ + int argc, i, j; + char *argv[9]; + uint32_t hour, minute, second; + uint8_t wday_mask; + int32_t cnt; + RTC_WDayAlarmStartParam param; + + argc = cmd_parse_argv(cmd, argv, 9); + if (argc < 2 || argc > 8) { + return CMD_STATUS_INVALID_ARG; + } + + cnt = cmd_sscanf(argv[0], "%d:%d:%d", &hour, &minute, &second); + if (cnt != 3) { + return CMD_STATUS_INVALID_ARG; + } + + if (hour < 0 || hour > 23) { + CMD_ERR("invalid hour %u\n", hour); + return CMD_STATUS_INVALID_ARG; + } + + if (minute < 0 || minute > 59) { + CMD_ERR("invalid minute %u\n", minute); + return CMD_STATUS_INVALID_ARG; + } + + if (second < 0 || second > 59) { + CMD_ERR("invalid second %u\n", second); + return CMD_STATUS_INVALID_ARG; + } + + wday_mask = 0; + for (i = 1; i < argc; ++i) { + for (j = 0; j < cmd_nitems(g_cmd_wday_str); ++j) { + if (cmd_strcmp(argv[i], g_cmd_wday_str[j]) == 0) { + wday_mask |= RTC_WDAY_ALARM_EN_BIT(CMD_WDAY_TM2RTC(j)); + break; + } + } + if (j >= cmd_nitems(g_cmd_wday_str)) { + wday_mask = 0; + break; /* Invalid week day */ + } + } + + if (wday_mask == 0) { + CMD_ERR("invalid week days\n"); + return CMD_STATUS_INVALID_ARG; + } + + param.alarmHour = hour; + param.alarmMinute = minute; + param.alarmSecond = second; + param.alarmWDayMask = wday_mask; + param.callback = cmd_rtc_alarm_callback; + param.arg = (void *)CMD_RTC_ALARM_WDAY; + HAL_RTC_StartWDayAlarm(¶m); + return CMD_STATUS_OK; +} + +/* + * drv rtc alarm wday stop + */ +static enum cmd_status cmd_rtc_wday_alarm_stop_exec(char *cmd) +{ + HAL_RTC_StopWDayAlarm(); + return CMD_STATUS_OK; +} + +static struct cmd_data g_rtc_cmds[] = { + { "set", cmd_rtc_set_exec }, + { "get", cmd_rtc_get_exec }, + { "sec-alarm-start", cmd_rtc_sec_alarm_start_exec }, + { "sec-alarm-stop", cmd_rtc_sec_alarm_stop_exec}, + { "wday-alarm-start", cmd_rtc_wday_alarm_start_exec }, + { "wday-alarm-stop", cmd_rtc_wday_alarm_stop_exec }, +}; + +enum cmd_status cmd_rtc_exec(char *cmd) +{ + return cmd_exec(cmd, g_rtc_cmds, cmd_nitems(g_rtc_cmds)); +} diff --git a/platform/mcu/xr871/project/common/cmd/cmd_rtc.h b/platform/mcu/xr871/project/common/cmd/cmd_rtc.h new file mode 100644 index 0000000000..72e4160842 --- /dev/null +++ b/platform/mcu/xr871/project/common/cmd/cmd_rtc.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _CMD_RTC_H_ +#define _CMD_RTC_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +enum cmd_status cmd_rtc_exec(char *cmd); + +#ifdef __cplusplus +} +#endif + +#endif /* _CMD_RTC_H_ */ diff --git a/platform/mcu/xr871/project/common/cmd/cmd_sd.c b/platform/mcu/xr871/project/common/cmd/cmd_sd.c new file mode 100644 index 0000000000..1366383c82 --- /dev/null +++ b/platform/mcu/xr871/project/common/cmd/cmd_sd.c @@ -0,0 +1,565 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include "sys/xr_debug.h" + +#include "cmd_util.h" +#include "cmd_sd.h" + +#include "driver/chip/hal_def.h" +#include "driver/chip/sdmmc/hal_sdhost.h" +#include "driver/chip/sdmmc/sdmmc.h" + +static struct mmc_card *card; + +extern int32_t mmc_test(uint32_t cd_mode); +extern int32_t mmc_test_init(void); +extern int32_t mmc_test_exit(void); +struct mmc_card *mmc_scan_init(void); + +/* + * drv sd init + */ +static enum cmd_status cmd_sd_init_exec(char *cmd) +{ + mmc_test_init(); + + return CMD_STATUS_OK; +} + +/* + * drv sd scan + */ +static enum cmd_status cmd_sd_scan_exec(char *cmd) +{ + card = mmc_scan_init(); + if (!card) { + CMD_ERR("scan card failed!\n"); + return CMD_STATUS_OK;; + } + + return CMD_STATUS_OK; +} + +/* + * drv sd read s= n= + */ +static enum cmd_status cmd_sd_read_exec(char *cmd) +{ + int32_t err; + uint32_t cnt; + uint32_t start_sector, r_secnum; + char *buf; + + cnt = cmd_sscanf(cmd, "s=%d n=%d", &start_sector, &r_secnum); + if (cnt != 2) { + CMD_ERR("invalid argument %s\n", cmd); + return CMD_STATUS_FAIL; + } + + buf = cmd_malloc(r_secnum*512); + if (!buf) + return CMD_STATUS_OK;; + + err = mmc_block_read(card, (uint8_t *)buf, start_sector, r_secnum); + if (err) { + CMD_ERR("mmc mult blocks read err!\n"); + return CMD_STATUS_FAIL; + } + + CMD_DBG("read from %d sector, %d sectors\n", start_sector, r_secnum); + print_hex_dump_bytes(buf, r_secnum*512); + + return CMD_STATUS_OK; +} + +/* + * drv sd test + */ +static enum cmd_status cmd_sd_test_exec(char *cmd) +{ + mmc_test(0); + + return CMD_STATUS_OK; +} + +/* + * drv sd deinit + */ +static enum cmd_status cmd_sd_deinit_exec(char *cmd) +{ + mmc_test_exit(); + + return CMD_STATUS_OK; +} + +#define CMD_ARG_LEN 64 + +struct sd_test_param { + uint8_t task_idx; + uint8_t random; + uint8_t task_num; + uint16_t time_sec; + uint32_t start_sector; + uint32_t sector_num; +}; + +static void cmd_sd_bench_task(void *arg) +{ + int32_t err; + char *cmd = (char *)arg; + uint32_t start_sector; + uint32_t cnt; + uint32_t throuth_mb, throuth_kb; + OS_Time_t tick_use; + uint32_t bench_sector; + + cnt = cmd_sscanf(cmd, "s=%d", &start_sector); + if (cnt != 1) { + CMD_ERR("invalid argument %s\n", cmd); + goto out; + } + + mmc_test_init(); + card = mmc_scan_init(); + if (!card) { + CMD_ERR("scan card failed!\n"); + goto out; + } + + bench_sector = start_sector - 1; + + for (int i = 0; i < 1000; i++) { + uint32_t bench_size = 512 * (1 << i); + uint8_t *buf = cmd_malloc(bench_size); + if (!buf) { + CMD_DBG("%s test end for malloc buff failed!\n", __func__); + goto out; + } + + for (int j = 0; j < bench_size/4; j++) + ((unsigned int *)buf)[j] = j; + + bench_sector += bench_size/512; + + tick_use = OS_GetTicks(); + err = mmc_block_write(card, buf, bench_sector, bench_size/512); + tick_use = OS_GetTicks() - tick_use; + if (!tick_use) + tick_use = 1; + if (err) { + CMD_ERR("mmc mult blocks write err!\n"); + goto next; + } else { + throuth_kb = bench_size*1000/1024/(uint32_t)OS_TicksToMSecs(tick_use); + throuth_mb = throuth_kb/1000; + CMD_DBG("%s mult blocks write ok, ", __func__); + if (bench_size <= 512) + CMD_LOG(CMD_DBG_ON, "0.5"); + else + CMD_LOG(CMD_DBG_ON, "%3d", bench_size/1024); + CMD_LOG(CMD_DBG_ON, " KB use:%3d ms, throughput:%d.%d MB/S\n", + (uint32_t)OS_TicksToMSecs(tick_use), + throuth_mb, throuth_kb - throuth_mb); + } + + for (int j = 0; j < bench_size/4; j++) + ((unsigned int *)buf)[j] = 0; + + tick_use = OS_GetTicks(); + err = mmc_block_read(card, buf, bench_sector, bench_size/512); + tick_use = OS_GetTicks() - tick_use; + if (!tick_use) + tick_use = 1; + if (err) { + CMD_ERR("mmc mult blocks read err!\n"); + goto next; + } else { + throuth_kb = bench_size*1000/1024/(uint32_t)OS_TicksToMSecs(tick_use); + throuth_mb = throuth_kb/1000; + CMD_DBG("%s mult blocks read ok, ", __func__); + if (bench_size <= 512) + CMD_LOG(CMD_DBG_ON, "0.5"); + else + CMD_LOG(CMD_DBG_ON, "%3d", bench_size/1024); + CMD_LOG(CMD_DBG_ON, " KB use:%3d ms, throughput:%d.%d MB/S\n", + (uint32_t)OS_TicksToMSecs(tick_use), + throuth_mb, throuth_kb - throuth_mb); + } + + err = 0; + for (int j = 0; j < bench_size/4; j++) { + if (((unsigned int *)buf)[j] != j) { + err = -1; + break; + } + } + if (err) { + CMD_ERR("mmc %d blocks write data err!\n", bench_size/512); + goto next; + } +next: + cmd_free(buf); + if (err) + break; + } + +out: + CMD_DBG("%s test end\n", __func__); + mmc_test_exit(); + cmd_free(arg); + OS_ThreadDelete(NULL); +} + +#define PRESS_FILE_SIZE (20 * 1024 * 1024) /* 20M */ +#define PRESS_READ_SIZE 2048 +#define PRESS_WRITE_SIZE 8192 +static OS_Semaphore_t sem_wait; + +static void cmd_sd_press_read_task(void *arg) +{ + int32_t err; + struct sd_test_param *param = (struct sd_test_param *)arg; + OS_Time_t tick_now = 0, tick_print = 0; + OS_Time_t tick_end = OS_GetTicks() + OS_MSecsToTicks(param->time_sec * 1000); + uint32_t random_sleep = param->random; + char *buf; + uint32_t start_sector = param->start_sector; + + if (param->task_idx == 0) { + buf = cmd_malloc(4096); + for (int i = 0; i < 512/4; i++) { + ((unsigned int *)buf)[i] = i; + } + memcpy(buf+512, buf, 512); + memcpy(buf+1024, buf, 1024); + memcpy(buf+2048, buf, 2048); + CMD_DBG("%s do nothing until sd card prepare ok!\n", __func__); + for (int i = 0; i < (PRESS_FILE_SIZE+4095)/4096; i++) { /* 10M */ + int32_t err; + err = mmc_block_write(card, (uint8_t *)buf, start_sector + (i*(4096/512)), 4096/512); + if (err) + goto out; + } + CMD_DBG("%s sd card prepared ok!\n", __func__); + + for (int j = 0; j < param->task_num - 1; j++) + OS_SemaphoreRelease(&sem_wait); + cmd_free(buf); + } else { + OS_SemaphoreWait(&sem_wait, OS_WAIT_FOREVER); + } + + buf = cmd_malloc(param->sector_num*512); + if (!buf) + goto exit; + + if (!random_sleep) + random_sleep = 2; + OS_MSleep(random_sleep); + CMD_DBG("%s id:%d random:%d start_sector:%d\n", __func__, param->task_idx, + random_sleep, start_sector); + + while (tick_now < tick_end) { + int j; + err = mmc_block_read(card, (uint8_t *)buf, start_sector, param->sector_num); + if (err) { + CMD_ERR("mmc mult blocks read err!\n"); + goto out; + } + err = 0; + for (j = 0; j < param->sector_num*(512/4); j++) { + if (((unsigned int *)buf)[j] != (j & 0x07f)) { + err = -1; + CMD_ERR("%x %x\n", j, ((unsigned int *)buf)[j]); + break; + } + } + if (err) { + CMD_ERR("mmc %d blocks read data err! at sector:%d count:%d\n", + param->sector_num, start_sector, j); + goto out; + } else + ;//CMD_DBG("%s mmc blocks read data ok! at sector:%d\n", + // __func__, start_sector); + start_sector += param->sector_num; + if (start_sector >= param->start_sector + PRESS_FILE_SIZE/512) { + start_sector = param->start_sector; + } + OS_MSleep(random_sleep); + tick_now = OS_GetTicks(); + if (tick_now >= tick_print + 5000) { + CMD_DBG("%s id:%d testing... at sector:%d\n", __func__, + param->task_idx, start_sector); + tick_print = tick_now; + } + } + +out: + CMD_DBG("%s id:%d test end\n", __func__, param->task_idx); + cmd_free(buf); +exit: + cmd_free(param); + if (param->task_idx == 0) + OS_SemaphoreDelete(&sem_wait); + OS_ThreadDelete(NULL); +} + +static void cmd_sd_press_write_task(void *arg) +{ + int32_t err; + struct sd_test_param *param = (struct sd_test_param *)arg; + OS_Time_t tick_now = 0, tick_print = 0; + OS_Time_t tick_end = OS_GetTicks() + OS_MSecsToTicks(param->time_sec * 1000); + uint32_t random_sleep = param->random; + char *buf; + uint32_t start_sector = param->start_sector; + uint32_t round = 0; + uint32_t total_num; + + buf = cmd_malloc(param->sector_num*512); + if (!buf) + goto out; + + for (int i = 0; i < 512/4; i++) + ((unsigned int *)buf)[i] = i; + + for (int i = 1; i < param->sector_num; i++) + memcpy(buf + i * 512, buf, 512); + + if (!random_sleep) + random_sleep = 2; + OS_MSleep(random_sleep); + CMD_DBG("%s id:%d random:%d\n", __func__, param->task_idx, random_sleep); + + while (tick_now < tick_end) { + err = mmc_block_write(card, (uint8_t *)buf, start_sector, param->sector_num); + if (err) { + CMD_ERR("mmc mult blocks write err!\n"); + goto out; + } + start_sector += param->sector_num; + if (start_sector >= param->start_sector + PRESS_FILE_SIZE/512) { + start_sector = param->start_sector; + round = 1; + } + OS_MSleep(random_sleep); + tick_now = OS_GetTicks(); + if (tick_now >= tick_print + 5000) { + CMD_DBG("%s id:%d testing... at sector:%d\n", __func__, + param->task_idx, start_sector); + tick_print = tick_now; + } + } + CMD_DBG("%s test end. round:%d start_sector:%d end_sector:%d\n", __func__, + round, param->start_sector, start_sector); + + if (round) { + total_num = PRESS_FILE_SIZE/512; + } else { + // BUG_ON(start_sector <= param->start_sector); + total_num = start_sector - param->start_sector; + } + total_num /= param->sector_num; + + start_sector = param->start_sector; + CMD_DBG("%s test checking... start_sector:%d total_num:%d\n", __func__, + start_sector, total_num); + for (int i = 0; i < total_num; i++) { + int j; + + memset(buf, 0, param->sector_num*512); + err = mmc_block_read(card, (uint8_t *)buf, start_sector, param->sector_num); + if (err) { + CMD_ERR("mmc mult blocks read err!\n"); + goto out; + } + err = 0; + for (j = 0; j < param->sector_num*(512/4); j++) { + if (((unsigned int *)buf)[j] != (j & 0x07f)) { + err = -1; + break; + } + } + if (err) { + CMD_ERR("%x %x\n", ((unsigned int *)buf)[j], (j & 0x07f)); + CMD_ERR("mmc %d blocks write data err! at sector:%d count:%d\n", + param->sector_num, start_sector, j); + goto out; + } else + ;//CMD_DBG("%s mmc blocks write data ok! at sector:%d\n", + // __func__, start_sector); + start_sector += param->sector_num; + } + +out: + CMD_DBG("%s id:%d test end\n", __func__, param->task_idx); + cmd_free(buf); + cmd_free(param); + OS_ThreadDelete(NULL); +} + +/* + * drv sd bench s= + */ +static enum cmd_status cmd_sd_bench_exec(char *cmd) +{ + OS_Thread_t thread; + char *param; + uint32_t len; + + len = strlen(cmd); + if (len >= CMD_ARG_LEN) { + CMD_ERR("should adjust CMD_ARG_LEN to %d\n", len); + return CMD_STATUS_FAIL; + } + + param = cmd_malloc(CMD_ARG_LEN); + if (!param) + return CMD_STATUS_FAIL; + memcpy(param, cmd, len); + param[len+1] = 0; + + OS_ThreadSetInvalid(&thread); + if (OS_ThreadCreate(&thread, + "", + cmd_sd_bench_task, + param, + OS_THREAD_PRIO_APP, + 2 * 1024) != OS_OK) { + CMD_ERR("create sd bench test task failed\n"); + return CMD_STATUS_FAIL; + } + + return CMD_STATUS_OK; +} + +/* + * drv sd press r= s= n= w= + * s= n= t= + */ +static enum cmd_status cmd_sd_press_exec(char *cmd) +{ + OS_Thread_t thread; + struct sd_test_param *param; + uint32_t r_threads, w_threads; + uint32_t r_secnum, w_secnum; + uint32_t cnt; + uint32_t time_sec; + uint32_t start_rsector, start_wsector; + + cnt = cmd_sscanf(cmd, "r=%d s=%d n=%d w=%d s=%d n=%d t=%d", + &r_threads, &start_rsector, &r_secnum, + &w_threads, &start_wsector, &w_secnum, &time_sec); + if (cnt != 7) { + CMD_ERR("invalid argument %s\n", cmd); + return CMD_STATUS_FAIL; + } + + OS_SemaphoreCreate(&sem_wait, 0, OS_SEMAPHORE_MAX_COUNT); + + OS_MSleep(5); + + for (uint32_t i = 0; i < r_threads; i++) { + param = cmd_malloc(sizeof(struct sd_test_param)); + if (!param) + return CMD_STATUS_FAIL; + param->task_idx = i; + param->task_num = r_threads; + param->start_sector = start_rsector; + param->sector_num = r_secnum; + param->time_sec = time_sec; + param->random = rand() % 8 + i; + if (!r_secnum || time_sec < 2) { + CMD_ERR("%s read n= should not 0 !\n", __func__); + cmd_free(param); + goto out; + } + OS_ThreadSetInvalid(&thread); + if (OS_ThreadCreate(&thread, + "", + cmd_sd_press_read_task, + param, + OS_THREAD_PRIO_APP, + 2 * 1024) != OS_OK) { + CMD_ERR("create sd bench test task failed\n"); + return CMD_STATUS_FAIL; + } + (void)cmd_sd_press_read_task; + OS_MSleep(2); + } + + for (uint32_t i = 0; i < w_threads; i++) { + param = cmd_malloc(sizeof(struct sd_test_param)); + if (!param) + return CMD_STATUS_FAIL; + param->task_idx = i; + param->task_num = w_threads; + param->start_sector = start_wsector; + param->sector_num = w_secnum; + param->time_sec = time_sec; + param->random = rand() % 20 + i; + if (!w_secnum || time_sec < 2) { + CMD_ERR("%s write n= should not 0 !\n", __func__); + cmd_free(param); + goto out; + } + OS_ThreadSetInvalid(&thread); + if (OS_ThreadCreate(&thread, + "", + cmd_sd_press_write_task, + param, + OS_THREAD_PRIO_APP, + 2 * 1024) != OS_OK) { + CMD_ERR("create sd bench test task failed\n"); + return CMD_STATUS_FAIL; + } + OS_MSleep(2); + } + +out: + return CMD_STATUS_OK; +} + +static struct cmd_data g_sd_cmds[] = { + { "init", cmd_sd_init_exec }, + { "deinit", cmd_sd_deinit_exec }, + { "scan", cmd_sd_scan_exec }, + { "read", cmd_sd_read_exec }, + { "test", cmd_sd_test_exec }, + { "bench", cmd_sd_bench_exec }, + { "press", cmd_sd_press_exec }, +}; + +enum cmd_status cmd_sd_exec(char *cmd) +{ + return cmd_exec(cmd, g_sd_cmds, cmd_nitems(g_sd_cmds)); +} diff --git a/platform/mcu/xr871/project/common/cmd/cmd_sd.h b/platform/mcu/xr871/project/common/cmd/cmd_sd.h new file mode 100644 index 0000000000..11a96bf089 --- /dev/null +++ b/platform/mcu/xr871/project/common/cmd/cmd_sd.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _CMD_SD_H_ +#define _CMD_SD_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/* example: + * 1. init sd use: drv sd config p=invert + * use "p=none" when tx rx gpio direct connect in test mode. + * 2. get received addr and key use: drv sd value 0 + * 3. deinit sd use: drv sd deconfig 0 + */ + +enum cmd_status cmd_sd_exec(char *cmd); + +#ifdef __cplusplus +} +#endif + +#endif /* _CMD_SD_H_ */ diff --git a/platform/mcu/xr871/project/common/cmd/cmd_smart_config.c b/platform/mcu/xr871/project/common/cmd/cmd_smart_config.c new file mode 100644 index 0000000000..cda779a185 --- /dev/null +++ b/platform/mcu/xr871/project/common/cmd/cmd_smart_config.c @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "cmd_util.h" + +#include "common/framework/net_ctrl.h" +#include "net/wlan/wlan.h" +#include "net/wlan/wlan_defs.h" +#include + +#define SC_TIME_OUT 120000 +#define SC_ACK_TIME_OUT 30000 +static char *key = "1234567812345678"; + +static OS_Thread_t g_sc_ctrl_thread; +#define SC_CTRL_THREAD_STACK_SIZE (1 * 1024) + +static void sc_task(void *arg) +{ + int ret; + wlan_smart_config_status_t status; + wlan_smart_config_result_t result; + + memset(&result, 0, sizeof(result)); + + net_switch_mode(WLAN_MODE_MONITOR); + + status = wlan_smart_config_start(g_wlan_netif, SC_TIME_OUT, &result); + + net_switch_mode(WLAN_MODE_STA); + + if (status == WLAN_SMART_CONFIG_SUCCESS) { + CMD_DBG("ssid: %.32s\n", (char *)result.ssid); + CMD_DBG("psk: %s\n", (char *)result.passphrase); + CMD_DBG("random: %d\n", result.random_num); + } else { + CMD_DBG ("smartconfig failed %d\n", status); + OS_ThreadDelete(&g_sc_ctrl_thread); + return; + } + + if (result.passphrase[0] != '\0') { + wlan_sta_set(result.ssid, result.ssid_len, result.passphrase); + } else { + wlan_sta_set(result.ssid, result.ssid_len, NULL); + } + + wlan_sta_enable(); + + ret = wlan_smart_config_ack_start(g_wlan_netif, result.random_num, SC_ACK_TIME_OUT); + if (ret < 0) + CMD_ERR("smartconfig ack error, timeout\n"); + + OS_ThreadDelete(&g_sc_ctrl_thread); +} + +static int sc_create() +{ + if (OS_ThreadCreate(&g_sc_ctrl_thread, + "sc_thread", + sc_task, + NULL, + OS_THREAD_PRIO_APP, + SC_CTRL_THREAD_STACK_SIZE) != OS_OK) { + CMD_ERR("create sc thread failed\n"); + return -1; + } + return 0; +} + +enum cmd_status cmd_smart_config_exec(char *cmd) +{ + int ret; + + if (g_wlan_netif == NULL) { + return CMD_STATUS_FAIL; + } + + if (cmd_strcmp(cmd, "start") == 0) { + if (OS_ThreadIsValid(&g_sc_ctrl_thread)) { + CMD_ERR("smartconfig already start\n"); + ret = -1; + } else { + ret = sc_create(); + } + } else if (cmd_strcmp(cmd, "stop") == 0) { + ret = wlan_smart_config_stop(); + } else if (cmd_strcmp(cmd, "set_key") == 0) { + ret = wlan_smart_config_set_key(key, WLAN_SMART_CONFIG_KEY_LEN); + CMD_DBG("Smartconfig set key : %s\n", key); + } else { + CMD_ERR("invalid argument '%s'\n", cmd); + return CMD_STATUS_INVALID_ARG; + } + + return (ret == 0 ? CMD_STATUS_OK : CMD_STATUS_FAIL); +} diff --git a/platform/mcu/xr871/project/common/cmd/cmd_smart_config.h b/platform/mcu/xr871/project/common/cmd/cmd_smart_config.h new file mode 100644 index 0000000000..1a9750ccd3 --- /dev/null +++ b/platform/mcu/xr871/project/common/cmd/cmd_smart_config.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _CMD_SMART_CONFIG_H_ +#define _CMD_SMART_CONFIG_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +enum cmd_status cmd_smart_config_exec(char *cmd); + +#ifdef __cplusplus +} +#endif + +#endif /* _CMD_SMART_CONFIG_H_ */ diff --git a/platform/mcu/xr871/project/common/cmd/cmd_sntp.c b/platform/mcu/xr871/project/common/cmd/cmd_sntp.c new file mode 100644 index 0000000000..2c7e32d638 --- /dev/null +++ b/platform/mcu/xr871/project/common/cmd/cmd_sntp.c @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "cmd_util.h" +#include "cmd_sntp.h" +#include "net/sntp/sntp.h" + +static OS_Thread_t g_sntp_thread; +#define SNTP_THREAD_STACK_SIZE (1 * 1024) +#define SNTP_THREAD_EXIT OS_ThreadDelete + +void sntp_run(void *arg) +{ + CMD_LOG(1, " \n"); + if (sntp_request(NULL) != 0) { + CMD_LOG(1, " \n"); + goto exit; + } + + sntp_time *time = (sntp_time *)sntp_obtain_time(); + CMD_LOG(1, " \n"); + CMD_LOG(1,"sntp(%u %u %u ",time->week, time->mon, time->day); + CMD_LOG(1,"%u:%u:%u %u)\n", time->hour, time->min, time->sec, time->year); +exit: + SNTP_THREAD_EXIT(&g_sntp_thread); + +} + +int sntp_start() +{ + if (OS_ThreadIsValid(&g_sntp_thread)) { + CMD_ERR("sntp task is running\n"); + return -1; + } + + if (OS_ThreadCreate(&g_sntp_thread, + "", + sntp_run, + NULL, + OS_THREAD_PRIO_APP, + SNTP_THREAD_STACK_SIZE) != OS_OK) { + CMD_ERR("sntp task create failed\n"); + return -1; + } + + return 0; +} + +enum cmd_status cmd_sntp_exec(char *cmd) +{ + sntp_start(); + return CMD_STATUS_OK; +} diff --git a/platform/mcu/xr871/project/common/cmd/cmd_sntp.h b/platform/mcu/xr871/project/common/cmd/cmd_sntp.h new file mode 100644 index 0000000000..0e7642971d --- /dev/null +++ b/platform/mcu/xr871/project/common/cmd/cmd_sntp.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _CMD_SNTP_H_ +#define _CMD_SNTP_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +enum cmd_status cmd_sntp_exec(char *cmd); + +#ifdef __cplusplus +} +#endif + +#endif /* _CMD_SNTP_H_ */ diff --git a/platform/mcu/xr871/project/common/cmd/cmd_sysinfo.c b/platform/mcu/xr871/project/common/cmd/cmd_sysinfo.c new file mode 100644 index 0000000000..e31b4a7835 --- /dev/null +++ b/platform/mcu/xr871/project/common/cmd/cmd_sysinfo.c @@ -0,0 +1,334 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "cmd_util.h" +#include "lwip/inet.h" +#include "common/framework/sysinfo.h" + +/* + * sysinfo default + * sysinfo save + * sysinfo load + * + * sysinfo get mac + * sysinfo set mac + * - sysinfo set mac 00:80:E1:29:E8:D1 + * + * sysinfo get sta ssid + * sysinfo set sta ssid + * - sysinfo set sta ssid ssid_example + * + * sysinfo get sta psk + * sysinfo set sta psk + * - sysinfo set sta psk psk_example + * + * sysinfo get ap ssid + * sysinfo set ap ssid + * - sysinfo set ap ssid ssid_example + * + * sysinfo get ap psk + * sysinfo set ap psk + * - sysinfo set ap psk psk_example + * + * sysinfo get sta dhcp + * sysinfo set sta dhcp + * - sysinfo set sta dhcp 1 + * + * sysinfo get sta ip + * sysinfo set sta ip + * - sysinfo set sta ip 192.168.51.100 + * + * sysinfo get sta mask + * sysinfo set sta mask + * - sysinfo set sta mask 255.255.255.0 + * + * sysinfo get sta gateway + * sysinfo set sta gateway + * - sysinfo set sta gateway 192.168.51.1 + * + * sysinfo get ap ip + * sysinfo set ap ip + * - sysinfo set ap ip 192.168.51.1 + * + * sysinfo get ap mask + * sysinfo set ap mask + * - sysinfo set ap mask 255.255.255.0 + * + * sysinfo get ap gateway + * sysinfo set ap gateway + * - sysinfo set ap gateway 192.168.51.1 + */ + +enum cmd_status cmd_sysinfo_default_exec(char *cmd) +{ + if (sysinfo_default() != 0) + return CMD_STATUS_FAIL; + else + return CMD_STATUS_OK; +} + +enum cmd_status cmd_sysinfo_save_exec(char *cmd) +{ + if (sysinfo_save() != 0) + return CMD_STATUS_FAIL; + else + return CMD_STATUS_OK; +} + +enum cmd_status cmd_sysinfo_load_exec(char *cmd) +{ + if (sysinfo_load() != 0) + return CMD_STATUS_FAIL; + else + return CMD_STATUS_OK; +} + +static int cmd_sysinfo_parse_int(const char *value, int min, int max, int *dst) +{ + int val; + char *end; + + val = cmd_strtol(value, &end, 0); + if (*end) { + CMD_ERR("Invalid number '%s'", value); + return -1; + } + + if (val < min || val > max) { + CMD_ERR("out of range value %d (%s), range is [%d, %d]\n", + val, value, min, max); + return -1; + } + + *dst = val; + return 0; +} + +static enum cmd_status cmd_sysinfo_set_mac(char *cmd, struct sysinfo *sysinfo) +{ + int i; + int cnt; + uint32_t mac[SYSINFO_MAC_ADDR_LEN]; + + cnt = cmd_sscanf(cmd, "%x:%x:%x:%x:%x:%x", + &mac[0], &mac[1], &mac[2], &mac[3], &mac[4], &mac[5]); + if (cnt != 6) { + CMD_ERR("cnt %d\n", cnt); + return CMD_STATUS_INVALID_ARG; + } + + for (i = 0; i < SYSINFO_MAC_ADDR_LEN; i++) + sysinfo->mac_addr[i] = (uint8_t)mac[i]; + + return CMD_STATUS_OK; +} + +static enum cmd_status cmd_sysinfo_set_ssid(char *cmd, uint8_t *ssid, uint8_t *ssid_len) +{ + uint32_t len = cmd_strlen(cmd); + + if ((len == 0) || (len > SYSINFO_SSID_LEN_MAX)) { + CMD_ERR("len %d\n", len); + return CMD_STATUS_INVALID_ARG; + } + + cmd_memset(ssid, 0, SYSINFO_SSID_LEN_MAX); + cmd_memcpy(ssid, cmd, len); + *ssid_len = (uint8_t)len; + + return CMD_STATUS_OK; +} + +static enum cmd_status cmd_sysinfo_set_psk(char *cmd, uint8_t *psk) +{ + uint32_t len = cmd_strlen(cmd); + + if ((len < 8) || (len >= SYSINFO_PSK_LEN_MAX)) { + CMD_ERR("len %d\n", len); + return CMD_STATUS_INVALID_ARG; + } + + cmd_memset(psk, 0, SYSINFO_PSK_LEN_MAX); + cmd_memcpy(psk, cmd, len); + + return CMD_STATUS_OK; +} + +static enum cmd_status cmd_sysinfo_set_dhcp(char *cmd, struct sysinfo *sysinfo) +{ + int dhcp; + + if (cmd_sysinfo_parse_int(cmd, 0, 1, &dhcp) != 0) { + CMD_ERR("invalid dhcp '%s'\n", cmd); + return CMD_STATUS_INVALID_ARG; + } + + sysinfo->sta_use_dhcp = dhcp; + + return CMD_STATUS_OK; +} + +static enum cmd_status cmd_sysinfo_set_netif(char *cmd, ip_addr_t *addr) +{ + int cnt; + uint32_t val[4]; + + cnt = cmd_sscanf(cmd, "%u.%u.%u.%u", &val[0], &val[1], &val[2], &val[3]); + if (cnt != 4) { + CMD_ERR("cnt %d\n", cnt); + return CMD_STATUS_INVALID_ARG; + } + + if (((val[0] | val[1] | val[2] | val[3]) & 0xffffff00) != 0) { + CMD_ERR("val[0] %d, val[1] %d, val[2] %d, val[3] %d\n", + val[0], val[1], val[2], val[3]); + return CMD_STATUS_INVALID_ARG; + } + + IP4_ADDR(addr, val[0], val[1], val[2], val[3]); + + return CMD_STATUS_OK; +} + +enum cmd_status cmd_sysinfo_set_exec(char *cmd) +{ + struct sysinfo *sysinfo = sysinfo_get(); + if (sysinfo == NULL) { + CMD_ERR("sysinfo %p\n", sysinfo); + return CMD_STATUS_FAIL; + } + + if (cmd_strncmp(cmd, "mac ", 4) == 0) { + return cmd_sysinfo_set_mac(cmd + 4, sysinfo); + } else if (cmd_strncmp(cmd, "sta ", 4) == 0) { + if (cmd_strncmp(cmd + 4, "ssid ", 5) == 0) { + return cmd_sysinfo_set_ssid(cmd + 9, sysinfo->wlan_sta_param.ssid, + &(sysinfo->wlan_sta_param.ssid_len)); + } else if (cmd_strncmp(cmd + 4, "psk ", 4) == 0) { + return cmd_sysinfo_set_psk(cmd + 8, sysinfo->wlan_sta_param.psk); + } else if (cmd_strncmp(cmd + 4, "dhcp ", 5) == 0) { + return cmd_sysinfo_set_dhcp(cmd + 9, sysinfo); + } else if (cmd_strncmp(cmd + 4, "ip ", 3) == 0) { + return cmd_sysinfo_set_netif(cmd + 7, &sysinfo->netif_sta_param.ip_addr); + } else if (cmd_strncmp(cmd + 4, "mask ", 5) == 0) { + return cmd_sysinfo_set_netif(cmd + 9, &sysinfo->netif_sta_param.net_mask); + } else if (cmd_strncmp(cmd + 4, "gateway ", 8) == 0) { + return cmd_sysinfo_set_netif(cmd + 12, &sysinfo->netif_sta_param.gateway); + } else { + CMD_ERR("invalid arg '%s'\n", cmd); + return CMD_STATUS_INVALID_ARG; + } + } else if (cmd_strncmp(cmd, "ap ", 3) == 0) { + if (cmd_strncmp(cmd + 3, "ssid ", 5) == 0) { + return cmd_sysinfo_set_ssid(cmd + 8, sysinfo->wlan_ap_param.ssid, + &(sysinfo->wlan_ap_param.ssid_len)); + } else if (cmd_strncmp(cmd + 3, "psk ", 4) == 0) { + return cmd_sysinfo_set_psk(cmd + 7, sysinfo->wlan_ap_param.psk); + } else if (cmd_strncmp(cmd + 3, "ip ", 3) == 0) { + return cmd_sysinfo_set_netif(cmd + 6, &sysinfo->netif_ap_param.ip_addr); + } else if (cmd_strncmp(cmd + 3, "mask ", 5) == 0) { + return cmd_sysinfo_set_netif(cmd + 8, &sysinfo->netif_ap_param.net_mask); + } else if (cmd_strncmp(cmd + 3, "gateway ", 8) == 0) { + return cmd_sysinfo_set_netif(cmd + 11, &sysinfo->netif_ap_param.gateway); + } else { + CMD_ERR("invalid arg '%s'\n", cmd); + return CMD_STATUS_INVALID_ARG; + } + } else { + CMD_ERR("invalid arg '%s'\n", cmd); + return CMD_STATUS_INVALID_ARG; + } + + return CMD_STATUS_OK; +} + +enum cmd_status cmd_sysinfo_get_exec(char *cmd) +{ + struct sysinfo *sysinfo = sysinfo_get(); + if (sysinfo == NULL) { + CMD_ERR("sysinfo %p\n", sysinfo); + return CMD_STATUS_FAIL; + } + + if (cmd_strcmp(cmd, "mac") == 0) { + CMD_LOG(1, "MAC: %02x:%02x:%02x:%02x:%02x:%02x\n", + sysinfo->mac_addr[0], sysinfo->mac_addr[1], sysinfo->mac_addr[2], + sysinfo->mac_addr[3], sysinfo->mac_addr[4], sysinfo->mac_addr[5]); + } else if (cmd_strncmp(cmd, "sta ", 4) == 0) { + if (cmd_strcmp(cmd + 4, "ssid") == 0) { + CMD_LOG(1, "ssid: %.32s\n", sysinfo->wlan_sta_param.ssid); + } else if (cmd_strcmp(cmd + 4, "psk") == 0) { + CMD_LOG(1, "psk: %.64s\n", sysinfo->wlan_sta_param.psk); + } else if (cmd_strcmp(cmd + 4, "dhcp") == 0) { + CMD_LOG(1, "use dhcp: %d\n", sysinfo->sta_use_dhcp); + } else if (cmd_strcmp(cmd + 4, "ip") == 0) { + CMD_LOG(1, "ip: %s\n", inet_ntoa(sysinfo->netif_sta_param.ip_addr)); + } else if (cmd_strcmp(cmd + 4, "mask") == 0) { + CMD_LOG(1, "net mask: %s\n", inet_ntoa(sysinfo->netif_sta_param.net_mask)); + } else if (cmd_strcmp(cmd + 4, "gateway") == 0) { + CMD_LOG(1, "gateway: %s\n", inet_ntoa(sysinfo->netif_sta_param.gateway)); + } else { + CMD_ERR("invalid arg '%s'\n", cmd); + return CMD_STATUS_INVALID_ARG; + } + } else if (cmd_strncmp(cmd, "ap ", 3) == 0) { + if (cmd_strcmp(cmd + 3, "ssid") == 0) { + CMD_LOG(1, "ssid: %.32s\n", sysinfo->wlan_ap_param.ssid); + } else if (cmd_strcmp(cmd + 3, "psk") == 0) { + CMD_LOG(1, "psk: %.64s\n", sysinfo->wlan_ap_param.psk); + } else if (cmd_strcmp(cmd + 3, "ip") == 0) { + CMD_LOG(1, "ip: %s\n", inet_ntoa(sysinfo->netif_ap_param.ip_addr)); + } else if (cmd_strcmp(cmd + 3, "mask") == 0) { + CMD_LOG(1, "net mask: %s\n", inet_ntoa(sysinfo->netif_ap_param.net_mask)); + } else if (cmd_strcmp(cmd + 3, "gateway") == 0) { + CMD_LOG(1, "gateway: %s\n", inet_ntoa(sysinfo->netif_ap_param.gateway)); + } else { + CMD_ERR("invalid arg '%s'\n", cmd); + return CMD_STATUS_INVALID_ARG; + } + } else { + CMD_ERR("invalid arg '%s'\n", cmd); + return CMD_STATUS_INVALID_ARG; + } + + return CMD_STATUS_OK; +} + +static struct cmd_data g_sysinfo_cmds[] = { + { "default", cmd_sysinfo_default_exec}, + { "save", cmd_sysinfo_save_exec}, + { "load", cmd_sysinfo_load_exec}, + { "set", cmd_sysinfo_set_exec}, + { "get", cmd_sysinfo_get_exec}, +}; + +enum cmd_status cmd_sysinfo_exec(char *cmd) +{ + return cmd_exec(cmd, g_sysinfo_cmds, cmd_nitems(g_sysinfo_cmds)); +} diff --git a/platform/mcu/xr871/project/common/cmd/cmd_sysinfo.h b/platform/mcu/xr871/project/common/cmd/cmd_sysinfo.h new file mode 100644 index 0000000000..f723862f62 --- /dev/null +++ b/platform/mcu/xr871/project/common/cmd/cmd_sysinfo.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _CMD_SYSINFO_H_ +#define _CMD_SYSINFO_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +enum cmd_status cmd_sysinfo_exec(char *cmd); + +#ifdef __cplusplus +} +#endif + +#endif /* _CMD_SYSINFO_H_ */ diff --git a/platform/mcu/xr871/project/common/cmd/cmd_timer.c b/platform/mcu/xr871/project/common/cmd/cmd_timer.c new file mode 100644 index 0000000000..77c5673760 --- /dev/null +++ b/platform/mcu/xr871/project/common/cmd/cmd_timer.c @@ -0,0 +1,248 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "cmd_util.h" +#include "cmd_timer.h" +#include "driver/chip/hal_timer.h" + +enum cmd_timer_action { + CMD_TIMER_ACTION_DECONFIG, + CMD_TIMER_ACTION_START, + CMD_TIMER_ACTION_STOP, + CMD_TIMER_ACTION_PAUSE, + CMD_TIMER_ACTION_CONTINUE, + CMD_TIMER_ACTION_VALUE, +}; + +static void cmd_timer_callback(void *arg) +{ + cmd_write_event(CMD_EVENT_TIMER_NOTIFY, "timer %d notify @ %d ms", + (int)arg, OS_GetTicks()); +} + +/* + * drv timer config i= m= s= d= p= + */ +static enum cmd_status cmd_timer_config_exec(char *cmd) +{ + uint32_t id, clk_div, period; + char mode_str[8], clk_src_str[8]; + TIMER_Mode mode; + TIMER_ClkSrc clk_src; + TIMER_ClkPrescaler clk_prescaler; + TIMER_InitParam param; + int32_t cnt; + + cnt = cmd_sscanf(cmd, "i=%u m=%7s s=%7s d=%u p=%u", + &id, mode_str, clk_src_str, &clk_div, &period); + if (cnt != 5) { + return CMD_STATUS_INVALID_ARG; + } + + if (id >= TIMER_NUM) { + CMD_ERR("invalid id %d\n", id); + return CMD_STATUS_INVALID_ARG; + } + + if (cmd_strcmp(mode_str, "repeat") == 0) { + mode = TIMER_MODE_REPEAT; + } else if (cmd_strcmp(mode_str, "once") == 0) { + mode = TIMER_MODE_ONCE; + } else { + CMD_ERR("invalid mode %s\n", mode_str); + return CMD_STATUS_INVALID_ARG; + } + + if (cmd_strcmp(clk_src_str, "LF") == 0) { + clk_src = TIMER_CLK_SRC_LFCLK; + } else if (cmd_strcmp(clk_src_str, "HF") == 0) { + clk_src = TIMER_CLK_SRC_HFCLK; + } else { + CMD_ERR("invalid clk-src %s\n", clk_src_str); + return CMD_STATUS_INVALID_ARG; + } + + switch (clk_div) { + case 1: + clk_prescaler = TIMER_CLK_PRESCALER_1; + break; + case 2: + clk_prescaler = TIMER_CLK_PRESCALER_2; + break; + case 4: + clk_prescaler = TIMER_CLK_PRESCALER_4; + break; + case 8: + clk_prescaler = TIMER_CLK_PRESCALER_8; + break; + case 16: + clk_prescaler = TIMER_CLK_PRESCALER_16; + break; + case 32: + clk_prescaler = TIMER_CLK_PRESCALER_32; + break; + case 64: + clk_prescaler = TIMER_CLK_PRESCALER_64; + break; + case 128: + clk_prescaler = TIMER_CLK_PRESCALER_128; + break; + default: + CMD_ERR("invalid clk-div %u\n", clk_div); + return CMD_STATUS_INVALID_ARG; + } + + if (period == 0) { + CMD_ERR("invalid period %u\n", period); + return CMD_STATUS_INVALID_ARG; + } + + param.cfg = HAL_TIMER_MakeInitCfg(mode, clk_src, clk_prescaler); + param.period = period; + param.isEnableIRQ = 1; + param.callback = cmd_timer_callback; + param.arg = (void *)id; + if (HAL_TIMER_Init((TIMER_ID)id, ¶m) != HAL_OK) { + return CMD_STATUS_FAIL; + } + + return CMD_STATUS_OK; +} + +/* + * drv timer i= + */ +static enum cmd_status cmd_timer_action_exec(char *cmd, enum cmd_timer_action action) +{ + uint32_t id, tmp; + int32_t cnt; + enum cmd_status status; + + cnt = cmd_sscanf(cmd, "i=%u", &id); + if (cnt != 1) { + return CMD_STATUS_INVALID_ARG; + } + + if (id >= TIMER_NUM) { + CMD_ERR("invalid id %d\n", id); + return CMD_STATUS_INVALID_ARG; + } + + status = CMD_STATUS_OK; + switch (action) { + case CMD_TIMER_ACTION_DECONFIG: + HAL_TIMER_DeInit((TIMER_ID)id); + break; + case CMD_TIMER_ACTION_START: + HAL_TIMER_Start((TIMER_ID)id); + break; + case CMD_TIMER_ACTION_STOP: + HAL_TIMER_Stop((TIMER_ID)id); + break; + case CMD_TIMER_ACTION_PAUSE: + HAL_TIMER_Pause((TIMER_ID)id); + break; + case CMD_TIMER_ACTION_CONTINUE: + HAL_TIMER_Continue((TIMER_ID)id); + break; + case CMD_TIMER_ACTION_VALUE: + tmp = HAL_TIMER_GetCurrentValue((TIMER_ID)id); + cmd_write_respond(CMD_STATUS_OK, "value=%u", tmp); + status = CMD_STATUS_ACKED; + break; + default: + status = CMD_STATUS_INVALID_ARG; + break; + } + return status; +} + +/* + * drv timer deconfig i= + */ +static enum cmd_status cmd_timer_deconfig_exec(char *cmd) +{ + return cmd_timer_action_exec(cmd, CMD_TIMER_ACTION_DECONFIG); +} + +/* + * drv timer start i= + */ +static enum cmd_status cmd_timer_start_exec(char *cmd) +{ + return cmd_timer_action_exec(cmd, CMD_TIMER_ACTION_START); +} + +/* + * drv timer stop i= + */ +static enum cmd_status cmd_timer_stop_exec(char *cmd) +{ + return cmd_timer_action_exec(cmd, CMD_TIMER_ACTION_STOP); +} + +/* + * drv timer pause i= + */ +static enum cmd_status cmd_timer_pause_exec(char *cmd) +{ + return cmd_timer_action_exec(cmd, CMD_TIMER_ACTION_PAUSE); +} + +/* + * drv timer continue i= + */ +static enum cmd_status cmd_timer_continue_exec(char *cmd) +{ + return cmd_timer_action_exec(cmd, CMD_TIMER_ACTION_CONTINUE); +} + +/* + * drv timer value i= + */ +static enum cmd_status cmd_timer_value_exec(char *cmd) +{ + return cmd_timer_action_exec(cmd, CMD_TIMER_ACTION_VALUE); +} + + +static struct cmd_data g_timer_cmds[] = { + { "config", cmd_timer_config_exec }, + { "deconfig", cmd_timer_deconfig_exec }, + { "start", cmd_timer_start_exec }, + { "stop", cmd_timer_stop_exec }, + { "pause", cmd_timer_pause_exec }, + { "continue", cmd_timer_continue_exec }, + { "value", cmd_timer_value_exec }, +}; + +enum cmd_status cmd_timer_exec(char *cmd) +{ + return cmd_exec(cmd, g_timer_cmds, cmd_nitems(g_timer_cmds)); +} diff --git a/platform/mcu/xr871/project/common/cmd/cmd_timer.h b/platform/mcu/xr871/project/common/cmd/cmd_timer.h new file mode 100644 index 0000000000..3314c1cc4c --- /dev/null +++ b/platform/mcu/xr871/project/common/cmd/cmd_timer.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _CMD_TIMER_H_ +#define _CMD_TIMER_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +enum cmd_status cmd_timer_exec(char *cmd); + +#ifdef __cplusplus +} +#endif + +#endif /* _CMD_TIMER_H_ */ diff --git a/platform/mcu/xr871/project/common/cmd/cmd_uart.c b/platform/mcu/xr871/project/common/cmd/cmd_uart.c new file mode 100644 index 0000000000..6f3c8361fc --- /dev/null +++ b/platform/mcu/xr871/project/common/cmd/cmd_uart.c @@ -0,0 +1,385 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "cmd_util.h" +#include "cmd_uart.h" +#include "driver/chip/hal_gpio.h" +#include "driver/chip/hal_uart.h" + + +typedef int32_t (*uart_receive_func)(UART_ID uartID, uint8_t *buf, int32_t size, uint32_t msec); +typedef int32_t (*uart_transmit_func)(UART_ID uartID, uint8_t *buf, int32_t size); + +#define CMD_UART_TRANSFER_BUF_SIZE 2048 +#define CMD_UART_QUEUE_LEN 1 +#define CMD_UART_TRANSFER_THREAD_STACK_SIZE (1 * 1024) + +struct cmd_uart_msg { + int32_t len; + uint32_t timeout; +}; + +enum cmd_uart_transfer_mode { + CMD_UART_TRANSFER_MODE_IT = 0, + CMD_UART_TRANSFER_MODE_DMA, + CMD_UART_TRANSFER_MODE_POLL, +}; + +struct cmd_uart_priv { + UART_ID id; + enum cmd_uart_transfer_mode mode; + OS_Queue_t queue; + OS_Thread_t thread; +}; + +struct cmd_uart_priv g_cmd_uart_priv; + +UART_DataBits uart_data_bits[4] = { + UART_DATA_BITS_5, UART_DATA_BITS_6, UART_DATA_BITS_7, UART_DATA_BITS_8 +}; + +UART_StopBits uart_stop_bits[2] = { UART_STOP_BITS_1, UART_STOP_BITS_2 }; + +/* + * drv uart config i= b= d= p= s= f= + */ +static enum cmd_status cmd_uart_config_exec(char *cmd) +{ + uint32_t id, baud_rate, data_bits, stop_bits; + char parity[8]; + char flow_ctrl[8]; + int cnt; + UART_Parity uart_parity; + UART_InitParam uart_param; + + cnt = cmd_sscanf(cmd, "i=%u b=%u d=%u p=%7s s=%u f=%7s", &id, &baud_rate, + &data_bits, parity, &stop_bits, flow_ctrl); + if (cnt != 6) { + return CMD_STATUS_INVALID_ARG; + } + + if (id >= UART_NUM) { + CMD_ERR("invalid id %u\n", id); + return CMD_STATUS_INVALID_ARG; + } + + if (baud_rate < 110 || baud_rate > 921600) { + CMD_ERR("invalid baud rate %u\n", baud_rate); + return CMD_STATUS_INVALID_ARG; + } + + if (data_bits < 5 || data_bits > 8) { + CMD_ERR("invalid data bits %u\n", data_bits); + return CMD_STATUS_INVALID_ARG; + } + + if (cmd_strcmp(parity, "none") == 0) { + uart_parity = UART_PARITY_NONE; + } else if (cmd_strcmp(parity, "odd") == 0) { + uart_parity = UART_PARITY_ODD; + } else if (cmd_strcmp(parity, "even") == 0) { + uart_parity = UART_PARITY_EVEN; + } else { + CMD_ERR("invalid parity %s\n", parity); + return CMD_STATUS_INVALID_ARG; + } + + if (stop_bits < 1 || stop_bits > 2) { + CMD_ERR("invalid stop bits %u\n", stop_bits); + return CMD_STATUS_INVALID_ARG; + } + + if (cmd_strcmp(flow_ctrl, "none") != 0) { + CMD_ERR("invalid flow contorl %s\n", flow_ctrl); + return CMD_STATUS_INVALID_ARG; + } + + HAL_UART_DeInit((UART_ID)id); + uart_param.baudRate = baud_rate; + uart_param.parity = uart_parity; + uart_param.stopBits = uart_stop_bits[stop_bits - 1]; + uart_param.dataBits = uart_data_bits[data_bits - 5]; + uart_param.isAutoHwFlowCtrl = 0; + HAL_UART_Init((UART_ID)id, &uart_param); + + return CMD_STATUS_OK; +} + +static void cmd_uart_transfer_task(void *arg) +{ + struct cmd_uart_priv *priv = arg; + struct cmd_uart_msg msg; + uart_receive_func rx_func; + uart_transmit_func tx_func; + uint8_t *buf = NULL; + int32_t cnt; + + if (priv->mode == CMD_UART_TRANSFER_MODE_IT) { + rx_func = HAL_UART_Receive_IT; + tx_func = HAL_UART_Transmit_IT; + } else if (priv->mode == CMD_UART_TRANSFER_MODE_DMA) { + rx_func = HAL_UART_Receive_DMA; + tx_func = HAL_UART_Transmit_DMA; + } else if (priv->mode == CMD_UART_TRANSFER_MODE_POLL) { + rx_func = HAL_UART_Receive_Poll; + tx_func = HAL_UART_Transmit_Poll; + } else { + CMD_ERR("invalid mode %d\n", priv->mode); + goto out; + } + + buf = cmd_malloc(CMD_UART_TRANSFER_BUF_SIZE); + if (buf == NULL) { + CMD_ERR("no memory\n"); + goto out; + } + + if (priv->mode == CMD_UART_TRANSFER_MODE_DMA) { + if (HAL_UART_EnableRxDMA(priv->id) != HAL_OK) { + CMD_ERR("enable rx dma fail\n"); + goto out; + } + if (HAL_UART_EnableTxDMA(priv->id) != HAL_OK) { + CMD_ERR("enable tx dma fail\n"); + HAL_UART_DisableRxDMA(priv->id); + goto out; + } + } + + while (1) { + if (OS_QueueReceive(&priv->queue, &msg, OS_WAIT_FOREVER) != OS_OK) { + CMD_ERR("wait msg fail\n"); + continue; + } + + if (msg.len <= 0) { + CMD_DBG("uart transfer test end\n"); + break; + } + + cnt = rx_func(priv->id, buf, msg.len, msg.timeout); + if (cnt > 0) { + tx_func(priv->id, buf, cnt); + } + } + + if (priv->mode == CMD_UART_TRANSFER_MODE_DMA) { + HAL_UART_DisableTxDMA(priv->id); + HAL_UART_DisableRxDMA(priv->id); + } + +out: + if (buf) + cmd_free(buf); + + CMD_DBG("%s() exit\n", __func__); + OS_ThreadDelete(&priv->thread); +} + +/* + * drv uart transfer-start i= m= + */ +static enum cmd_status cmd_uart_transfer_start_exec(char *cmd) +{ + int32_t cnt; + uint32_t id; + char mode[8]; + struct cmd_uart_priv *priv = &g_cmd_uart_priv; + + if (OS_ThreadIsValid(&priv->thread)) { + CMD_ERR("uart transfer already start\n"); + return CMD_STATUS_FAIL; + } + + cnt = cmd_sscanf(cmd, "i=%u m=%7s", &id, mode); + if (cnt != 2) { + return CMD_STATUS_INVALID_ARG; + } + + if (id >= UART_NUM) { + CMD_ERR("invalid id %u\n", id); + return CMD_STATUS_INVALID_ARG; + } else { + priv->id = (UART_ID)id; + } + + if (cmd_strcmp(mode, "it") == 0) { + priv->mode = CMD_UART_TRANSFER_MODE_IT; + } else if (cmd_strcmp(mode, "dma") == 0) { + priv->mode = CMD_UART_TRANSFER_MODE_DMA; + } else if (cmd_strcmp(mode, "poll") == 0) { + priv->mode = CMD_UART_TRANSFER_MODE_POLL; + } else { + CMD_ERR("invalid mode %s\n", mode); + return CMD_STATUS_INVALID_ARG; + } + + if (OS_QueueCreate(&priv->queue, + CMD_UART_QUEUE_LEN, + sizeof(struct cmd_uart_msg)) != OS_OK) { + CMD_ERR("create queue failed\n"); + return CMD_STATUS_FAIL; + } + + if (OS_ThreadCreate(&priv->thread, + "", + cmd_uart_transfer_task, + priv, + OS_PRIORITY_NORMAL, + CMD_UART_TRANSFER_THREAD_STACK_SIZE) != OS_OK) { + CMD_ERR("create transfer task failed\n"); + OS_QueueDelete(&priv->queue); + return CMD_STATUS_FAIL; + } + + return CMD_STATUS_OK; +} + +/* + * drv uart transfer-data i= l= t= + */ +static enum cmd_status cmd_uart_transfer_data_exec(char *cmd) +{ + int32_t cnt; + uint32_t id, len, timeout; + struct cmd_uart_msg msg; + struct cmd_uart_priv *priv = &g_cmd_uart_priv; + + if (!OS_ThreadIsValid(&priv->thread)) { + CMD_ERR("uart transfer not started\n"); + return CMD_STATUS_FAIL; + } + + cnt = cmd_sscanf(cmd, "i=%u l=%u t=%u", &id, &len, &timeout); + if (cnt != 3) { + return CMD_STATUS_INVALID_ARG; + } + + if (id != priv->id) { + CMD_ERR("invalid id %u, != %d\n", id, priv->id); + return CMD_STATUS_INVALID_ARG; + } + + if (len == 0 || len > CMD_UART_TRANSFER_BUF_SIZE) { + CMD_ERR("invalid data-length %u\n", len); + return CMD_STATUS_INVALID_ARG; + } + + if (timeout == 0) { + timeout = HAL_WAIT_FOREVER; + } + + msg.len = len; + msg.timeout = timeout; + if (OS_QueueSend(&priv->queue, &msg, 0) != OS_OK) + return CMD_STATUS_FAIL; + else + return CMD_STATUS_OK; +} + +/* + * drv uart transfer-stop i= + */ +static enum cmd_status cmd_uart_transfer_stop_exec(char *cmd) +{ + int32_t cnt; + uint32_t id; + struct cmd_uart_msg msg; + struct cmd_uart_priv *priv = &g_cmd_uart_priv; + + if (!OS_ThreadIsValid(&priv->thread)) { + CMD_ERR("uart transfer not started\n"); + return CMD_STATUS_FAIL; + } + + cnt = cmd_sscanf(cmd, "i=%u", &id); + if (cnt != 1) { + return CMD_STATUS_INVALID_ARG; + } + + if (id != priv->id) { + CMD_ERR("invalid id %u, != %d\n", id, priv->id); + return CMD_STATUS_INVALID_ARG; + } + + msg.len = 0; /* trigger transfer test end */ + msg.timeout = 0; + if (OS_QueueSend(&priv->queue, &msg, OS_WAIT_FOREVER) != OS_OK) + return CMD_STATUS_FAIL; + + while (OS_ThreadIsValid(&priv->thread)) { + OS_MSleep(1); /* wait for thread termination */ + } + + OS_QueueDelete(&priv->queue); + return CMD_STATUS_OK; +} + +/* + * drv uart sendbreak i= t= + */ +static enum cmd_status cmd_uart_sendbreak_exec(char *cmd) +{ + uint32_t id, break_len; + int32_t cnt; + + cnt = cmd_sscanf(cmd, "i=%u t=%u", &id, &break_len); + if (cnt != 2) { + return CMD_STATUS_INVALID_ARG; + } + + if (id >= UART_NUM) { + CMD_ERR("invalid id %u\n", id); + return CMD_STATUS_INVALID_ARG; + } + + if (break_len < 1 || break_len > 5000) { + CMD_ERR("invalid break length %u\n", break_len); + return CMD_STATUS_INVALID_ARG; + } + + HAL_UART_SetBreakCmd((UART_ID)id, 1); + OS_MSleep(break_len); + HAL_UART_SetBreakCmd((UART_ID)id, 0); + + return CMD_STATUS_OK; +} + +static struct cmd_data g_uart_cmds[] = { + { "config", cmd_uart_config_exec }, + { "transfer-start", cmd_uart_transfer_start_exec }, + { "transfer-data", cmd_uart_transfer_data_exec }, + { "transfer-stop", cmd_uart_transfer_stop_exec }, + { "sendbreak", cmd_uart_sendbreak_exec }, +}; + +enum cmd_status cmd_uart_exec(char *cmd) +{ + return cmd_exec(cmd, g_uart_cmds, cmd_nitems(g_uart_cmds)); +} diff --git a/platform/mcu/xr871/project/common/cmd/cmd_uart.h b/platform/mcu/xr871/project/common/cmd/cmd_uart.h new file mode 100644 index 0000000000..24fae5ec93 --- /dev/null +++ b/platform/mcu/xr871/project/common/cmd/cmd_uart.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _CMD_UART_H_ +#define _CMD_UART_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +enum cmd_status cmd_uart_exec(char *cmd); + +#ifdef __cplusplus +} +#endif + +#endif /* _CMD_UART_H_ */ diff --git a/platform/mcu/xr871/project/common/cmd/cmd_upgrade.c b/platform/mcu/xr871/project/common/cmd/cmd_upgrade.c new file mode 100644 index 0000000000..8cd67566ff --- /dev/null +++ b/platform/mcu/xr871/project/common/cmd/cmd_upgrade.c @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "cmd_util.h" +#include "driver/chip/hal_prcm.h" + +#define CMD_REBOOT_BY_WDG 1 + +#if CMD_REBOOT_BY_WDG + +#include "driver/chip/hal_wdg.h" + +enum cmd_status cmd_reboot(PRCM_CPUABootFlag flag) +{ + cmd_write_respond(CMD_STATUS_OK, cmd_get_status_desc(CMD_STATUS_OK)); + + HAL_PRCM_SetCPUABootFlag(flag); + HAL_WDG_Reboot(); + return CMD_STATUS_ACKED; +} + +#else /* CMD_REBOOT_BY_WDG */ + +#include "driver/chip/hal_nvic.h" +#include "driver/chip/hal_global.h" +#include "driver/chip/system_chip.h" +#include "sys/interrupt.h" + +#define CMD_BROM_RESET_HANDLER (*((volatile uint32_t *)0x4)) + +enum cmd_status cmd_reboot(PRCM_CPUABootFlag flag) +{ + uint32_t handler; + + cmd_write_respond(CMD_STATUS_OK, cmd_get_status_desc(CMD_STATUS_OK)); + cmd_msleep(10); + + HAL_PRCM_SetCPUABootFlag(flag); + handler = CMD_BROM_RESET_HANDLER; +#ifdef __CONFIG_CHIP_XR871 + handler |= 0x1; /* set thumb bit */ +#endif + + arch_irq_disable(); + HAL_PRCM_DisableSys2(); + HAL_PRCM_DisableSys2Power(); + HAL_GlobalInit(); + SystemDeInit(0); + + SCB->VTOR = 0x0; + __set_CONTROL(0); /* reset to Privileged Thread mode and use MSP */ + __DSB(); + __ISB(); + ((NVIC_IRQHandler)handler)(); + return CMD_STATUS_ACKED; +} + +#endif /* CMD_REBOOT_BY_WDG */ + +enum cmd_status cmd_reboot_exec(char *cmd) +{ + return cmd_reboot(PRCM_CPUA_BOOT_FROM_COLD_RESET); +} + +enum cmd_status cmd_upgrade_exec(char *cmd) +{ + return cmd_reboot(PRCM_CPUA_BOOT_FROM_SYS_UPDATE); +} diff --git a/platform/mcu/xr871/project/common/cmd/cmd_upgrade.h b/platform/mcu/xr871/project/common/cmd/cmd_upgrade.h new file mode 100644 index 0000000000..4df30b8f39 --- /dev/null +++ b/platform/mcu/xr871/project/common/cmd/cmd_upgrade.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _CMD_UPGRADE_H_ +#define _CMD_UPGRADE_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +enum cmd_status cmd_upgrade_exec(char *cmd); +enum cmd_status cmd_reboot_exec(char *cmd); + +#ifdef __cplusplus +} +#endif + +#endif /* _CMD_UPGRADE_H_ */ diff --git a/platform/mcu/xr871/project/common/cmd/cmd_util.c b/platform/mcu/xr871/project/common/cmd/cmd_util.c new file mode 100644 index 0000000000..02fc12b535 --- /dev/null +++ b/platform/mcu/xr871/project/common/cmd/cmd_util.c @@ -0,0 +1,195 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "cmd_util.h" +#include + + +/* cmd format: ... */ +enum cmd_status cmd_exec(char *cmd, struct cmd_data *cdata, int count) +{ + int i; + char *args; + + args = cmd_strchr(cmd, ' '); + if (args) { + *args++ = '\0'; /* has arguments */ + } + + for (i = 0; i < count; ++i, ++cdata) { + if (cmd_strcmp(cmd, cdata->name) == 0) { + return cdata->exec(args ? args : ""); + } + } + + CMD_ERR("unknown cmd '%s'\n", cmd); + return CMD_STATUS_UNKNOWN_CMD; +} + +/* cmd2 format: [ ...] */ +enum cmd_status cmd2_exec(char *cmd, struct cmd2_data *cdata, int count) +{ + int i; + + for (i = 0; i < count; ++i, ++cdata) { + if (cmd_strncmp(cmd, cdata->name, cdata->name_len) == 0) { + return cdata->exec(cmd + cdata->name_len); + } + } + + CMD_ERR("unknown cmd '%s'\n", cmd); + return CMD_STATUS_UNKNOWN_CMD; +} + +/* parse all argument vectors from a command string, return argument count */ +int cmd_parse_argv(char *cmd, char *argv[], int size) +{ + int last, argc; + char *start, *end; + + argc = 0; + start = cmd; + + while (argc < size && *start != '\0') { + while (*start == ' ' || *start == '\t') + start++; + if (*start == '\0') + break; + end = start; + while (*end != ' ' && *end != '\t' && *end != '\0') + end++; + last = *end == '\0'; + *end = '\0'; + argv[argc++] = start; + if (last) + break; + start = end + 1; + } + + if (argc > 0 && argc < size) { + argv[argc] = NULL; /* ANSI-C requirement */ + } + + return argc; +} + +static const char *cmd_status_success_desc[CMD_STATUS_SUCCESS_MAX + 1 - + CMD_STATUS_SUCCESS_MIN] = { + "OK", +}; + +static const char *cmd_status_error_desc[CMD_STATUS_ERROR_MAX + 1 - + CMD_STATUS_ERROR_MIN] = { + "Unknown command", + "Invalid argument", + "Fail", +}; + +static const char *cmd_empty_desc = ""; + +const char *cmd_get_status_desc(enum cmd_status status) +{ + if (status >= CMD_STATUS_SUCCESS_MIN && + status <= CMD_STATUS_SUCCESS_MAX) { + return cmd_status_success_desc[status - CMD_STATUS_SUCCESS_MIN]; + } else if (status >= CMD_STATUS_ERROR_MIN && + status <= CMD_STATUS_ERROR_MAX) { + return cmd_status_error_desc[status - CMD_STATUS_ERROR_MIN]; + } + + return cmd_empty_desc; +} + +#if 0 +static const char *cmd_event_desc[CMD_EVENT_MAX + 1 - CMD_EVENT_MIN] = { + "Test finish", + "timer notify" +}; + +const char *cmd_get_event_desc(enum cmd_event event) +{ + if (event >= CMD_EVENT_MIN && event <= CMD_EVENT_MAX) { + return cmd_event_desc[event - CMD_EVENT_MIN]; + } + + return cmd_empty_desc; +} +#endif + +int cmd_write(enum cmd_code_type type, int code, const char *fmt, ...) +{ + static char str_buf[400]; + + va_list ap; + int len, left; + char *ptr; + + ptr = str_buf; + left = sizeof(str_buf) - 1; /* reserve 1 byte for '\n' */ + len = cmd_snprintf(ptr, left, "<%s> %d ", + type == CMD_CODE_TYEP_STATUS ? "ACK" : "EVT", code); + + ptr += len; + left -= len; + va_start(ap, fmt); + len = vsnprintf(ptr, left, fmt, ap); + va_end(ap); + + ptr += len; + left -= len; + left += 1; + len = cmd_snprintf(ptr, left, "\n"); + + left -= len; + len = sizeof(str_buf) - left; + + return console_write((uint8_t *)str_buf, len); +} + +int32_t cmd_raw_mode_read(uint8_t *buf, int32_t size, uint32_t msec) +{ + UART_ID uart_id = console_get_uart_id(); + + if (uart_id < UART_NUM) { + return HAL_UART_Receive_Poll(uart_id, buf, size, msec); + } else { + return -1; + } +} + +int32_t cmd_raw_mode_write(uint8_t *buf, int32_t size) +{ + UART_ID uart_id = console_get_uart_id(); + + if (uart_id < UART_NUM) { + return HAL_UART_Transmit_Poll(uart_id, buf, size); + } else { + return -1; + } +} diff --git a/platform/mcu/xr871/project/common/cmd/cmd_util.h b/platform/mcu/xr871/project/common/cmd/cmd_util.h new file mode 100644 index 0000000000..95856f31ab --- /dev/null +++ b/platform/mcu/xr871/project/common/cmd/cmd_util.h @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _CMD_UTIL_H_ +#define _CMD_UTIL_H_ + +#include +#include +#include "sys/param.h" +#include "cmd_defs.h" +#include "cmd_debug.h" +#include "console/console.h" +#include "kernel/os/os.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* command format: ... */ +struct cmd_data { + char *name; + enum cmd_status (*exec)(char *); +}; + +/* command2 format: [ ...] */ +struct cmd2_data { + char *name; + int name_len; + enum cmd_status (*exec)(char *); +}; + +enum cmd_status cmd_exec(char *cmd, struct cmd_data *cdata, int count); +enum cmd_status cmd2_exec(char *cmd, struct cmd2_data *cdata, int count); + +int cmd_parse_argv(char *cmd, char *argv[], int size); + +const char *cmd_get_status_desc(enum cmd_status status); +//const char *cmd_get_event_desc(enum cmd_event event); + +int cmd_write(enum cmd_code_type type, int code, const char *fmt, ...); + +#define cmd_write_respond(status, fmt, arg...) \ + cmd_write(CMD_CODE_TYEP_STATUS, (int)status, fmt, ##arg) + +#define cmd_write_event(event, fmt, arg...) \ + cmd_write(CMD_CODE_TYEP_EVENT, (int)event, fmt, ##arg) + +int32_t cmd_raw_mode_read(uint8_t *buf, int32_t size, uint32_t msec); +int32_t cmd_raw_mode_write(uint8_t *buf, int32_t size); + +#define cmd_raw_mode_enable() console_disable(); +#define cmd_raw_mode_disable() console_enable(); + + +#define cmd_malloc(l) malloc(l) +#define cmd_free(p) free(p) + +#define cmd_memcpy(d, s, n) memcpy(d, s, n) +#define cmd_memset(s, c, n) memset(s, c, n) +#define cmd_memcmp(s1, s2, n) memcmp(s1, s2, n) + +#define cmd_strlen(s) strlen(s) +#define cmd_strcmp(s1, s2) strcmp(s1, s2) +#define cmd_strncmp(s1, s2, n) strncmp(s1, s2, n) +#define cmd_strcasecmp(s1, s2) strcasecmp(s1, s2) +#define cmd_strncasecmp(s1, s2, n) strncasecmp(s1, s2, n) +#define cmd_strchr(s, c) strchr(s, c) +#define cmd_strrchr(s, c) strrchr(s, c) +#define cmd_strstr(s1, s2) strstr(s1, s2) +#define cmd_strtol(s, p, b) strtol(s, p, b) +#define cmd_strdup(s) strdup(s) +#define cmd_strlcpy(d, s, n) strlcpy(d, s, n) + +#define cmd_atoi(s) atoi(s) +#define cmd_atol(s) atol(s) + +#define cmd_sscanf(s, f, a...) sscanf(s, f, ##a) +#define cmd_sprintf(s, f, a...) sprintf(s, f, ##a) +#define cmd_snprintf(s, n, f, a...) snprintf(s, n, f, ##a) + +#define cmd_nitems(a) nitems(a) + +#define cmd_msleep(msec) OS_MSleep(msec) + +#ifdef __cplusplus +} +#endif + +#endif /* _CMD_UTIL_H_ */ diff --git a/platform/mcu/xr871/project/common/cmd/cmd_wdg.c b/platform/mcu/xr871/project/common/cmd/cmd_wdg.c new file mode 100644 index 0000000000..8698c121cb --- /dev/null +++ b/platform/mcu/xr871/project/common/cmd/cmd_wdg.c @@ -0,0 +1,139 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "cmd_util.h" +#include "cmd_wdg.h" +#include "driver/chip/hal_wdg.h" + +static void cmd_wdg_callback(void *arg) +{ + cmd_write_event(CMD_EVENT_WDG_TIMEOUT, "wdg timeout @ %d ms", OS_GetTicks()); +} + +/* + * drv wdg config m= t= + */ +static enum cmd_status cmd_wdg_config_exec(char *cmd) +{ + char mode_str[8]; + uint32_t timeout; + WDG_InitParam param; + int32_t cnt; + + cnt = cmd_sscanf(cmd, "m=%7s t=%u", mode_str, &timeout); + if (cnt != 2) { + return CMD_STATUS_INVALID_ARG; + } + + cmd_memset(¶m, 0, sizeof(param)); + + if (cmd_strcmp(mode_str, "reset") == 0) { + param.event = WDG_EVT_RESET; + param.resetCycle = WDG_DEFAULT_RESET_CYCLE; + } else if (cmd_strcmp(mode_str, "it") == 0) { + param.event = WDG_EVT_INTERRUPT; + param.callback = cmd_wdg_callback; + param.arg = NULL; + } else { + CMD_ERR("invalid mode %s\n", mode_str); + return CMD_STATUS_INVALID_ARG; + } + + if (timeout > 11) { + CMD_ERR("invalid timeout %u\n", timeout); + return CMD_STATUS_INVALID_ARG; + } else { + param.timeout = (timeout << WDG_TIMEOUT_SHIFT) & WDG_TIMEOUT_MASK; + } + + if (HAL_WDG_Init(¶m) != HAL_OK) { + return CMD_STATUS_FAIL; + } + + return CMD_STATUS_OK; +} + +/* + * drv wdg deconfig + */ +static enum cmd_status cmd_wdg_deconfig_exec(char *cmd) +{ + HAL_WDG_DeInit(); + return CMD_STATUS_OK; +} + +/* + * drv wdg start + */ +static enum cmd_status cmd_wdg_start_exec(char *cmd) +{ + HAL_WDG_Start(); + return CMD_STATUS_OK; +} + +/* + * drv wdg stop + */ +static enum cmd_status cmd_wdg_stop_exec(char *cmd) +{ + HAL_WDG_Stop(); + return CMD_STATUS_OK; +} + +/* + * drv wdg feed + */ +static enum cmd_status cmd_wdg_feed_exec(char *cmd) +{ + HAL_WDG_Feed(); + return CMD_STATUS_OK; +} + +/* + * drv wdg reboot + */ +static enum cmd_status cmd_wdg_reboot_exec(char *cmd) +{ + HAL_WDG_Reboot(); + return CMD_STATUS_OK; +} + +static struct cmd_data g_wdg_cmds[] = { + { "config", cmd_wdg_config_exec }, + { "deconfig", cmd_wdg_deconfig_exec }, + { "start", cmd_wdg_start_exec }, + { "stop", cmd_wdg_stop_exec }, + { "feed", cmd_wdg_feed_exec }, + { "reboot", cmd_wdg_reboot_exec }, +}; + +enum cmd_status cmd_wdg_exec(char *cmd) +{ + return cmd_exec(cmd, g_wdg_cmds, cmd_nitems(g_wdg_cmds)); +} diff --git a/platform/mcu/xr871/project/common/cmd/cmd_wdg.h b/platform/mcu/xr871/project/common/cmd/cmd_wdg.h new file mode 100644 index 0000000000..a10c704a5a --- /dev/null +++ b/platform/mcu/xr871/project/common/cmd/cmd_wdg.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _CMD_WDG_H_ +#define _CMD_WDG_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +enum cmd_status cmd_wdg_exec(char *cmd); + +#ifdef __cplusplus +} +#endif + +#endif /* _CMD_WDG_H_ */ diff --git a/platform/mcu/xr871/project/common/cmd/cmd_wlan.c b/platform/mcu/xr871/project/common/cmd/cmd_wlan.c new file mode 100644 index 0000000000..be9f2607b0 --- /dev/null +++ b/platform/mcu/xr871/project/common/cmd/cmd_wlan.c @@ -0,0 +1,1041 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * net mode + * - net mode sta + * - net mode ap + * - net mode mon + */ + +/* + * net sta config [psk] + * - net sta config ssid_example + * - net sta config ssid_example psk_example + * + * net sta set + * - net sta set ssid ssid_example + * - net sta set psk psk_example + * - net sta set wep_key0 wep_key_example + * - net sta set wep_key1 wep_key_example + * - net sta set wep_key2 wep_key_example + * - net sta set wep_key3 wep_key_example + * - net sta set wep_key_index <0, 1, 2, 3> + * - net sta set key_mgmt {WPA-PSK, NONE} + * - net sta set pairwise {CCMP, TKIP, WEP40, WEP104, NONE} + * - net sta set group {CCMP, TKIP, WEP40, WEP104, NONE} + * - net sta set proto {WPA, RSN} + * - net sta set auth_alg {OPEN, SHARED} + * - net sta set ptk_rekey + * - net sta set scan_ssid (0, 1) + * + * net sta get + * - net sta get ssid + * - net sta get psk + * - net sta get wep_key0 + * - net sta get wep_key1 + * - net sta get wep_key2 + * - net sta get wep_key3 + * - net sta get wep_key_index + * - net sta get key_mgmt + * - net sta get pairwise + * - net sta get group + * - net sta get proto + * - net sta get auth_alg + * - net sta get ptk_rekey + * - net sta get scan_ssid + * + * net sta enable + * net sta disable + * + * net sta scan once + * net sta scan result + * net sta scan interval + * net sta bss flush + * + * net sta connect + * net sta disconnect + * net sta state + * net sta ap + * + * net sta wps pbc + * net sta wps pin get + * net sta wps pin set + */ + +/* + * net ap config [psk] + * - net ap config ssid_example + * - net ap config ssid_example psk_example + * + * net ap set + * - net ap set ssid ssid_example + * - net ap set psk psk_example + * - net ap set key_mgmt {WPA-PSK, NONE} + * - net ap set wpa {CCMP, TKIP, NONE} + * - net ap set rsn {CCMP, TKIP, NONE} + * - net ap set proto + * - net ap set auth_alg {OPEN} + * - net ap set group_rekey + * - net ap set strict_rekey <0, 1> + * - net ap set gmk_rekey + * - net ap set ptk_rekey + * - net ap set hw_mode + * - net ap set 80211n <0, 1> + * - net ap set channel <1 ~ 13> + * - net ap set beacon_int <15 ~ 65535> + * - net ap set dtim <1 ~ 255> + * - net ap set max_num_sta + * + * net ap get + * - net ap get ssid + * - net ap get psk + * - net ap get key_mgmt + * - net ap get wpa + * - net ap get rsn + * - net ap get proto + * - net ap get auth_alg + * - net ap get group_rekey + * - net ap get strict_rekey + * - net ap get gmk_rekey + * - net ap get ptk_rekey + * - net ap get hw_mode + * - net ap get 80211n + * - net ap get channel + * - net ap get beacon_int + * - net ap get dtim + * - net ap get max_num_sta + * + * net ap enable + * net ap reload + * net ap disable + * + * net ap sta num + * net ap sta info + */ + +#include "cmd_util.h" +#include "net/wlan/wlan.h" +#include "net/wlan/wlan_defs.h" +#include "common/framework/net_ctrl.h" + +#ifdef __PRJ_CONFIG_WLAN_STA_AP +static const char *g_wlan_mode_str[WLAN_MODE_NUM] = { + [WLAN_MODE_STA] = "station", + [WLAN_MODE_HOSTAP] = "hostap", + [WLAN_MODE_MONITOR] = "monitor", +}; + +enum cmd_status cmd_wlan_mode_exec(char *cmd) +{ + enum wlan_mode cur_mode, new_mode; + const char *mode_str; + + if (cmd_strcmp(cmd, "") == 0) { + cur_mode = wlan_if_get_mode(g_wlan_netif); + if (cur_mode < WLAN_MODE_NUM) { + mode_str = g_wlan_mode_str[cur_mode]; + } else { + mode_str = "invalid"; + } + cmd_write_respond(CMD_STATUS_OK, "%s", mode_str); + return CMD_STATUS_ACKED; + } + + if (cmd_strcmp(cmd, "sta") == 0) { + new_mode = WLAN_MODE_STA; + } else if (cmd_strcmp(cmd, "ap") == 0) { + new_mode = WLAN_MODE_HOSTAP; + } else if (cmd_strcmp(cmd, "mon") == 0) { + new_mode = WLAN_MODE_MONITOR; + } else { + return CMD_STATUS_INVALID_ARG; + } + + cmd_write_respond(CMD_STATUS_OK, "OK"); + net_switch_mode(new_mode); + + return CMD_STATUS_ACKED; +} +#endif /* __PRJ_CONFIG_WLAN_STA_AP */ + +/* wpas parse */ + +static int cmd_wpas_parse_int(const char *value, int min, int max, int *dst) +{ + int val; + char *end; + + val = cmd_strtol(value, &end, 0); + if (*end) { + CMD_ERR("Invalid number '%s'", value); + return -1; + } + + if (val < min || val > max) { + CMD_ERR("out of range value %d (%s), range is [%d, %d]\n", + val, value, min, max); + return -1; + } + + *dst = val; + return 0; +} + +static int cmd_wpas_parse_key_mgmt(const char *value) +{ + int val = 0, last, errors = 0; + char *start, *end, *buf; + + buf = cmd_strdup(value); + if (buf == NULL) + return -1; + start = buf; + + while (*start != '\0') { + while (*start == ' ' || *start == '\t') + start++; + if (*start == '\0') + break; + end = start; + while (*end != ' ' && *end != '\t' && *end != '\0') + end++; + last = *end == '\0'; + *end = '\0'; + if (cmd_strcmp(start, "WPA-PSK") == 0) + val |= WPA_KEY_MGMT_PSK; + else if (cmd_strcmp(start, "WPA-EAP") == 0) + val |= WPA_KEY_MGMT_IEEE8021X; + else if (cmd_strcmp(start, "IEEE8021X") == 0) + val |= WPA_KEY_MGMT_IEEE8021X_NO_WPA; + else if (cmd_strcmp(start, "NONE") == 0) + val |= WPA_KEY_MGMT_NONE; + else if (cmd_strcmp(start, "WPA-NONE") == 0) + val |= WPA_KEY_MGMT_WPA_NONE; + else { + CMD_DBG("Invalid key_mgmt '%s'", start); + errors++; + } + + if (last) + break; + start = end + 1; + } + cmd_free(buf); + + if (val == 0) { + CMD_DBG("No key_mgmt values configured\n"); + errors++; + } + + CMD_DBG("key_mgmt: 0x%x\n", val); + return errors ? -1 : val; +} + +static int cmd_wpas_parse_cipher(const char *value) +{ + int val = 0, last; + char *start, *end, *buf; + + buf = cmd_strdup(value); + if (buf == NULL) + return -1; + start = buf; + + while (*start != '\0') { + while (*start == ' ' || *start == '\t') + start++; + if (*start == '\0') + break; + end = start; + while (*end != ' ' && *end != '\t' && *end != '\0') + end++; + last = *end == '\0'; + *end = '\0'; + if (cmd_strcmp(start, "CCMP-256") == 0) + val |= WPA_CIPHER_CCMP_256; + else if (cmd_strcmp(start, "GCMP-256") == 0) + val |= WPA_CIPHER_GCMP_256; + else if (cmd_strcmp(start, "CCMP") == 0) + val |= WPA_CIPHER_CCMP; + else if (cmd_strcmp(start, "GCMP") == 0) + val |= WPA_CIPHER_GCMP; + else if (cmd_strcmp(start, "TKIP") == 0) + val |= WPA_CIPHER_TKIP; + else if (cmd_strcmp(start, "WEP104") == 0) + val |= WPA_CIPHER_WEP104; + else if (cmd_strcmp(start, "WEP40") == 0) + val |= WPA_CIPHER_WEP40; + else if (cmd_strcmp(start, "NONE") == 0) + val |= WPA_CIPHER_NONE; + else if (cmd_strcmp(start, "GTK_NOT_USED") == 0) + val |= WPA_CIPHER_GTK_NOT_USED; + else { + cmd_free(buf); + return -1; + } + + if (last) + break; + start = end + 1; + } + cmd_free(buf); + + return val; +} + +static int cmd_wpas_parse_proto(const char *value) +{ + int val = 0, last, errors = 0; + char *start, *end, *buf; + + buf = cmd_strdup(value); + if (buf == NULL) + return -1; + start = buf; + + while (*start != '\0') { + while (*start == ' ' || *start == '\t') + start++; + if (*start == '\0') + break; + end = start; + while (*end != ' ' && *end != '\t' && *end != '\0') + end++; + last = *end == '\0'; + *end = '\0'; + + /* softAP work on open mode. */ + if (cmd_strcmp(start, "NONE") == 0) { + val = 0; + break; + } + + if (cmd_strcmp(start, "WPA") == 0) + val |= WPA_PROTO_WPA; + else if (cmd_strcmp(start, "RSN") == 0 || + cmd_strcmp(start, "WPA2") == 0) + val |= WPA_PROTO_RSN; + else if (cmd_strcmp(start, "OSEN") == 0) + val |= WPA_PROTO_OSEN; + else { + CMD_DBG("Invalid proto '%s'\n", start); + errors++; + } + + if (last) + break; + start = end + 1; + } + cmd_free(buf); + +/* softAP work on open mode. */ +#if 0 + if (val == 0) { + CMD_DBG("No proto values configured\n"); + errors++; + } +#endif + + CMD_DBG("proto: 0x%x\n", val); + return errors ? -1 : val; +} + +static int cmd_wpas_parse_auth_alg(const char *value) +{ + int val = 0, last, errors = 0; + char *start, *end, *buf; + + buf = cmd_strdup(value); + if (buf == NULL) + return -1; + start = buf; + + while (*start != '\0') { + while (*start == ' ' || *start == '\t') + start++; + if (*start == '\0') + break; + end = start; + while (*end != ' ' && *end != '\t' && *end != '\0') + end++; + last = *end == '\0'; + *end = '\0'; + if (cmd_strcmp(start, "OPEN") == 0) + val |= WPA_AUTH_ALG_OPEN; + else if (cmd_strcmp(start, "SHARED") == 0) + val |= WPA_AUTH_ALG_SHARED; + else if (cmd_strcmp(start, "LEAP") == 0) + val |= WPA_AUTH_ALG_LEAP; + else { + CMD_DBG("Invalid auth_alg '%s'\n", start); + errors++; + } + + if (last) + break; + start = end + 1; + } + cmd_free(buf); + + if (val == 0) { + CMD_DBG("No auth_alg values configured\n"); + errors++; + } + + CMD_DBG("auth_alg: 0x%x\n", val); + return errors ? -1 : val; +} + +static __inline void cmd_wlan_sta_print_ap(wlan_sta_ap_t *ap) +{ + CMD_LOG(1, "%02x:%02x:%02x:%02x:%02x:%02x ssid=%-32.32s " + "beacon_int=%d freq=%d channel=%u rssi=%d level=%d " + "flags=%#010x wpa_key_mgmt=%#010x wpa_cipher=%#010x " + "wpa2_key_mgmt=%#010x wpa2_cipher=%#010x\n", + ap->bssid[0], ap->bssid[1], + ap->bssid[2], ap->bssid[3], + ap->bssid[4], ap->bssid[5], + ap->ssid.ssid, + ap->beacon_int, + ap->freq, + ap->channel, + ap->rssi, + ap->level, + ap->wpa_flags, + ap->wpa_key_mgmt, + ap->wpa_cipher, + ap->wpa2_key_mgmt, + ap->wpa2_cipher); +} + + +static void cmd_wlan_sta_print_scan_results(wlan_sta_scan_results_t *results) +{ + int i; + + for (i = 0; i < results->num; ++i) { + CMD_LOG(1, "\n%02d: ", i + 1); + cmd_wlan_sta_print_ap(&results->ap[i]); + } +} + +/* @return + * -2: CMD_STATUS_INVALID_ARG + * -1: CMD_STATUS_FAIL + * 0: CMD_STATUS_OK + */ +static int cmd_wlan_sta_set(char *cmd) +{ + char *value; + wlan_sta_config_t config; + + value = cmd_strchr(cmd, ' '); + if (value == NULL) + return -2; + *value++ = '\0'; + + config.field = WLAN_STA_FIELD_NUM; + + if (cmd_strcmp(cmd, "ssid") == 0) { + uint8_t ssid_len = cmd_strlen(value); + if ((ssid_len >= 1) && (ssid_len <= 32)) { + config.field = WLAN_STA_FIELD_SSID; + cmd_memcpy(config.u.ssid.ssid, value, ssid_len); + config.u.ssid.ssid_len = ssid_len; + } + } else if (cmd_strcmp(cmd, "psk") == 0) { + config.field = WLAN_STA_FIELD_PSK; + cmd_strlcpy((char *)config.u.psk, value, sizeof(config.u.psk)); + } else if (cmd_strcmp(cmd, "wep_key0") == 0) { + config.field = WLAN_STA_FIELD_WEP_KEY0; + cmd_strlcpy((char *)config.u.wep_key, value, sizeof(config.u.wep_key)); + } else if (cmd_strcmp(cmd, "wep_key1") == 0) { + config.field = WLAN_STA_FIELD_WEP_KEY1; + cmd_strlcpy((char *)config.u.wep_key, value, sizeof(config.u.wep_key)); + } else if (cmd_strcmp(cmd, "wep_key2") == 0) { + config.field = WLAN_STA_FIELD_WEP_KEY2; + cmd_strlcpy((char *)config.u.wep_key, value, sizeof(config.u.wep_key)); + } else if (cmd_strcmp(cmd, "wep_key3") == 0) { + config.field = WLAN_STA_FIELD_WEP_KEY3; + cmd_strlcpy((char *)config.u.wep_key, value, sizeof(config.u.wep_key)); + } else if (cmd_strcmp(cmd, "wep_key_index") == 0) { + int index; + if (cmd_wpas_parse_int(value, 0, 3, &index) == 0) { + config.field = WLAN_STA_FIELD_WEP_KEY_INDEX; + config.u.wep_tx_keyidx = index; + } + } else if (cmd_strcmp(cmd, "key_mgmt") == 0) { + int key_mgmt = cmd_wpas_parse_key_mgmt(value); + if (key_mgmt > 0) { + config.field = WLAN_STA_FIELD_KEY_MGMT; + config.u.key_mgmt = key_mgmt; + } + } else if (cmd_strcmp(cmd, "pairwise") == 0) { + int pairwise_cipher = cmd_wpas_parse_cipher(value); + if (pairwise_cipher > 0) { + config.field = WLAN_STA_FIELD_PAIRWISE_CIPHER; + config.u.pairwise_cipher = pairwise_cipher; + } + } else if (cmd_strcmp(cmd, "group") == 0) { + int group_cipher = cmd_wpas_parse_cipher(value); + if (group_cipher > 0) { + config.field = WLAN_STA_FIELD_GROUP_CIPHER; + config.u.group_cipher = group_cipher; + } + } else if (cmd_strcmp(cmd, "proto") == 0) { + int proto = cmd_wpas_parse_proto(value); + if (proto >= 0) { + config.field = WLAN_STA_FIELD_PROTO; + config.u.proto = proto; + } + } else if (cmd_strcmp(cmd, "auth_alg") == 0) { + int auth_alg = cmd_wpas_parse_auth_alg(value); + if (auth_alg > 0) { + config.field = WLAN_STA_FIELD_AUTH_ALG; + config.u.auth_alg = auth_alg; + } + } else if (cmd_strcmp(cmd, "ptk_rekey") == 0) { + int sec; + if (cmd_wpas_parse_int(value, 0, INT32_MAX, &sec) == 0) { + config.field = WLAN_STA_FIELD_WPA_PTK_REKEY; + config.u.wpa_ptk_rekey = sec; + } + } else if (cmd_strcmp(cmd, "scan_ssid") == 0) { + int enable; + if (cmd_wpas_parse_int(value, 0, 1, &enable) == 0) { + config.field = WLAN_STA_FIELD_SCAN_SSID; + config.u.scan_ssid = enable; + } + } + + if (config.field < WLAN_STA_FIELD_NUM) + return wlan_sta_set_config(&config); + + CMD_ERR("%s: invalid arg '%s %s'\n", __func__, cmd, value); + return -2; +} + +/* @return + * -2: CMD_STATUS_INVALID_ARG + * -1: CMD_STATUS_FAIL + * 0: CMD_STATUS_OK + */ +static int cmd_wlan_sta_get(char *cmd) +{ + wlan_sta_config_t config; + cmd_memset(&config, 0, sizeof(config)); + + if (cmd_strcmp(cmd, "ssid") == 0) { + config.field = WLAN_STA_FIELD_SSID; + } else if (cmd_strcmp(cmd, "psk") == 0) { + config.field = WLAN_STA_FIELD_PSK; + } else if (cmd_strcmp(cmd, "wep_key0") == 0) { + config.field = WLAN_STA_FIELD_WEP_KEY0; + } else if (cmd_strcmp(cmd, "wep_key1") == 0) { + config.field = WLAN_STA_FIELD_WEP_KEY1; + } else if (cmd_strcmp(cmd, "wep_key2") == 0) { + config.field = WLAN_STA_FIELD_WEP_KEY2; + } else if (cmd_strcmp(cmd, "wep_key3") == 0) { + config.field = WLAN_STA_FIELD_WEP_KEY3; + } else if (cmd_strcmp(cmd, "wep_key_index") == 0) { + config.field = WLAN_STA_FIELD_WEP_KEY_INDEX; + } else if (cmd_strcmp(cmd, "key_mgmt") == 0) { + config.field = WLAN_STA_FIELD_KEY_MGMT; + } else if (cmd_strcmp(cmd, "pairwise") == 0) { + config.field = WLAN_STA_FIELD_PAIRWISE_CIPHER; + } else if (cmd_strcmp(cmd, "group") == 0) { + config.field = WLAN_STA_FIELD_GROUP_CIPHER; + } else if (cmd_strcmp(cmd, "proto") == 0) { + config.field = WLAN_STA_FIELD_PROTO; + } else if (cmd_strcmp(cmd, "auth_alg") == 0) { + config.field = WLAN_STA_FIELD_AUTH_ALG; + } else if (cmd_strcmp(cmd, "ptk_rekey") == 0) { + config.field = WLAN_STA_FIELD_WPA_PTK_REKEY; + } else if (cmd_strcmp(cmd, "scan_ssid") == 0) { + config.field = WLAN_STA_FIELD_SCAN_SSID; + } else { + CMD_ERR("%s: invalid arg '%s'\n", __func__, cmd); + return -2; + } + + if (wlan_sta_get_config(&config) != 0) { + CMD_ERR("%s: get config failed\n", __func__); + return -1; + } + + if (config.field == WLAN_STA_FIELD_SSID) { + CMD_LOG(1, "ssid: %.32s\n", config.u.ssid.ssid); + } else if (config.field == WLAN_STA_FIELD_PSK) { + CMD_LOG(1, "psk: %s\n", config.u.psk); + } else if (config.field == WLAN_STA_FIELD_WEP_KEY0) { + CMD_LOG(1, "wep_key0: %s\n", config.u.wep_key); + } else if (config.field == WLAN_STA_FIELD_WEP_KEY1) { + CMD_LOG(1, "wep_key1: %s\n", config.u.wep_key); + } else if (config.field == WLAN_STA_FIELD_WEP_KEY2) { + CMD_LOG(1, "wep_key2: %s\n", config.u.wep_key); + } else if (config.field == WLAN_STA_FIELD_WEP_KEY3) { + CMD_LOG(1, "wep_key3: %s\n", config.u.wep_key); + } else if (config.field == WLAN_STA_FIELD_WEP_KEY_INDEX) { + CMD_LOG(1, "wep_key_index: %d\n", config.u.wep_tx_keyidx); + } else if (config.field == WLAN_STA_FIELD_KEY_MGMT) { + CMD_LOG(1, "key_mgmt: %#06x\n", config.u.key_mgmt); + } else if (config.field == WLAN_STA_FIELD_PAIRWISE_CIPHER) { + CMD_LOG(1, "pairwise_cipher: %#06x\n", config.u.pairwise_cipher); + } else if (config.field == WLAN_STA_FIELD_GROUP_CIPHER) { + CMD_LOG(1, "group_cipher: %#06x\n", config.u.group_cipher); + } else if (config.field == WLAN_STA_FIELD_PROTO) { + CMD_LOG(1, "proto: %#06x\n", config.u.proto); + } else if (config.field == WLAN_STA_FIELD_AUTH_ALG) { + CMD_LOG(1, "auth_alg: %#06x\n", config.u.auth_alg); + } else if (config.field == WLAN_STA_FIELD_WPA_PTK_REKEY) { + CMD_LOG(1, "ptk_rekey: %d\n", config.u.wpa_ptk_rekey); + } else if (config.field == WLAN_STA_FIELD_SCAN_SSID) { + CMD_LOG(1, "scan_ssid: %d\n", config.u.scan_ssid); + } + + return 0; +} + +enum cmd_status cmd_wlan_sta_exec(char *cmd) +{ + int ret; + + cmd_write_respond(CMD_STATUS_OK, "OK"); + + if (cmd_strncmp(cmd, "config ", 7) == 0) { + char *argv[2]; + if (cmd_parse_argv(cmd + 7, argv, cmd_nitems(argv)) == 0) { + ret = -2; + goto out; + } + ret = wlan_sta_set((uint8_t *)argv[0], cmd_strlen(argv[0]), (uint8_t *)argv[1]); + } else if (cmd_strncmp(cmd, "set ", 4) == 0) { + ret = cmd_wlan_sta_set(cmd + 4); + } else if (cmd_strncmp(cmd, "get ", 4) == 0) { + ret = cmd_wlan_sta_get(cmd + 4); + } else if (cmd_strcmp(cmd, "enable") == 0) { + ret = wlan_sta_enable(); + } else if (cmd_strcmp(cmd, "disable") == 0) { + ret = wlan_sta_disable(); + } else if (cmd_strcmp(cmd, "scan once") == 0) { + ret = wlan_sta_scan_once(); + } else if (cmd_strncmp(cmd, "scan result ", 12) == 0) { + int size; + if (cmd_wpas_parse_int(cmd + 12, 1, 30, &size) != 0) { + ret = -2; + goto out; + } + wlan_sta_scan_results_t results; + results.ap = cmd_malloc(size * sizeof(wlan_sta_ap_t)); + if (results.ap == NULL) { + CMD_ERR("%s: malloc failed\n", __func__); + ret = -1; + goto out; + } + results.size = size; + ret = wlan_sta_scan_result(&results); + if (ret == 0) + cmd_wlan_sta_print_scan_results(&results); + cmd_free(results.ap); + } else if (cmd_strncmp(cmd, "scan interval ", 14) == 0) { + int sec; + if (cmd_wpas_parse_int(cmd + 14, 0, INT32_MAX, &sec) != 0) { + ret = -2; + goto out; + } + ret = wlan_sta_scan_interval(sec); + } else if (cmd_strncmp(cmd, "bss flush ", 10) == 0) { + int age; + if (cmd_wpas_parse_int(cmd + 10, 0, INT32_MAX, &age) != 0) { + ret = -2; + goto out; + } + ret = wlan_sta_bss_flush(age); + } else if (cmd_strcmp(cmd, "connect") == 0) { + ret = wlan_sta_connect(); + } else if (cmd_strcmp(cmd, "disconnect") == 0) { + ret = wlan_sta_disconnect(); + } else if (cmd_strcmp(cmd, "state") == 0) { + wlan_sta_states_t state; + ret = wlan_sta_state(&state); + if (ret == 0) + CMD_LOG(1, "sta state: %d\n", state); + } else if (cmd_strcmp(cmd, "ap") == 0) { + wlan_sta_ap_t *ap = cmd_malloc(sizeof(wlan_sta_ap_t)); + if (ap == NULL) { + CMD_ERR("%s: malloc failed\n", __func__); + ret = -1; + goto out; + } + ret = wlan_sta_ap_info(ap); + if (ret == 0) + cmd_wlan_sta_print_ap(ap); + cmd_free(ap); + } else if (cmd_strcmp(cmd, "wps pbc") == 0) { + ret = wlan_sta_wps_pbc(); + } else if ((cmd_strlen(cmd) == 7) || (cmd_strcmp(cmd, "wps pin") == 0)) { + wlan_sta_wps_pin_t wps; + ret = wlan_sta_wps_pin_get(&wps); + if (ret == 0) + CMD_LOG(1, "WPS pin: %s\n", wps.pin); + } else if (cmd_strncmp(cmd, "wps pin ", 8) == 0) { + if (cmd_strlen(cmd + 8) != 8) { + ret = -2; + goto out; + } + wlan_sta_wps_pin_t wps; + cmd_memcpy(wps.pin, cmd + 8, 8); + wps.pin[8] = '\0'; + ret = wlan_sta_wps_pin_set(&wps); + } else { + CMD_ERR("%s: unknown command '%s'\n", __func__, cmd); + return CMD_STATUS_ACKED; + } + +out: + if (ret == -2) { + CMD_ERR("%s: command '%s' invalid arg\n", __func__, cmd); + return CMD_STATUS_ACKED; + } else if (ret == -1) { + CMD_ERR("%s: command '%s' exec failed\n", __func__, cmd); + return CMD_STATUS_ACKED; + } + + return CMD_STATUS_ACKED; +} + +static void cmd_wlan_ap_print_sta_info(wlan_ap_stas_t *stas) +{ + int i; + + CMD_LOG(1, "sta_num: %d\n", stas->num); + + for (i = 0; i < stas->num; i++) { + CMD_LOG(1, "[%02d]Mac addr: %02x:%02x:%02x:%02x:%02x:%02x\n", + i + 1, stas->sta[i].addr[0], stas->sta[i].addr[1], + stas->sta[i].addr[2], stas->sta[i].addr[3], + stas->sta[i].addr[4], stas->sta[i].addr[5]); + } +} + +/* @return + * -2: CMD_STATUS_INVALID_ARG + * -1: CMD_STATUS_FAIL + * 0: CMD_STATUS_OK + */ +static int cmd_wlan_ap_set(char *cmd) +{ + char *value; + wlan_ap_config_t config; + + value = cmd_strchr(cmd, ' '); + if (value == NULL) + return -2; + *value++ = '\0'; + + config.field = WLAN_AP_FIELD_NUM; + + if (cmd_strcmp(cmd, "ssid") == 0) { + uint8_t ssid_len = cmd_strlen(value); + if ((ssid_len >= 1) && (ssid_len <= 32)) { + config.field = WLAN_STA_FIELD_SSID; + cmd_memcpy(config.u.ssid.ssid, value, ssid_len); + config.u.ssid.ssid_len = ssid_len; + } + } else if (cmd_strcmp(cmd, "psk") == 0) { + config.field = WLAN_AP_FIELD_PSK; + cmd_strlcpy((char *)config.u.psk, value, sizeof(config.u.psk)); + } else if (cmd_strcmp(cmd, "key_mgmt") == 0) { + int key_mgmt = cmd_wpas_parse_key_mgmt(value); + if (key_mgmt > 0) { + config.field = WLAN_AP_FIELD_KEY_MGMT; + config.u.key_mgmt = key_mgmt; + } + } else if (cmd_strcmp(cmd, "wpa") == 0) { + int wpa_cipher = cmd_wpas_parse_cipher(value); + if (wpa_cipher > 0) { + config.field = WLAN_AP_FIELD_WPA_CIPHER; + config.u.wpa_cipher = wpa_cipher; + } + } else if (cmd_strcmp(cmd, "rsn") == 0) { + int rsn_cipher = cmd_wpas_parse_cipher(value); + if (rsn_cipher > 0) { + config.field = WLAN_AP_FIELD_RSN_CIPHER; + config.u.rsn_cipher = rsn_cipher; + } + } else if (cmd_strcmp(cmd, "proto") == 0) { + int proto = cmd_wpas_parse_proto(value); + if (proto >= 0) { + config.field = WLAN_AP_FIELD_PROTO; + config.u.proto = proto; + } + } else if (cmd_strcmp(cmd, "auth_alg") == 0) { + int auth_alg = cmd_wpas_parse_auth_alg(value); + if (auth_alg > 0) { + config.field = WLAN_AP_FIELD_AUTH_ALG; + config.u.auth_alg = auth_alg; + } + } else if (cmd_strcmp(cmd, "group_rekey") == 0) { + int group_rekey; + if (cmd_wpas_parse_int(value, 0, INT32_MAX, &group_rekey) == 0) { + config.field = WLAN_AP_FIELD_GROUP_REKEY; + config.u.group_rekey = group_rekey; + } + } else if (cmd_strcmp(cmd, "strict_rekey") == 0) { + int strict_rekey; + if (cmd_wpas_parse_int(value, 0, 1, &strict_rekey) == 0) { + config.field = WLAN_AP_FIELD_STRICT_REKEY; + config.u.strict_rekey = strict_rekey; + } + } else if (cmd_strcmp(cmd, "gmk_rekey") == 0) { + int gmk_rekey; + if (cmd_wpas_parse_int(value, 0, INT32_MAX, &gmk_rekey) == 0) { + config.field = WLAN_AP_FIELD_GMK_REKEY; + config.u.gmk_rekey = gmk_rekey; + } + } else if (cmd_strcmp(cmd, "ptk_rekey") == 0) { + int ptk_rekey; + if (cmd_wpas_parse_int(value, 0, INT32_MAX, &ptk_rekey) == 0) { + config.field = WLAN_AP_FIELD_PTK_REKEY; + config.u.ptk_rekey = ptk_rekey; + } + } else if (cmd_strcmp(cmd, "hw_mode") == 0) { + if ((value[0] == 'b') && (value[1] == '\0')) { + config.field = WLAN_AP_FIELD_HW_MODE; + config.u.hw_mode = WLAN_AP_HW_MODE_IEEE80211B; + } else if ((value[0] == 'g') && (value[1] == '\0')) { + config.field = WLAN_AP_FIELD_HW_MODE; + config.u.hw_mode = WLAN_AP_HW_MODE_IEEE80211G; + } + } else if (cmd_strcmp(cmd, "80211n") == 0) { + int ieee80211n; + if (cmd_wpas_parse_int(value, 0, 1, &ieee80211n) == 0) { + config.field = WLAN_AP_FIELD_IEEE80211N; + config.u.ieee80211n = ieee80211n; + } + } else if (cmd_strcmp(cmd, "channel") == 0) { + int channel; + if (cmd_wpas_parse_int(value, 1, 14, &channel) == 0) { + config.field = WLAN_AP_FIELD_CHANNEL; + config.u.channel = channel; + } + } else if (cmd_strcmp(cmd, "beacon_int") == 0) { + int beacon_int; + if (cmd_wpas_parse_int(value, 15, 65535, &beacon_int) == 0) { + config.field = WLAN_AP_FIELD_BEACON_INT; + config.u.beacon_int = beacon_int; + } + } else if (cmd_strcmp(cmd, "dtim") == 0) { + int dtim; + if (cmd_wpas_parse_int(value, 1, 255, &dtim) == 0) { + config.field = WLAN_AP_FIELD_DTIM; + config.u.dtim = dtim; + } + } else if (cmd_strcmp(cmd, "max_num_sta") == 0) { + int max_num_sta; + if (cmd_wpas_parse_int(value, 0, INT32_MAX, &max_num_sta) == 0) { + config.field = WLAN_AP_FIELD_MAX_NUM_STA; + config.u.max_num_sta = max_num_sta; + } + } + + if (config.field < WLAN_AP_FIELD_NUM) + return wlan_ap_set_config(&config); + + CMD_ERR("%s: invalid arg '%s %s'\n", __func__, cmd, value); + return -2; +} + +/* @return + * -2: CMD_STATUS_INVALID_ARG + * -1: CMD_STATUS_FAIL + * 0: CMD_STATUS_OK + */ +static int cmd_wlan_ap_get(char *cmd) +{ + wlan_ap_config_t config; + cmd_memset(&config, 0, sizeof(config)); + + if (cmd_strcmp(cmd, "ssid") == 0) { + config.field = WLAN_AP_FIELD_SSID; + } else if (cmd_strcmp(cmd, "psk") == 0) { + config.field = WLAN_AP_FIELD_PSK; + } else if (cmd_strcmp(cmd, "key_mgmt") == 0) { + config.field = WLAN_AP_FIELD_KEY_MGMT; + } else if (cmd_strcmp(cmd, "wpa") == 0) { + config.field = WLAN_AP_FIELD_WPA_CIPHER; + } else if (cmd_strcmp(cmd, "rsn") == 0) { + config.field = WLAN_AP_FIELD_RSN_CIPHER; + } else if (cmd_strcmp(cmd, "proto") == 0) { + config.field = WLAN_AP_FIELD_PROTO; + } else if (cmd_strcmp(cmd, "auth_alg") == 0) { + config.field = WLAN_AP_FIELD_AUTH_ALG; + } else if (cmd_strcmp(cmd, "group_rekey") == 0) { + config.field = WLAN_AP_FIELD_GROUP_REKEY; + } else if (cmd_strcmp(cmd, "strict_rekey") == 0) { + config.field = WLAN_AP_FIELD_STRICT_REKEY; + } else if (cmd_strcmp(cmd, "gmk_rekey") == 0) { + config.field = WLAN_AP_FIELD_GMK_REKEY; + } else if (cmd_strcmp(cmd, "ptk_rekey") == 0) { + config.field = WLAN_AP_FIELD_PTK_REKEY; + } else if (cmd_strcmp(cmd, "hw_mode") == 0) { + config.field = WLAN_AP_FIELD_HW_MODE; + } else if (cmd_strcmp(cmd, "80211n") == 0) { + config.field = WLAN_AP_FIELD_IEEE80211N; + } else if (cmd_strcmp(cmd, "channel") == 0) { + config.field = WLAN_AP_FIELD_CHANNEL; + } else if (cmd_strcmp(cmd, "beacon_int") == 0) { + config.field = WLAN_AP_FIELD_BEACON_INT; + } else if (cmd_strcmp(cmd, "dtim") == 0) { + config.field = WLAN_AP_FIELD_DTIM; + } else if (cmd_strcmp(cmd, "max_num_sta") == 0) { + config.field = WLAN_AP_FIELD_MAX_NUM_STA; + } else { + CMD_ERR("%s: invalid arg '%s'\n", __func__, cmd); + return -2; + } + + if (wlan_ap_get_config(&config) != 0) { + CMD_ERR("%s: get config failed\n", __func__); + return -1; + } + + if (config.field == WLAN_AP_FIELD_SSID) { + CMD_LOG(1, "ssid: %.32s\n", config.u.ssid.ssid); + } else if (config.field == WLAN_AP_FIELD_PSK) { + CMD_LOG(1, "psk: %s\n", config.u.psk); + } else if (config.field == WLAN_AP_FIELD_KEY_MGMT) { + CMD_LOG(1, "key_mgmt: %#06x\n", config.u.key_mgmt); + } else if (config.field == WLAN_AP_FIELD_WPA_CIPHER) { + CMD_LOG(1, "wpa_cipher: %#06x\n", config.u.wpa_cipher); + } else if (config.field == WLAN_AP_FIELD_RSN_CIPHER) { + CMD_LOG(1, "rsn_cipher: %#06x\n", config.u.rsn_cipher); + } else if (config.field == WLAN_AP_FIELD_PROTO) { + CMD_LOG(1, "proto: %#06x\n", config.u.proto); + } else if (config.field == WLAN_AP_FIELD_AUTH_ALG) { + CMD_LOG(1, "auth_alg: %#06x\n", config.u.auth_alg); + } else if (config.field == WLAN_AP_FIELD_GROUP_REKEY) { + CMD_LOG(1, "group_rekey: %d\n", config.u.group_rekey); + } else if (config.field == WLAN_AP_FIELD_STRICT_REKEY) { + CMD_LOG(1, "strict_rekey: %d\n", config.u.strict_rekey); + } else if (config.field == WLAN_AP_FIELD_GMK_REKEY) { + CMD_LOG(1, "gmk_rekey: %d\n", config.u.gmk_rekey); + } else if (config.field == WLAN_AP_FIELD_PTK_REKEY) { + CMD_LOG(1, "ptk_rekey: %d\n", config.u.ptk_rekey); + } else if (config.field == WLAN_AP_FIELD_HW_MODE) { + if (config.u.hw_mode == WLAN_AP_HW_MODE_IEEE80211B) { + CMD_LOG(1, "hw_mode: b\n"); + } else if (config.u.hw_mode == WLAN_AP_HW_MODE_IEEE80211G) { + CMD_LOG(1, "hw_mode: g\n"); + } else if (config.u.hw_mode == WLAN_AP_HW_MODE_IEEE80211A) { + CMD_LOG(1, "hw_mode: a\n"); + } else if (config.u.hw_mode == WLAN_AP_HW_MODE_IEEE80211AD) { + CMD_LOG(1, "hw_mode: ad\n"); + } else { + CMD_ERR("%s: invalid hw_mode\n", __func__); + } + } else if (config.field == WLAN_AP_FIELD_IEEE80211N) { + CMD_LOG(1, "ieee80211n: %d\n", config.u.ieee80211n); + } else if (config.field == WLAN_AP_FIELD_CHANNEL) { + CMD_LOG(1, "channel: %d\n", config.u.channel); + } else if (config.field == WLAN_AP_FIELD_BEACON_INT) { + CMD_LOG(1, "beacon_int: %d\n", config.u.beacon_int); + } else if (config.field == WLAN_AP_FIELD_DTIM) { + CMD_LOG(1, "dtim: %d\n", config.u.dtim); + } else if (config.field == WLAN_AP_FIELD_MAX_NUM_STA) { + CMD_LOG(1, "max_num_sta: %d\n", config.u.max_num_sta); + } + + return 0; +} + +enum cmd_status cmd_wlan_ap_exec(char *cmd) +{ + int ret; + + cmd_write_respond(CMD_STATUS_OK, "OK"); + + if (cmd_strncmp(cmd, "config ", 7) == 0) { + char *argv[2]; + if (cmd_parse_argv(cmd + 7, argv, cmd_nitems(argv)) == 0) { + ret = -2; + goto out; + } + ret = wlan_ap_set((uint8_t *)argv[0], cmd_strlen(argv[0]), (uint8_t *)argv[1]); + } else if (cmd_strncmp(cmd, "set ", 4) == 0) { + ret = cmd_wlan_ap_set(cmd + 4); + } else if (cmd_strncmp(cmd, "get ", 4) == 0) { + ret = cmd_wlan_ap_get(cmd + 4); + } else if (cmd_strcmp(cmd, "enable") == 0) { + ret = wlan_ap_enable(); + } else if (cmd_strcmp(cmd, "reload") == 0) { + ret = wlan_ap_reload(); + } else if (cmd_strcmp(cmd, "disable") == 0) { + ret = wlan_ap_disable(); + } else if (cmd_strcmp(cmd, "sta num") == 0) { + int num; + ret = wlan_ap_sta_num(&num); + if (ret == 0) + CMD_LOG(1, "sta num: %d\n", num); + } else if (cmd_strncmp(cmd, "sta info ", 9) == 0) { + int size; + if (cmd_wpas_parse_int(cmd + 9, 1, 30, &size) != 0) { + ret = -2; + goto out; + } + wlan_ap_stas_t stas; + stas.sta = (wlan_ap_sta_t *)cmd_malloc(size * sizeof(wlan_ap_sta_t)); + if (stas.sta == NULL) { + CMD_ERR("%s: malloc failed\n", __func__); + ret = -1; + goto out; + } + stas.size = size; + ret = wlan_ap_sta_info(&stas); + if (ret == 0) + cmd_wlan_ap_print_sta_info(&stas); + cmd_free(stas.sta); + } else { + CMD_ERR("%s: unknown command '%s'\n", __func__, cmd); + return CMD_STATUS_ACKED; + } + +out: + if (ret == -2) { + CMD_ERR("%s: command '%s' invalid arg\n", __func__, cmd); + return CMD_STATUS_ACKED; + } else if (ret == -1) { + CMD_ERR("%s: command '%s' exec failed\n", __func__, cmd); + return CMD_STATUS_ACKED; + } + + return CMD_STATUS_ACKED; +} + diff --git a/platform/mcu/xr871/project/common/cmd/cmd_wlan.h b/platform/mcu/xr871/project/common/cmd/cmd_wlan.h new file mode 100644 index 0000000000..237bf044ce --- /dev/null +++ b/platform/mcu/xr871/project/common/cmd/cmd_wlan.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _CMD_WLAN_H_ +#define _CMD_WLAN_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __PRJ_CONFIG_WLAN_STA_AP +enum cmd_status cmd_wlan_mode_exec(char *cmd); +#endif +enum cmd_status cmd_wlan_sta_exec(char *cmd); +enum cmd_status cmd_wlan_ap_exec(char *cmd); + +#ifdef __cplusplus +} +#endif + +#endif /* _CMD_WLAN_H_ */ diff --git a/platform/mcu/xr871/project/common/cmd/tls/client.c b/platform/mcu/xr871/project/common/cmd/tls/client.c new file mode 100644 index 0000000000..30ecfc504c --- /dev/null +++ b/platform/mcu/xr871/project/common/cmd/tls/client.c @@ -0,0 +1,274 @@ +/* + * SSL client demonstration program + * + * Copyright (C) 2006-2015, ARM Limited, All Rights Reserved + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * This file is part of mbed TLS (https://tls.mbed.org) + */ +#include "tls.h" + +#if !defined(MBEDTLS_BIGNUM_C) || !defined(MBEDTLS_ENTROPY_C) || \ + !defined(MBEDTLS_SSL_TLS_C) || !defined(MBEDTLS_SSL_CLI_C) || \ + !defined(MBEDTLS_NET_C) || !defined(MBEDTLS_RSA_C) || \ + !defined(MBEDTLS_CERTS_C) || !defined(MBEDTLS_PEM_PARSE_C) || \ + !defined(MBEDTLS_CTR_DRBG_C) || !defined(MBEDTLS_X509_CRT_PARSE_C) + +void mbedtls_client( void *arg) +{ + mbedtls_printf("MBEDTLS_BIGNUM_C and/or MBEDTLS_ENTROPY_C and/or " + "MBEDTLS_SSL_TLS_C and/or MBEDTLS_SSL_CLI_C and/or " + "MBEDTLS_NET_C and/or MBEDTLS_RSA_C and/or " + "MBEDTLS_CTR_DRBG_C and/or MBEDTLS_X509_CRT_PARSE_C " + "not defined.\n"); +} + +#else +#include +#include "net/mbedtls/net.h" +#include "net/mbedtls/debug.h" +#include "net/mbedtls/ssl.h" +#include "net/mbedtls/entropy.h" +#include "net/mbedtls/ctr_drbg.h" +#include "net/mbedtls/error.h" +#include "net/mbedtls/certs.h" + +extern volatile int mbedtls_string_mismatch; + +static void cli_debug( void *ctx, int level, + const char *file, int line, + const char *str ) +{ + ((void) level); + mbedtls_printf("%s:%04d: %s\n", file, line, str); +} + +void mbedtls_client(void *arg) +{ + mbedtls_test_param *param = (mbedtls_test_param *)arg; + unsigned int flags = param->flags; + unsigned int time_start = 0,time_now = 0, time_elapse = 0; + unsigned int milliseconds = 0; + int ret = 0, len = 0; + const char *pers = "ssl_client1"; + mbedtls_net_context server_fd; + mbedtls_entropy_context entropy; + mbedtls_ctr_drbg_context ctr_drbg; + mbedtls_ssl_context ssl; + mbedtls_ssl_config conf; + mbedtls_x509_crt cacert; + unsigned char *buf = mbedtls_calloc(1,TLS_TEST_BUF_SIZE); + if (!buf) { + mbedtls_printf( "\n[TLS CLI]Malloc failed.\n" ); + goto exit; + } + +#if defined(MBEDTLS_DEBUG_C) + mbedtls_debug_set_threshold(DEBUG_LEVEL); +#endif + /* Initialize the RNG and the session data */ + mbedtls_net_init(&server_fd); + mbedtls_ssl_init(&ssl); + mbedtls_ssl_config_init(&conf); + mbedtls_x509_crt_init(&cacert); + mbedtls_ctr_drbg_init(&ctr_drbg); + + mbedtls_printf("Seeding the random number generator.\n"); + mbedtls_entropy_init(&entropy); + if ((ret = mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy, + (const unsigned char *) pers, + strlen(pers))) != 0) { + mbedtls_printf(" failed\n ! mbedtls_ctr_drbg_seed returned %d\n", ret); + goto exit; + } + mbedtls_printf("ok\n"); + /* Initialize certificates */ + mbedtls_printf("Loading the CA root certificate .\n"); +#if defined(MBEDTLS_CUSTOM_CA) + ret = mbedtls_x509_crt_parse(&cacert, (const unsigned char *) mbedtls_custom_cas_pem, + mbedtls_custom_cas_pem_len); +#else + ret = mbedtls_x509_crt_parse(&cacert, (const unsigned char *) mbedtls_test_cas_pem, + mbedtls_test_cas_pem_len); +#endif + if (ret < 0) { + mbedtls_printf(" failed\n ! mbedtls_x509_crt_parse returned -0x%x\n\n", -ret); + goto exit; + } + mbedtls_printf(" ok (%d skipped)\n", ret); + /* Start the connection */ + char *port = NULL; + if (flags & MBEDTLS_SSL_FLAG_SERVER_PORT) + port = param->server_port; + else + port = SERVER_PORT; + + char *server = NULL; + if (flags & MBEDTLS_SSL_FLAG_SERVER_NAME) + server = param->server_name; + else + goto exit; + mbedtls_printf("Connecting to %s (%s).\n", server, port); + if ((ret = mbedtls_net_connect(&server_fd, server, + port, MBEDTLS_NET_PROTO_TCP)) != 0) { + mbedtls_printf(" failed\n ! mbedtls_net_connect returned %d\n\n", ret); + goto exit; + } + mbedtls_printf(" ok\n"); + /* Setup stuff */ + mbedtls_printf("Setting up the SSL/TLS structure...\n"); + if ((ret = mbedtls_ssl_config_defaults(&conf, + MBEDTLS_SSL_IS_CLIENT, + MBEDTLS_SSL_TRANSPORT_STREAM, + MBEDTLS_SSL_PRESET_DEFAULT)) != 0) { + mbedtls_printf(" failed\n ! mbedtls_ssl_config_defaults returned %d\n\n", ret); + goto exit; + } + mbedtls_printf(" ok\n"); + /* + * OPTIONAL is not optimal for security, + * but makes interop easier in this simplified example. + */ +#if defined(MBEDTLS_CUSTOM_CA) + mbedtls_ssl_conf_authmode(&conf, MBEDTLS_SSL_VERIFY_REQUIRED); +#else + mbedtls_ssl_conf_authmode(&conf, MBEDTLS_SSL_VERIFY_OPTIONAL); +#endif + mbedtls_ssl_conf_ca_chain(&conf, &cacert, NULL); + mbedtls_ssl_conf_rng(&conf, mbedtls_ctr_drbg_random, &ctr_drbg); + mbedtls_ssl_conf_dbg(&conf, cli_debug, stdout); + if ((ret = mbedtls_ssl_setup(&ssl, &conf)) != 0) { + mbedtls_printf(" failed\n ! mbedtls_ssl_setup returned %d\n\n", ret); + goto exit; + } + #if 0 + if ((ret = mbedtls_ssl_set_hostname(&ssl, server)) != 0) + { + mbedtls_printf( " failed\n ! mbedtls_ssl_set_hostname returned %d\n\n", ret ); + goto exit; + } + #endif + mbedtls_ssl_set_bio( &ssl, &server_fd, mbedtls_net_send, mbedtls_net_recv, NULL ); + /* Handshake */ + mbedtls_printf( "Performing the SSL/TLS handshake...\n" ); + while ((ret = mbedtls_ssl_handshake( &ssl ) ) != 0) { + if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) { + mbedtls_printf( " failed\n ! mbedtls_ssl_handshake returned -0x%x\n\n", -ret); + goto exit; + } + } + mbedtls_printf(" ok(%s)\n", mbedtls_ssl_get_ciphersuite(&ssl)); + /* Verify the server certificate */ + mbedtls_printf("Verifying peer X.509 certificate...\n"); + + /* In real life, we probably want to bail out when ret != 0 */ + if ((ret = mbedtls_ssl_get_verify_result( &ssl ) ) != 0) { + char *vrfy_buf = malloc(512); + if (!vrfy_buf) + mbedtls_printf("[TLS-CLI]Malloc vrfy buf failed\n"); + else { + mbedtls_printf(" failed\n"); + mbedtls_x509_crt_verify_info(vrfy_buf, sizeof( vrfy_buf ), " ! ", ret); + mbedtls_printf("%s\n", vrfy_buf); + free(vrfy_buf); + } + } + else + mbedtls_printf(" ok\n"); + /* Write */ + mbedtls_printf("Write to server.\n"); + + if (flags & MBEDTLS_SSL_FLAG_CONTINUE) + milliseconds = param->continue_ms; + else + milliseconds = DEFALT_TEST_TIME; + time_start = TLS_GET_TIME(); + +client: + mbedtls_string_mismatch = -1; + + len = sprintf((char *) buf, CLIENT_DATA); + while (( ret = mbedtls_ssl_write(&ssl, buf, len)) <= 0) { + if(ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) + { + mbedtls_printf(" failed\n ! mbedtls_ssl_write returned %d\n", ret); + goto exit; + } + } + mbedtls_printf(" %d bytes written\n", len); +// mbedtls_printf("%s\n", (char *) buf); + /* Read response */ + mbedtls_printf("Read from server:\n"); + unsigned int length = 0; + do { + len = TLS_TEST_BUF_SIZE; + memset(buf, 0, len); + unsigned char *read_buf = buf; + +read_data: + ret = mbedtls_ssl_read(&ssl, read_buf, len); + if (ret == MBEDTLS_ERR_SSL_WANT_READ || ret == MBEDTLS_ERR_SSL_WANT_WRITE) + continue; + if (ret == MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY) + break; + if (ret < 0) { + mbedtls_printf("failed\n ! mbedtls_ssl_read returned %d\n\n", ret); + break; + } + if (ret == 0) { + mbedtls_printf("\n\nEOF\n\n" ); + break; + } + mbedtls_printf(" %d bytes read\n", ret); + //mbedtls_printf("%s\n", (char *)buf); + length += ret; + if (ret >= mbedtls_strlen(CLIENT_DATA)) { + mbedtls_printf("Read data success\n"); + break; + } else { + read_buf += ret; + len -= ret; + goto read_data; + } + } while (1); + if ((ret = mbedtls_strncmp(CLIENT_DATA, (char *)buf, mbedtls_strlen(CLIENT_DATA))) != 0) { + mbedtls_string_mismatch = -1; + goto exit; + } else + mbedtls_string_mismatch = 0; + time_now = TLS_GET_TIME(); + if (time_now >= time_start) { + time_elapse = time_now - time_start; + } else { + time_elapse = 0xFFFFFFFFUL - time_start + time_now; + } + if (time_elapse < milliseconds) + goto client; + mbedtls_ssl_close_notify(&ssl); +exit: + if (ret != 0) + mbedtls_printf("Last error was: %d\n\n", ret); + mbedtls_net_free(&server_fd); + mbedtls_x509_crt_free(&cacert); + mbedtls_ssl_free(&ssl); + mbedtls_ssl_config_free(&conf); + mbedtls_ctr_drbg_free(&ctr_drbg); + mbedtls_entropy_free(&entropy); + FREE_BUF(buf); +} +#endif /* MBEDTLS_BIGNUM_C && MBEDTLS_ENTROPY_C && MBEDTLS_SSL_TLS_C && + MBEDTLS_SSL_CLI_C && MBEDTLS_NET_C && MBEDTLS_RSA_C && + MBEDTLS_CERTS_C && MBEDTLS_PEM_PARSE_C && MBEDTLS_CTR_DRBG_C && + MBEDTLS_X509_CRT_PARSE_C */ diff --git a/platform/mcu/xr871/project/common/cmd/tls/cmd_tls.c b/platform/mcu/xr871/project/common/cmd/tls/cmd_tls.c new file mode 100644 index 0000000000..a75ba45147 --- /dev/null +++ b/platform/mcu/xr871/project/common/cmd/tls/cmd_tls.c @@ -0,0 +1,177 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "../cmd_util.h" +#include "cmd_tls.h" +#include "tls.h" + +struct config_exec_arg { + char cmd_str[5]; + int (*handler)(void *var); + int config; +}; + +mbedtls_test_param g_tls_data; + +int count_one(unsigned int P) +{ + int n = 0; + while (P != 0) { + if ((P & 0x1) != 0) { + n++; + } + P = P>>1; + } + return n; +} + +static int set_port(void *arg) +{ + char *dest = arg; + if (dest != NULL) { + if (cmd_strlen(dest) > (sizeof(g_tls_data.server_port) - 1)) { + CMD_ERR("invalid param : port.\n"); + return -1; + } + cmd_memcpy(g_tls_data.server_port, dest, cmd_strlen(dest)); + g_tls_data.flags |= MBEDTLS_SSL_FLAG_SERVER_PORT; + g_tls_data.server_port[cmd_strlen(dest)] = '\0'; + } + + return 0; +} + +static int set_servername(void *arg) +{ + char *dest = arg; + if (dest != NULL) { + if (cmd_strlen(dest) > (sizeof(g_tls_data.server_name) - 1)) { + CMD_ERR("invalid param : server_name.\n"); + return -1; + } + cmd_memcpy(g_tls_data.server_name, dest, cmd_strlen(dest)); + g_tls_data.flags |= MBEDTLS_SSL_FLAG_SERVER_NAME; + g_tls_data.server_name[cmd_strlen(dest)] = '\0'; + } + + return 0; +} + +static int set_timer(void *arg) +{ + char *dest = arg; + + if (*dest) { + g_tls_data.continue_ms = cmd_atoi(dest); + g_tls_data.flags |= MBEDTLS_SSL_FLAG_CONTINUE; + } + + return 0; +} + +static int set_client(void *arg) +{ + g_tls_data.flags |= MBEDTLS_SSL_FLAG_CLINET; + + return 0; +} + +static int set_server(void *arg) +{ + g_tls_data.flags |= MBEDTLS_SSL_FLAG_SERVER; + + return 0; +} +#if 0 +static int set_webserver(void *arg) +{ + g_tls_data.flags |= MBEDTLS_SSL_FLAG_WEBSERVER; + + return 0; +} + +static int set_webclient(void *arg) +{ + g_tls_data.flags |= MBEDTLS_SSL_FLAG_WEBCLIENT; + + return 0; +} +#endif +static struct config_exec_arg keywords[] = { + + {"-p", set_port, 1}, /* set port*/ + {"-n", set_servername, 1}, + {"-t", set_timer, 1}, + {"-c", set_client, 0}, + {"-s", set_server, 0}, + //{"-ws", set_webserver, 0}, + //-wc", set_webclient, 0}, + {"", NULL, 0}, +}; + +enum cmd_status cmd_tls_exec(char *cmd) +{ + int argc, i = 0, j = 0; + char *argv[12]; + unsigned int flags = 0; + mbedtls_test_param *param = (mbedtls_test_param *)&g_tls_data; + memset(param, 0, sizeof(mbedtls_test_param)); + argc = cmd_parse_argv(cmd, argv, 12); + + for (j = 0; j < argc; ++j) { + for (i = 0; strlen(keywords[i].cmd_str); i++) + if (cmd_strcmp(argv[j], keywords[i].cmd_str) == 0) { + if (keywords[i].config == 1) + j++; + keywords[i].handler(argv[j]); + } + } + + flags = MBEDTLS_SSL_FLAG_CLINET | MBEDTLS_SSL_FLAG_WEBCLIENT; + + if ((param->flags & flags) != 0) { + if ((param->flags & MBEDTLS_SSL_FLAG_SERVER_NAME) == 0) { + CMD_ERR("invalid tls cmd.\n"); + return CMD_STATUS_INVALID_ARG; + } + } + flags = MBEDTLS_SSL_FLAG_CLINET|MBEDTLS_SSL_FLAG_SERVER| \ + MBEDTLS_SSL_FLAG_WEBSERVER|MBEDTLS_SSL_FLAG_WEBCLIENT; + + flags = param->flags & flags; + if (count_one(flags) > 1 || count_one(flags) == 0) { + CMD_ERR("invalid tls cmd.\n"); + return CMD_STATUS_INVALID_ARG; + } + + if (tls_start(param) == 0) + return CMD_STATUS_OK; + else + return CMD_STATUS_FAIL; +} diff --git a/platform/mcu/xr871/project/common/cmd/tls/cmd_tls.h b/platform/mcu/xr871/project/common/cmd/tls/cmd_tls.h new file mode 100644 index 0000000000..c2ac1628c1 --- /dev/null +++ b/platform/mcu/xr871/project/common/cmd/tls/cmd_tls.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef _CMD_TLS_H_ +#define _CMD_TLS_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +enum cmd_status cmd_tls_exec(char *cmd); + +#ifdef __cplusplus +} +#endif + +#endif /* _CMD_TLS_H_ */ diff --git a/platform/mcu/xr871/project/common/cmd/tls/custom_certs.c b/platform/mcu/xr871/project/common/cmd/tls/custom_certs.c new file mode 100644 index 0000000000..24f25bf4ec --- /dev/null +++ b/platform/mcu/xr871/project/common/cmd/tls/custom_certs.c @@ -0,0 +1,29 @@ +#include "tls.h" + +//#define MBEDTLS_CUSTOM_CA + +#if defined(MBEDTLS_CUSTOM_CA) +#define CUSTOM_CA_CRT_RSA \ +"-----BEGIN CERTIFICATE-----\r\n" \ +"MIICtDCCAZwCAQAwDQYJKoZIhvcNAQELBQAwIDEeMBwGA1UEAxMVQ2VydGlmaWNh\r\n" \ +"dGUgQXV0aG9yaXR5MB4XDTE3MDcxOTExMzU1NVoXDTIyMDcxODExMzU1NVowIDEe\r\n" \ +"MBwGA1UEAxMVQ2VydGlmaWNhdGUgQXV0aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEF\r\n" \ +"AAOCAQ8AMIIBCgKCAQEA3FPtvNnMiETM5qprK4625nf8z39HnM5pdkyjW3a4JWt4\r\n" \ +"RTuLv6x7b56OnJttZPL0hYyVwGsxt3LeuoXD3pBlLn61iwUK6Fb4NX5Xo04LaKl7\r\n" \ +"gQFQ+lENPPSu/JdzcERly0d1JYIQ4Z11732yCdblf5oZJ/0skUzhTVCusWnYvY3Z\r\n" \ +"xs/O3wjvbuCucxgZoyDv6AZ8ZQ0xKZ3JKjm8URD4yrRYEkQ+gBeTkNC3nVFJ/u8X\r\n" \ +"nrNT/qzhhI6HS8Lf88dkQu3W5gvIVVy5qv49hqpWGXwbEkrIsMgC9OJCZ5qoo17Y\r\n" \ +"iUj/SBH6xVA9ikaFOlVeH9rD1euzwgjIm+X2Wu7S5wIDAQABMA0GCSqGSIb3DQEB\r\n" \ +"CwUAA4IBAQCt9EYWA2vTVJmEajB87MzHHvjTV/cTWRGLKnLoBHL3OKf+Lembmu6Q\r\n" \ +"YxoN8OCqRjrcvh0sgQ1H6jpfmVSIoLKoT9BVy16t5PZ8x0XSSrMlXQKz+pAuOZBc\r\n" \ +"sinSoRPDMr0M92m2CAnJ8mIpr6o0lTtWJfY1xeuT2+LbFzvaI6dtWnQYmN1mxPqA\r\n" \ +"JLhAlQhCqmRiDhgFPfKSwKsmEPdUtrA2InOsgxDGa0utawK2BWgQc6hkhT9uZ4dF\r\n" \ +"DxLkNG8w4QFnHXcm8pdpg5zbO7GSrtPRs2CU2fMpE79CMPKSH3ErxV2F4aPtHFP5\r\n" \ +"sV1Ai+iyFoUKCzjW4iUDTJux2gUTzpmH\r\n" \ +"-----END CERTIFICATE-----\r\n" + + +/* Concatenation of all available CA certificates */ +const char mbedtls_custom_cas_pem[] = CUSTOM_CA_CRT_RSA; +const size_t mbedtls_custom_cas_pem_len = sizeof( mbedtls_custom_cas_pem ); +#endif diff --git a/platform/mcu/xr871/project/common/cmd/tls/server.c b/platform/mcu/xr871/project/common/cmd/tls/server.c new file mode 100644 index 0000000000..809501f87e --- /dev/null +++ b/platform/mcu/xr871/project/common/cmd/tls/server.c @@ -0,0 +1,300 @@ +/* + * SSL server demonstration program + * + * Copyright (C) 2006-2015, ARM Limited, All Rights Reserved + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * This file is part of mbed TLS (https://tls.mbed.org) + */ +#include "tls.h" + +#if !defined(MBEDTLS_BIGNUM_C) || !defined(MBEDTLS_CERTS_C) || \ + !defined(MBEDTLS_ENTROPY_C) || !defined(MBEDTLS_SSL_TLS_C) || \ + !defined(MBEDTLS_SSL_SRV_C) || !defined(MBEDTLS_NET_C) || \ + !defined(MBEDTLS_RSA_C) || !defined(MBEDTLS_CTR_DRBG_C) || \ + !defined(MBEDTLS_X509_CRT_PARSE_C)|| \ + !defined(MBEDTLS_PEM_PARSE_C) + +void mbedtls_server(void *arg) +{ + mbedtls_printf("MBEDTLS_BIGNUM_C and/or MBEDTLS_CERTS_C and/or MBEDTLS_ENTROPY_C " + "and/or MBEDTLS_SSL_TLS_C and/or MBEDTLS_SSL_SRV_C and/or " + "MBEDTLS_NET_C and/or MBEDTLS_RSA_C and/or " + "MBEDTLS_CTR_DRBG_C and/or MBEDTLS_X509_CRT_PARSE_C " + "and/or MBEDTLS_PEM_PARSE_C not defined.\n"); +} +#else + +#include +#include +#include "net/mbedtls/entropy.h" +#include "net/mbedtls/ctr_drbg.h" +#include "net/mbedtls/certs.h" +#include "net/mbedtls/x509.h" +#include "net/mbedtls/ssl.h" +#include "net/mbedtls/net.h" +#include "net/mbedtls/error.h" +#include "net/mbedtls/debug.h" + +#if defined(MBEDTLS_SSL_CACHE_C) +#include "mbedtls/ssl_cache.h" +#endif + +extern volatile int mbedtls_string_mismatch; + +static char tls_cal_checksum(void *buffer, int length) +{ + unsigned char *cal = (unsigned char *)buffer; + unsigned char result = 0; + while (length != 0) { + result += cal[--length]; + } + return result; +} + +static void server_debug( void *ctx, int level, + const char *file, int line, + const char *str ) +{ + ((void) level); + mbedtls_printf("%s:%04d: %s\n", file, line, str ); +} + +void mbedtls_server(void *arg) +{ + mbedtls_test_param *param = (mbedtls_test_param *)arg; + unsigned int flags = param->flags; + int ret = 0, len = 0; + + const char *pers = "ssl_server"; + mbedtls_net_context listen_fd, client_fd; + mbedtls_entropy_context entropy; + mbedtls_ctr_drbg_context ctr_drbg; + mbedtls_ssl_context ssl; + mbedtls_ssl_config conf; + mbedtls_x509_crt srvcert; + mbedtls_pk_context pkey; +#if defined(MBEDTLS_SSL_CACHE_C) + mbedtls_ssl_cache_context cache; +#endif + unsigned char *buf = mbedtls_calloc(1,TLS_TEST_BUF_SIZE); + if (!buf) { + mbedtls_printf("[TLS SRV]Malloc failed...\n"); + goto exit; + } + mbedtls_net_init(&listen_fd); + mbedtls_net_init(&client_fd); + mbedtls_ssl_init(&ssl); + mbedtls_ssl_config_init(&conf); +#if defined(MBEDTLS_SSL_CACHE_C) + mbedtls_ssl_cache_init(&cache); +#endif + mbedtls_x509_crt_init(&srvcert); + mbedtls_pk_init(&pkey); + mbedtls_entropy_init(&entropy); + mbedtls_ctr_drbg_init(&ctr_drbg); + +#if defined(MBEDTLS_DEBUG_C) + mbedtls_debug_set_threshold(DEBUG_LEVEL); +#endif + /* load the certificates and private RSA key */ + mbedtls_printf("Loading the server cert. and key...\n"); + /* + * This demonstration program uses embedded test certificates. + * Instead, you may want to use mbedtls_x509_crt_parse_file() to read the + * server and CA certificates, as well as mbedtls_pk_parse_keyfile(). + */ + ret = mbedtls_x509_crt_parse(&srvcert, (const unsigned char *) mbedtls_test_srv_crt, + mbedtls_test_srv_crt_len); + if (ret != 0) { + mbedtls_printf( " failed\n ! mbedtls_x509_crt_parse returned %d\n\n", ret ); + goto exit; + } + + ret = mbedtls_x509_crt_parse(&srvcert, (const unsigned char *) mbedtls_test_cas_pem, + mbedtls_test_cas_pem_len); + if (ret != 0) { + mbedtls_printf( " failed\n ! mbedtls_x509_crt_parse returned %d\n\n", ret ); + goto exit; + } + + ret = mbedtls_pk_parse_key(&pkey, (const unsigned char *) mbedtls_test_srv_key, + mbedtls_test_srv_key_len, NULL, 0); + if (ret != 0) { + mbedtls_printf(" failed\n ! mbedtls_pk_parse_key returned %d\n\n", ret); + goto exit; + } + mbedtls_printf(" ok\n"); + /* Setup the listening TCP socket */ + char *tcp_port = NULL; + if (flags & MBEDTLS_SSL_FLAG_SERVER_PORT) + tcp_port = param->server_port; + else + tcp_port = SERVER_PORT; + + if ((ret = mbedtls_net_bind(&listen_fd, NULL, tcp_port, MBEDTLS_NET_PROTO_TCP)) != 0) { + mbedtls_printf( " failed\n ! mbedtls_net_bind returned %d\n\n", ret ); + goto exit; + } + mbedtls_printf(" ok\n"); + /* Seed the RNG */ + mbedtls_printf( "Seeding the random number generator...\n" ); + if ((ret = mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy, + (const unsigned char *) pers, + strlen(pers))) != 0) { + mbedtls_printf(" failed\n ! mbedtls_ctr_drbg_seed returned %d\n", ret); + goto exit; + } + mbedtls_printf(" ok\n"); + /* Setup stuff */ + mbedtls_printf("Setting up the SSL data....\n"); + if ((ret = mbedtls_ssl_config_defaults(&conf, + MBEDTLS_SSL_IS_SERVER, + MBEDTLS_SSL_TRANSPORT_STREAM, + MBEDTLS_SSL_PRESET_DEFAULT)) != 0) { + mbedtls_printf(" failed\n ! mbedtls_ssl_config_defaults returned %d\n\n", ret); + goto exit; + } + + mbedtls_ssl_conf_rng(&conf, mbedtls_ctr_drbg_random, &ctr_drbg); + mbedtls_ssl_conf_dbg(&conf, server_debug, stdout); + +#if defined(MBEDTLS_SSL_CACHE_C) + mbedtls_ssl_conf_session_cache(&conf, &cache, + mbedtls_ssl_cache_get, + mbedtls_ssl_cache_set); +#endif + + mbedtls_ssl_conf_ca_chain(&conf, srvcert.next, NULL); + if ((ret = mbedtls_ssl_conf_own_cert(&conf, &srvcert, &pkey)) != 0) { + mbedtls_printf(" failed\n ! mbedtls_ssl_conf_own_cert returned %d\n\n", ret); + goto exit; + } + + if ((ret = mbedtls_ssl_setup(&ssl, &conf)) != 0) { + mbedtls_printf(" failed\n ! mbedtls_ssl_setup returned %d\n", ret); + goto exit; + } + mbedtls_printf(" ok\n"); +reset: + if (ret != 0) { + mbedtls_printf("Last error was: %d\n", ret); + } + mbedtls_net_free(&client_fd); + mbedtls_ssl_session_reset(&ssl); + /* Wait until a client connects */ + mbedtls_printf("Waiting for a remote connection.\n"); + if ((ret = mbedtls_net_accept(&listen_fd, &client_fd, + NULL, 0, NULL)) != 0) { + mbedtls_printf(" failed\n ! mbedtls_net_accept returned %d\n", ret); + goto exit; + } + mbedtls_ssl_set_bio(&ssl, &client_fd, mbedtls_net_send, mbedtls_net_recv, NULL); + mbedtls_printf(" ok\n"); + + /* Handshake */ + mbedtls_printf("Performing the SSL/TLS handshake.\n"); + while ((ret = mbedtls_ssl_handshake(&ssl)) != 0) { + if(ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) { + mbedtls_printf(" failed\n ! mbedtls_ssl_handshake returned %d\n", ret); + goto reset; + } + } + mbedtls_printf(" ok(%s)\n",mbedtls_ssl_get_ciphersuite(&ssl)); + mbedtls_printf("Read from client.\n"); + + unsigned char checksum = 0; + mbedtls_string_mismatch = -1; + + do { + len = TLS_TEST_BUF_SIZE; + memset( buf, 0, TLS_TEST_BUF_SIZE ); + ret = mbedtls_ssl_read(&ssl, buf, len); + if(ret == MBEDTLS_ERR_SSL_WANT_READ || ret == MBEDTLS_ERR_SSL_WANT_WRITE) + continue; + if (ret <= 0) { + switch (ret) { + case MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY: + mbedtls_printf(" connection was closed gracefully\n"); + break; + case MBEDTLS_ERR_NET_CONN_RESET: + mbedtls_printf(" connection was reset by peer\n"); + break; + default: + mbedtls_printf(" mbedtls_ssl_read returned -0x%x\n", -ret); + break; + } + break; + } + + len = ret; + checksum += tls_cal_checksum(buf, len); + mbedtls_printf(" %d bytes read\n%s\n", len, (char *) buf); + } while (1); + + if (checksum == 0xFF) + mbedtls_string_mismatch = 0; + else { + mbedtls_string_mismatch = -1; + mbedtls_printf("checksum: 0x%x\n", checksum); + } +#if 0 + /* Write Response */ + mbedtls_printf("Write to client:\n"); + while ((ret = mbedtls_ssl_write(&ssl, buf, len)) <= 0) { + if (ret == MBEDTLS_ERR_NET_CONN_RESET) { + mbedtls_printf(" failed\n ! peer closed the connection\n"); + goto reset; + } + if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) { + mbedtls_printf( " failed\n ! mbedtls_ssl_write returned %d\n\n", ret ); + goto exit; + } + } +#endif + mbedtls_printf("Closing the connection...\n"); + while ((ret = mbedtls_ssl_close_notify(&ssl)) < 0) { + if (ret != MBEDTLS_ERR_SSL_WANT_READ && + ret != MBEDTLS_ERR_SSL_WANT_WRITE) { + mbedtls_printf("Warn : mbedtls_ssl_close_notify returned %d\n\n", ret); + //goto reset; + break; + } + } + mbedtls_printf(" ok\n"); + ret = 0; + +exit: + if (ret != 0) + mbedtls_printf("Last error was: %d\n\n", ret); + + mbedtls_net_free(&client_fd); + mbedtls_net_free(&listen_fd); + + mbedtls_x509_crt_free(&srvcert); + mbedtls_pk_free(&pkey); + mbedtls_ssl_free(&ssl); + mbedtls_ssl_config_free(&conf); +#if defined(MBEDTLS_SSL_CACHE_C) + mbedtls_ssl_cache_free(&cache); +#endif + mbedtls_ctr_drbg_free(&ctr_drbg); + mbedtls_entropy_free(&entropy); + FREE_BUF(buf); +} +#endif /* MBEDTLS_BIGNUM_C && MBEDTLS_CERTS_C && MBEDTLS_ENTROPY_C && + MBEDTLS_SSL_TLS_C && MBEDTLS_SSL_SRV_C && MBEDTLS_NET_C && + MBEDTLS_RSA_C && MBEDTLS_CTR_DRBG_C && MBEDTLS_X509_CRT_PARSE_C + && MBEDTLS_FS_IO && MBEDTLS_PEM_PARSE_C */ diff --git a/platform/mcu/xr871/project/common/cmd/tls/tls.c b/platform/mcu/xr871/project/common/cmd/tls/tls.c new file mode 100644 index 0000000000..74570b226b --- /dev/null +++ b/platform/mcu/xr871/project/common/cmd/tls/tls.c @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include "tls.h" + +extern void mbedtls_client( void *arg); +extern void mbedtls_server( void *arg); + +static const OS_ThreadEntry_t tls_thread_entry[2] = { + mbedtls_server, + mbedtls_client, +}; + +OS_Thread_t g_tls_thread; +volatile int mbedtls_string_mismatch = 0; + +void tls_thread_start(void *param) +{ + printf(" \n"); + int client = (((mbedtls_test_param *)param)->flags & (MBEDTLS_SSL_FLAG_CLINET + |MBEDTLS_SSL_FLAG_WEBCLIENT)) == 0 ? 0 : 1; + mbedtls_string_mismatch = -1; + tls_thread_entry[client](param); + + char * str = mbedtls_string_mismatch == 0 ? "success" : "fail"; + printf(" \n",str); + tls_thread_stop(); +} + +int tls_thread_stop() +{ + if (OS_ThreadIsValid(&g_tls_thread)) { + TLS_THREAD_EXIT(&g_tls_thread); + return 0; + } else + return -1; +} + +int tls_start(mbedtls_test_param *param) +{ + if (OS_ThreadIsValid(&g_tls_thread)) { + mbedtls_printf("tls task is running\n"); + return -1; + } + + if (OS_ThreadCreate(&g_tls_thread, + "", + tls_thread_start, + (void *)param, + OS_THREAD_PRIO_APP, + TLS_THREAD_STACK_SIZE) != OS_OK) { + mbedtls_printf("tls task create failed\n"); + return -1; + } + return 0; +} diff --git a/platform/mcu/xr871/project/common/cmd/tls/tls.h b/platform/mcu/xr871/project/common/cmd/tls/tls.h new file mode 100644 index 0000000000..1f33d36855 --- /dev/null +++ b/platform/mcu/xr871/project/common/cmd/tls/tls.h @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef __TLS_H_H +#define __TLS_H_H + +#define MBEDTLS_CLIENT_SERVER + +#if defined (MBEDTLS_CLIENT) +#include "net/mbedtls/configs/config-xr-mini-cli.h" +#endif +#if defined (MBEDTLS_CLIENT_SERVER) +#include "net/mbedtls/configs/config-xr-mini-cliserv.h" +#endif +#if defined (MBEDTLS_SERVER) +#include "net/mbedtls/configs/config-xr-mini-serv.h" +#endif + +#if defined(MBEDTLS_PLATFORM_C) +#include "mbedtls/platform.h" +#else +#include +#include +#define mbedtls_fprintf fprintf +#define mbedtls_printf printf +#define mbedtls_calloc calloc +#define mbedtls_strlen strlen +#define mbedtls_strncmp strncmp +#endif + +#include "kernel/os/os_time.h" +#include "kernel/os/os_thread.h" +#include "kernel/os/FreeRTOS/os_common.h" + +#ifndef DEBUG_LEVEL +#define DEBUG_LEVEL 0 +#endif + +#ifndef DEFALT_TEST_TIME +#define DEFALT_TEST_TIME 0 //ms +#endif + +#ifndef TLS_TEST_BUF_SIZE +#define TLS_TEST_BUF_SIZE 4096 +#endif + +#ifndef TLS_GET_TIME +#define TLS_GET_TIME OS_GetTicks +#endif + +#ifndef TLS_THREAD_STACK_SIZE +#define TLS_THREAD_STACK_SIZE (6 * 1024) +#endif + +#define SERVER_PORT "10000" +#define CLINET_PORT "10001" +#define CLIENT_DATA "1234567890QWERTYUIOPASDFGHJKLZXCVBNMfjafjakfjlak"\ + "jlakfjlakjflkajflkajflkjflkajflakfjlak" +typedef struct +{ + char server_port[10]; + char server_name[30]; + unsigned int continue_ms; + unsigned int flags; +} mbedtls_test_param; + +typedef enum +{ + MBEDTLS_SSL_FLAG_SERVER_PORT = 0x00000001, + MBEDTLS_SSL_FLAG_SERVER_NAME = 0x00000002, + MBEDTLS_SSL_FLAG_CONTINUE = 0x00000004, + MBEDTLS_SSL_FLAG_CLINET = 0x00000008, + MBEDTLS_SSL_FLAG_SERVER = 0x00000010, + MBEDTLS_SSL_FLAG_WEBSERVER = 0x00000020, + MBEDTLS_SSL_FLAG_WEBCLIENT = 0x00000040, +} mbedtls_test_flags; + +#define TLS_THREAD_EXIT OS_ThreadDelete +#define FREE_BUF(P) \ + if (P != NULL) { \ + free(P);\ + P = NULL;} + +extern const char mbedtls_custom_cas_pem[]; +extern const size_t mbedtls_custom_cas_pem_len; +int tls_start(mbedtls_test_param *param); +int tls_thread_stop(); + +#endif /* __TLS_H_H */ + diff --git a/platform/mcu/xr871/project/common/framework/ctrl_msg_debug.h b/platform/mcu/xr871/project/common/framework/ctrl_msg_debug.h new file mode 100644 index 0000000000..41e7448328 --- /dev/null +++ b/platform/mcu/xr871/project/common/framework/ctrl_msg_debug.h @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _CTRL_MSG_DEBUG_H_ +#define _CTRL_MSG_DEBUG_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define CTRL_MSG_DBG_ON 0 +#define CTRL_MSG_WRN_ON 1 +#define CTRL_MSG_ERR_ON 1 +#define CTRL_MSG_ABORT_ON 0 + +#define CTRL_MSG_VALIDITY_CHECK 0 + +#define CTRL_MSG_SYSLOG printf +#define CTRL_MSG_ABORT() do { } while (0) + +#define CTRL_MSG_LOG(flags, fmt, arg...) \ + do { \ + if (flags) \ + CTRL_MSG_SYSLOG(fmt, ##arg); \ + } while (0) + +#define CTRL_MSG_DBG(fmt, arg...) \ + CTRL_MSG_LOG(CTRL_MSG_DBG_ON, "[ctrlmsg] "fmt, ##arg) + +#define CTRL_MSG_WRN(fmt, arg...) \ + CTRL_MSG_LOG(CTRL_MSG_WRN_ON, "[ctrlmsg WRN] "fmt, ##arg) + +#define CTRL_MSG_ERR(fmt, arg...) \ + do { \ + CTRL_MSG_LOG(CTRL_MSG_ERR_ON, "[ctrlmsg ERR] %s():%d, "fmt, \ + __func__, __LINE__, ##arg); \ + if (CTRL_MSG_ABORT_ON) \ + CTRL_MSG_ABORT(); \ + } while (0) + +#ifdef __cplusplus +} +#endif + +#endif /* _CTRL_MSG_DEBUG_H_ */ diff --git a/platform/mcu/xr871/project/common/framework/fwk_debug.h b/platform/mcu/xr871/project/common/framework/fwk_debug.h new file mode 100644 index 0000000000..29f003ab0c --- /dev/null +++ b/platform/mcu/xr871/project/common/framework/fwk_debug.h @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _FWK_DEBUG_H_ +#define _FWK_DEBUG_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define FWK_DBG_ON 0 +#define FWK_WRN_ON 0 +#define FWK_ERR_ON 1 +#define FWK_ABORT_ON 0 + +#define FWK_SYSLOG printf +#define FWK_ABORT() do { } while (0) + +#define FWK_LOG(flags, fmt, arg...) \ + do { \ + if (flags) \ + FWK_SYSLOG(fmt, ##arg); \ + } while (0) + +#define FWK_DBG(fmt, arg...) \ + FWK_LOG(FWK_DBG_ON, "[fwk] "fmt, ##arg) + +#define FWK_WRN(fmt, arg...) \ + FWK_LOG(FWK_WRN_ON, "[fwk WRN] "fmt, ##arg) + +#define FWK_ERR(fmt, arg...) \ + do { \ + FWK_LOG(FWK_ERR_ON, "[fwk ERR] %s():%d, "fmt, \ + __func__, __LINE__, ##arg); \ + if (FWK_ABORT_ON) \ + FWK_ABORT(); \ + } while (0) + +#ifdef __cplusplus +} +#endif + +#endif /* _FWK_DEBUG_H_ */ diff --git a/platform/mcu/xr871/project/common/framework/img_ctrl.c b/platform/mcu/xr871/project/common/framework/img_ctrl.c new file mode 100644 index 0000000000..f54eaf0312 --- /dev/null +++ b/platform/mcu/xr871/project/common/framework/img_ctrl.c @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include "img_ctrl.h" +#include "img_ctrl_debug.h" + +#include "common/board/board.h" +#include "driver/chip/hal_wdg.h" +#include "sys/image.h" +#include "sys/ota.h" + +static void img_ctrl_ota_init(image_ota_param_t *param) +{ + + image_seq_t seq; + ota_cfg_t cfg; + + ota_init(param); + if (ota_read_cfg(&cfg) != OTA_STATUS_OK) + IMG_CTRL_ERR("ota read cfg failed\n"); + + if (((cfg.image == OTA_IMAGE_1ST) && (cfg.state == OTA_STATE_VERIFIED)) + || ((cfg.image == OTA_IMAGE_2ND) && (cfg.state == OTA_STATE_UNVERIFIED))) { + seq = IMAGE_SEQ_1ST; + } else if (((cfg.image == OTA_IMAGE_2ND) && (cfg.state == OTA_STATE_VERIFIED)) + || ((cfg.image == OTA_IMAGE_1ST) && (cfg.state == OTA_STATE_UNVERIFIED))) { + seq = IMAGE_SEQ_2ND; + } else { + IMG_CTRL_ERR("invalid image %d, state %d\n", cfg.image, cfg.state); + seq = IMAGE_SEQ_1ST; + } + + IMG_CTRL_DBG("image seq %d\n", seq); + image_set_running_seq(seq); + +} + +void img_ctrl_init(uint32_t flash, uint32_t addr, uint32_t size) +{ + image_ota_param_t param; + + image_init(flash, addr, size); + image_get_ota_param(¶m); + + if (param.addr[IMAGE_SEQ_2ND] == IMAGE_INVALID_ADDR) + image_set_running_seq(IMAGE_SEQ_1ST); + else + img_ctrl_ota_init(¶m); +} + diff --git a/platform/mcu/xr871/project/common/framework/img_ctrl.h b/platform/mcu/xr871/project/common/framework/img_ctrl.h new file mode 100644 index 0000000000..e24a2d88a8 --- /dev/null +++ b/platform/mcu/xr871/project/common/framework/img_ctrl.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _IMG_CTRL_H_ +#define _IMG_CTRL_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +void img_ctrl_init(uint32_t flash, uint32_t addr, uint32_t size); + +#ifdef __cplusplus +} +#endif + +#endif /* _IMG_CTRL_H_ */ diff --git a/platform/mcu/xr871/project/common/framework/img_ctrl_debug.h b/platform/mcu/xr871/project/common/framework/img_ctrl_debug.h new file mode 100644 index 0000000000..3755d50c0a --- /dev/null +++ b/platform/mcu/xr871/project/common/framework/img_ctrl_debug.h @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _IMG_CTRL_DEBUG_H_ +#define _IMG_CTRL_DEBUG_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define IMG_CTRL_DBG_ON 0 +#define IMG_CTRL_WRN_ON 1 +#define IMG_CTRL_ERR_ON 1 +#define IMG_CTRL_ABORT_ON 0 + +#define IMG_CTRL_VALIDITY_CHECK 0 + +#define IMG_CTRL_SYSLOG printf +#define IMG_CTRL_ABORT() do { } while (0) + +#define IMG_CTRL_LOG(flags, fmt, arg...) \ + do { \ + if (flags) \ + IMG_CTRL_SYSLOG(fmt, ##arg); \ + } while (0) + +#define IMG_CTRL_DBG(fmt, arg...) \ + IMG_CTRL_LOG(IMG_CTRL_DBG_ON, "[imgctrl] "fmt, ##arg) + +#define IMG_CTRL_WRN(fmt, arg...) \ + IMG_CTRL_LOG(IMG_CTRL_WRN_ON, "[imgctrl WRN] "fmt, ##arg) + +#define IMG_CTRL_ERR(fmt, arg...) \ + do { \ + IMG_CTRL_LOG(IMG_CTRL_ERR_ON, "[imgctrl ERR] %s():%d, "fmt, \ + __func__, __LINE__, ##arg); \ + if (IMG_CTRL_ABORT_ON) \ + IMG_CTRL_ABORT(); \ + } while (0) + +#ifdef __cplusplus +} +#endif + +#endif /* _IMG_CTRL_DEBUG_H_ */ diff --git a/platform/mcu/xr871/project/common/framework/net_ctrl.c b/platform/mcu/xr871/project/common/framework/net_ctrl.c new file mode 100644 index 0000000000..882293a987 --- /dev/null +++ b/platform/mcu/xr871/project/common/framework/net_ctrl.c @@ -0,0 +1,432 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include "lwip/tcpip.h" +#include "lwip/inet.h" +#include "lwip/dhcp.h" +#include "lwip/netifapi.h" +#include "net/wlan/wlan.h" +#include "net/udhcp/usr_dhcpd.h" + +#include "common/framework/sys_ctrl/sys_ctrl.h" +#include "common/framework/sysinfo.h" +#include "net_ctrl.h" +#include "net_ctrl_debug.h" + +struct netif_conf { + uint8_t bring_up; // bring up or down + uint8_t use_dhcp; // use DHCP or not +#ifdef __CONFIG_LWIP_V1 + ip_addr_t ipaddr; + ip_addr_t netmask; + ip_addr_t gw; +#elif LWIP_IPV4 /* now only for IPv4 */ + ip4_addr_t ipaddr; + ip4_addr_t netmask; + ip4_addr_t gw; +#else + #error "IPv4 not support!" +#endif +}; + +#if LWIP_NETIF_LINK_CALLBACK +static void netif_link_callback(struct netif *netif) +{ + if (netif_is_link_up(netif)) { + NET_INF("netif is link up\n"); + } else { + NET_INF("netif is link down\n"); + } +} +#endif /* LWIP_NETIF_LINK_CALLBACK */ + +#if LWIP_NETIF_STATUS_CALLBACK +static void netif_status_callback(struct netif *netif) +{ + if (NET_IS_IP4_VALID(netif)) { + NET_INF("netif is up\n"); + NET_INF("address: %s\n", inet_ntoa(netif->ip_addr)); +#if LWIP_IPV6 + NET_INF("ipv6 address: %s\n", inet6_ntoa(netif->ip6_addr)); +#endif + NET_INF("gateway: %s\n", inet_ntoa(netif->gw)); + NET_INF("netmask: %s\n", inet_ntoa(netif->netmask)); + net_ctrl_msg_send(NET_CTRL_MSG_NETWORK_UP, 0); + } else { + NET_INF("netif is down\n"); + net_ctrl_msg_send(NET_CTRL_MSG_NETWORK_DOWN, 0); + } +} +#endif /* LWIP_NETIF_STATUS_CALLBACK */ + +#if LWIP_NETIF_REMOVE_CALLBACK +static void netif_remove_callback(struct netif *netif) +{ + NET_DBG("%s()\n", __func__); +} +#endif /* LWIP_NETIF_REMOVE_CALLBACK */ + +#ifdef __CONFIG_LWIP_V1 +#define NET_DHCP_DATA(nif) (nif->dhcp) +#define NET_DHCP_STATE(nif) (nif->dhcp->state) +#define NET_DHCP_STATE_OFF DHCP_OFF +#define NET_DHCP_STATE_BOUND DHCP_BOUND +//#define NET_IP4_ADDR_ANY IP_ADDR_ANY +#elif LWIP_IPV4 /* now only for IPv4 */ +#include "lwip/prot/dhcp.h" +#define NET_DHCP_DATA(nif) netif_dhcp_data(nif) +#define NET_DHCP_STATE(nif) (netif_dhcp_data(nif)->state) +#define NET_DHCP_STATE_OFF DHCP_STATE_OFF +#define NET_DHCP_STATE_BOUND DHCP_STATE_BOUND +//#define NET_IP4_ADDR_ANY IP4_ADDR_ANY4 +#else +#error "IPv4 not support!" +#endif + +static void netif_config(struct netif *nif, struct netif_conf *conf) +{ + if (conf->bring_up) { + if (NET_IS_IP4_VALID(nif)) { + NET_INF("netif is already up\n"); + return; + } + if (conf->use_dhcp) { + if (NET_DHCP_DATA(nif) && + NET_DHCP_STATE(nif) != NET_DHCP_STATE_OFF) { + NET_INF("DHCP is already started\n"); + return; + } + + NET_INF("start DHCP...\n"); + if (netifapi_dhcp_start(nif) != ERR_OK) { + NET_ERR("DHCP start failed!\n"); + return; + } + } else { + netifapi_netif_set_addr(nif, &conf->ipaddr, &conf->netmask, &conf->gw); +#ifdef __CONFIG_LWIP_V1 + netifapi_netif_set_up(nif); +#endif + } + } else { + if (conf->use_dhcp) { + if (NET_DHCP_DATA(nif) == NULL) { + NET_INF("DHCP is not started\n"); + return; + } + if (netif_is_link_up(nif)) { + NET_INF("release DHCP\n"); + netifapi_dhcp_release(nif); + } else { + NET_INF("bring down netif\n"); +#ifdef __CONFIG_LWIP_V1 + netifapi_netif_set_down(nif); +#endif + netifapi_netif_set_addr(nif, NULL, NULL, NULL); + } + + NET_INF("stop DHCP\n"); + netifapi_dhcp_stop(nif); + } else { + if (!NET_IS_IP4_VALID(nif)) { + NET_INF("netif is already down\n"); + return; + } + NET_INF("bring down netif\n"); +#ifdef __CONFIG_LWIP_V1 + netifapi_netif_set_down(nif); +#endif + netifapi_netif_set_addr(nif, NULL, NULL, NULL); + } + } +} + +/* bring up/down network */ +void net_config(struct netif *nif, uint8_t bring_up) +{ + struct sysinfo *sysinfo = sysinfo_get(); + if (sysinfo == NULL) { + NET_ERR("failed to get sysinfo %p\n", sysinfo); + return; + } + + struct netif_conf net_conf; + memset(&net_conf, 0, sizeof(struct netif_conf)); + net_conf.bring_up = bring_up; + + if (sysinfo->wlan_mode == WLAN_MODE_STA) { + if (sysinfo->sta_use_dhcp) { + net_conf.use_dhcp = 1; + } else { + net_conf.use_dhcp = 0; + memcpy(&net_conf.ipaddr, &sysinfo->netif_sta_param.ip_addr, sizeof(net_conf.ipaddr)); + memcpy(&net_conf.netmask, &sysinfo->netif_sta_param.net_mask, sizeof(net_conf.netmask)); + memcpy(&net_conf.gw, &sysinfo->netif_sta_param.gateway, sizeof(net_conf.gw)); + } + } else if (sysinfo->wlan_mode == WLAN_MODE_HOSTAP) { + net_conf.use_dhcp = 0; + memcpy(&net_conf.ipaddr, &sysinfo->netif_ap_param.ip_addr, sizeof(net_conf.ipaddr)); + memcpy(&net_conf.netmask, &sysinfo->netif_ap_param.net_mask, sizeof(net_conf.netmask)); + memcpy(&net_conf.gw, &sysinfo->netif_ap_param.gateway, sizeof(net_conf.gw)); + } else { + NET_ERR("invalid wlan mode %d\n", sysinfo->wlan_mode); + return; + } + + netif_config(nif, &net_conf); +} + +struct netif *net_open(enum wlan_mode mode) +{ + struct netif *nif; + + NET_DBG("%s(), mode %d\n", __func__, mode); + + wlan_attach(); + nif = wlan_if_create(mode); + if (nif == NULL) { + return NULL; + } + +#ifndef __CONFIG_LWIP_V1 +#if LWIP_IPV6 + netif_create_ip6_linklocal_address(nif, 1); + #if LWIP_IPV6_AUTOCONFIG + netif_set_ip6_autoconfig_enabled(nif, 1); + #endif +#endif /* LWIP_IPV6 */ + /* set netif up, but no valid ip address, required by lwip-2.x.x */ + //netifapi_netif_set_up(nif); +#endif /* __CONFIG_LWIP_V1 */ + +#if LWIP_NETIF_LINK_CALLBACK + netif_set_link_callback(nif, netif_link_callback); +#endif +#if LWIP_NETIF_STATUS_CALLBACK + netif_set_status_callback(nif, netif_status_callback); +#endif +#if LWIP_NETIF_REMOVE_CALLBACK + netif_set_remove_callback(nif, netif_remove_callback); +#endif + wlan_start(nif); + + struct sysinfo *sysinfo = sysinfo_get(); + if (sysinfo) { + sysinfo->wlan_mode = mode; + } + + return nif; +} + +void _net_close(struct netif *nif) +{ + NET_DBG("%s(), mode %d\n", __func__, wlan_if_get_mode(nif)); + + wlan_stop(); +#if 0 +#if LWIP_NETIF_REMOVE_CALLBACK + netif_set_remove_callback(nif, NULL); +#endif +#if LWIP_NETIF_STATUS_CALLBACK + netif_set_status_callback(nif, NULL); +#endif +#if LWIP_NETIF_LINK_CALLBACK + netif_set_link_callback(nif, NULL); +#endif +#endif + wlan_if_delete(nif); + wlan_detach(); +} + +int net_switch_mode(enum wlan_mode mode) +{ + int ret = -1; + enum wlan_mode cur_mode; + struct netif *nif = g_wlan_netif; + + NET_DBG("%s(), mode %d --> %d\n", __func__, wlan_if_get_mode(nif), mode); + + if (nif == NULL) { + return net_sys_start(mode); /* start net sys only */ + } + + if (mode >= WLAN_MODE_NUM) { + NET_WRN("invalid wlan mode %d\n", mode); + return ret; + } + + cur_mode = wlan_if_get_mode(nif); + if (mode == cur_mode) { + NET_INF("no need to switch wlan mode %d\n", cur_mode); + return 0; + } + + if (NET_IS_IP4_VALID(nif)) { + net_config(nif, 0); /* bring down netif */ + } + + if (cur_mode == WLAN_MODE_STA && netif_is_link_up(nif)) { + wlan_sta_disable(); /* disconnect wlan */ + } + + switch (mode) { + case WLAN_MODE_HOSTAP: + net_sys_stop(); + ret = net_sys_start(mode); + break; + case WLAN_MODE_STA: + case WLAN_MODE_MONITOR: + if (cur_mode == WLAN_MODE_HOSTAP) { + net_sys_stop(); + ret = net_sys_start(mode); + } else { + _net_close(nif); + nif = net_open(mode); + g_wlan_netif = nif; + if (nif) { + ret = 0; + } + } + break; + default: + break; + } + + return ret; +} + +int net_ctrl_msg_send(uint16_t type, uint32_t data) +{ + return sys_event_send(CTRL_MSG_TYPE_NETWORK, type, data, OS_WAIT_FOREVER); +} + +int net_ctrl_msg_send_with_free(uint16_t type, uint32_t data) +{ + return sys_event_send_with_free(CTRL_MSG_TYPE_NETWORK, type, data, OS_WAIT_FOREVER); +} + +#if NET_INF_ON +const char *net_ctrl_msg_str[] = { + "wlan connected", + "wlan disconnected", + "wlan scan success", + "wlan scan failed", + "wlan 4way handshake failed", + "wlan connect failed", + "network up", + "network down", +}; +#endif + +int net_ctrl_connect_ap(void) +{ + return 0; +} + +int net_ctrl_disconnect_ap(void) +{ + return 0; +} + +void net_ctrl_msg_process(uint32_t event, uint32_t data) +{ + uint16_t type = EVENT_SUBTYPE(event); + NET_INF("msg <%s>\n", net_ctrl_msg_str[type]); + + switch (type) { + case NET_CTRL_MSG_WLAN_CONNECTED: + if (g_wlan_netif && !netif_is_link_up(g_wlan_netif)) { + //netifapi_netif_set_link_up(g_wlan_netif); /* set link up */ + netifapi_netif_common(g_wlan_netif, netif_set_link_up, NULL); +#if (defined(__CONFIG_LWIP_V1) || LWIP_IPV4) + netifapi_netif_set_up(g_wlan_netif); + net_config(g_wlan_netif, 1); /* bring up network */ +#endif + } + break; + case NET_CTRL_MSG_WLAN_DISCONNECTED: + if (g_wlan_netif) { + //netifapi_netif_set_link_down(g_wlan_netif); /* set link down */ + netifapi_netif_common(g_wlan_netif, netif_set_link_down, NULL); + } +#if (defined(__CONFIG_LWIP_V1) || LWIP_IPV4) + /* if dhcp is started and not bound, stop it */ + if (g_wlan_netif && NET_DHCP_DATA(g_wlan_netif) && + NET_DHCP_STATE(g_wlan_netif) != NET_DHCP_STATE_OFF && + NET_DHCP_STATE(g_wlan_netif) != NET_DHCP_STATE_BOUND) { + net_config(g_wlan_netif, 0); + } +#endif + break; + case NET_CTRL_MSG_WLAN_SCAN_SUCCESS: + break; + case NET_CTRL_MSG_WLAN_SCAN_FAILED: + break; + case NET_CTRL_MSG_WLAN_4WAY_HANDSHAKE_FAILED: + break; + case NET_CTRL_MSG_WLAN_CONNECT_FAILED: + break; + case NET_CTRL_MSG_NETWORK_UP: +#if (defined(__CONFIG_LWIP_V1) || LWIP_IPV4) + if (g_wlan_netif) { + enum wlan_mode mode = wlan_if_get_mode(g_wlan_netif); + if (mode == WLAN_MODE_STA) { + uint32_t addr = NET_IP_ADDR_GET_IP4U32(&g_wlan_netif->ip_addr); + wlan_set_ip_addr(g_wlan_netif, (uint8_t *)&addr, sizeof(addr)); + } else if (mode == WLAN_MODE_HOSTAP) { + NET_INF("dhcp_server_start...\n"); + dhcp_server_start(NULL); + } else { + NET_ERR("Invalid wlan mode %d\n", mode); + } + } +#endif + break; + case NET_CTRL_MSG_NETWORK_DOWN: + break; + default: + NET_WRN("unknown msg (%u, %u)\n", type, data); + break; + } +} + +int net_ctrl_init(void) +{ + observer_base *ob = sys_callback_observer_create(CTRL_MSG_TYPE_NETWORK, + NET_CTRL_MSG_ALL, + net_ctrl_msg_process); + if (ob == NULL) + return -1; + if (sys_ctrl_attach(ob) != 0) + return -1; + + return 0; +} diff --git a/platform/mcu/xr871/project/common/framework/net_ctrl.h b/platform/mcu/xr871/project/common/framework/net_ctrl.h new file mode 100644 index 0000000000..e036a4738c --- /dev/null +++ b/platform/mcu/xr871/project/common/framework/net_ctrl.h @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _NET_CTRL_H_ +#define _NET_CTRL_H_ + +#include "lwip/netif.h" +#include "net/wlan/wlan.h" +#include "net/wlan/wlan_defs.h" +#include "sys_ctrl/sys_ctrl.h" + +#ifdef __cplusplus +extern "C" { +#endif + +//#define CONFIG_AUTO_RECONNECT_AP + +enum net_ctrl_msg_type { + NET_CTRL_MSG_WLAN_CONNECTED = WLAN_EVENT_CONNECTED, + NET_CTRL_MSG_WLAN_DISCONNECTED = WLAN_EVENT_DISCONNECTED, + NET_CTRL_MSG_WLAN_SCAN_SUCCESS = WLAN_EVENT_SCAN_SUCCESS, + NET_CTRL_MSG_WLAN_SCAN_FAILED = WLAN_EVENT_SCAN_FAILED, + NET_CTRL_MSG_WLAN_4WAY_HANDSHAKE_FAILED = WLAN_EVENT_4WAY_HANDSHAKE_FAILED, + NET_CTRL_MSG_WLAN_CONNECT_FAILED = WLAN_EVENT_CONNECT_FAILED, + + NET_CTRL_MSG_NETWORK_UP, + NET_CTRL_MSG_NETWORK_DOWN, + + NET_CTRL_MSG_ALL = ALL_SUBTYPE, +}; + +#ifdef __CONFIG_LWIP_V1 +#define NET_IP_ADDR_GET_IP4U32(addr) ip4_addr_get_u32(addr) +#define NET_IS_IP4_VALID(nif) (netif_is_up(nif) && \ + !ip_addr_isany(&((nif)->ip_addr))) +#elif LWIP_IPV4 /* now only for IPv4 */ +#define NET_IP_ADDR_GET_IP4U32(addr) ip_addr_get_ip4_u32(addr) +#define NET_IS_IP4_VALID(nif) (netif_is_up(nif) && \ + !ip_addr_isany(&((nif)->ip_addr))) +#else +#error "IPv4 not support!" +#endif + +extern struct netif *g_wlan_netif; + +void net_sys_init(void); +int net_sys_start(enum wlan_mode mode); +int net_sys_stop(void); +int net_sys_onoff(unsigned int enable); + +struct netif *net_open(enum wlan_mode mode); +void _net_close(struct netif *nif); +int net_switch_mode(enum wlan_mode mode); +void net_config(struct netif *nif, uint8_t bring_up); +int net_ctrl_connect_ap(void); +int net_ctrl_disconnect_ap(void); + +int net_ctrl_init(void); +int net_ctrl_msg_send(uint16_t type, uint32_t data); +int net_ctrl_msg_send_with_free(uint16_t type, uint32_t data); +void net_ctrl_msg_process(uint32_t event, uint32_t data); + +#ifdef __cplusplus +} +#endif + +#endif /* _NET_CTRL_H_ */ diff --git a/platform/mcu/xr871/project/common/framework/net_ctrl_debug.h b/platform/mcu/xr871/project/common/framework/net_ctrl_debug.h new file mode 100644 index 0000000000..1216e9fd27 --- /dev/null +++ b/platform/mcu/xr871/project/common/framework/net_ctrl_debug.h @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _NET_CTRL_DEBUG_H_ +#define _NET_CTRL_DEBUG_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define NET_DBG_ON 0 +#define NET_INF_ON 1 +#define NET_WRN_ON 1 +#define NET_ERR_ON 1 +#define NET_ABORT_ON 0 + +#define NET_SYSLOG printf +#define NET_ABORT() do { } while (0) + +#define NET_LOG(flags, fmt, arg...) \ + do { \ + if (flags) \ + NET_SYSLOG(fmt, ##arg); \ + } while (0) + +#define NET_DBG(fmt, arg...) \ + NET_LOG(NET_DBG_ON, "[net DBG] "fmt, ##arg) + +#define NET_INF(fmt, arg...) \ + NET_LOG(NET_INF_ON, "[net INF] "fmt, ##arg) + +#define NET_WRN(fmt, arg...) \ + NET_LOG(NET_WRN_ON, "[net WRN] "fmt, ##arg) + +#define NET_ERR(fmt, arg...) \ + do { \ + NET_LOG(NET_ERR_ON, "[net ERR] %s():%d, "fmt, \ + __func__, __LINE__, ##arg); \ + if (NET_ABORT_ON) \ + NET_ABORT(); \ + } while (0) + +#ifdef __cplusplus +} +#endif + +#endif /* _NET_CTRL_DEBUG_H_ */ diff --git a/platform/mcu/xr871/project/common/framework/net_sys.c b/platform/mcu/xr871/project/common/framework/net_sys.c new file mode 100644 index 0000000000..f274039840 --- /dev/null +++ b/platform/mcu/xr871/project/common/framework/net_sys.c @@ -0,0 +1,158 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include "kernel/os/os.h" +#include "sys/ducc/ducc_net.h" +#include "sys/ducc/ducc_app.h" +#include "net/wlan/wlan.h" +#include "net/wlan/wlan_defs.h" +#include "lwip/tcpip.h" +#include "net/udhcp/usr_dhcpd.h" + +#include "common/framework/sysinfo.h" +#include "common/board/board.h" +#include "net_ctrl.h" +#include "net_ctrl_debug.h" + +#include "driver/chip/hal_prcm.h" + +struct netif *g_wlan_netif = NULL; + +static void netif_tcpip_init_done(void *arg) +{ +// NET_DBG("%s()\n", __func__); +} + +void net_sys_init(void) +{ + tcpip_init(netif_tcpip_init_done, NULL); /* Init lwip module */ +} + + +static int net_sys_callback(uint32_t param0, uint32_t param1) +{ + switch (param0) { + case DUCC_NET_CMD_WLAN_EVENT: + switch (param1) { + case WLAN_EVENT_CONNECTED: + case WLAN_EVENT_DISCONNECTED: + case WLAN_EVENT_SCAN_SUCCESS: + case WLAN_EVENT_SCAN_FAILED: + case WLAN_EVENT_4WAY_HANDSHAKE_FAILED: + case WLAN_EVENT_CONNECT_FAILED: + net_ctrl_msg_send(param1, 0); + break; + default: + break; + } + break; + default: + break; + } + return 0; +} + +int net_sys_start(enum wlan_mode mode) +{ + struct sysinfo *sysinfo = sysinfo_get(); + if (sysinfo == NULL) { + NET_ERR("failed to get sysinfo %p\n", sysinfo); + return -1; + } + + if (wlan_sys_init(mode, net_sys_callback) != 0) { + NET_ERR("net system start failed\n"); + return -1; + } +#if PRJCONF_UART_EN + /* netos debug output is disable by default */ + UART_T *uart; + if (BOARD_SUB_UART_ID < UART_NUM) { + uart = HAL_UART_GetInstance(BOARD_SUB_UART_ID); + } else { + uart = NULL; /* disable netos debug output */ + } + ducc_app_ioctl(DUCC_APP_CMD_UART_CONFIG, uart); +#endif + +#ifndef __PRJ_CONFIG_ETF_CLI + wlan_set_mac_addr(sysinfo->mac_addr, SYSINFO_MAC_ADDR_LEN); + g_wlan_netif = net_open(mode); +#endif + return 0; +} + +int net_sys_stop(void) +{ + if (g_wlan_netif && wlan_if_get_mode(g_wlan_netif) == WLAN_MODE_HOSTAP) { + NET_INF("dhcp_server_stop...\n"); + dhcp_server_stop(); + } + + _net_close(g_wlan_netif); + g_wlan_netif = NULL; + + while (HAL_PRCM_IsSys3Alive()) { + OS_MSleep(10); /* wait net */ + } + + if (wlan_sys_deinit()) { + NET_ERR("net system stop failed\n"); + return -1; + } + + return 0; +} + +int net_sys_onoff(unsigned int enable) +{ + struct sysinfo *sysinfo = sysinfo_get(); + if (sysinfo == NULL) { + NET_ERR("failed to get sysinfo %p\n", sysinfo); + return -1; + } + + printf("%s set net to power%s\n", __func__, enable?"on":"off"); + + if (enable) { + net_sys_start(sysinfo->wlan_mode); +#ifdef CONFIG_AUTO_RECONNECT_AP + net_ctrl_connect_ap(NULL); +#endif + } else { +#ifdef CONFIG_AUTO_RECONNECT_AP + net_ctrl_disconnect_ap(NULL, 1); +#endif + net_sys_stop(); + } + + return 0; +} diff --git a/platform/mcu/xr871/project/common/framework/platform_init.c b/platform/mcu/xr871/project/common/framework/platform_init.c new file mode 100644 index 0000000000..94eb46ca44 --- /dev/null +++ b/platform/mcu/xr871/project/common/framework/platform_init.c @@ -0,0 +1,259 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include "compiler.h" +#include "version.h" +#include "pm/pm.h" + +#include "common/board/board.h" +#include "sysinfo.h" +#include "img_ctrl.h" +#include "net_ctrl.h" +#include "sys_ctrl/sys_ctrl.h" +#include "fwk_debug.h" + +#if (PRJCONF_SOUNDCARD0_EN || PRJCONF_SOUNDCARD1_EN) +#include "audio/manager/audio_manager.h" +#include "audio/pcm/audio_pcm.h" +#endif +#if PRJCONF_CONSOLE_EN +#include "console/console.h" +#include "command.h" +#endif +#ifdef __PRJ_CONFIG_XIP +#include "driver/chip/hal_xip.h" +#include "sys/image.h" +#endif + +#define PLATFORM_SHOW_INFO 0 /* for internal debug only */ + +/* default app pm mode */ +#if (PRJCONF_PM_EN && !defined(PRJCONF_PM_MODE)) +#define PRJCONF_PM_MODE (PM_SUPPORT_SLEEP | \ + PM_SUPPORT_STANDBY | \ + PM_SUPPORT_POWEROFF) +#endif + +/* default net pm mode */ +#if (PRJCONF_NET_PM_EN && !defined(PRJCONF_NET_PM_MODE)) +#define PRJCONF_NET_PM_MODE (PM_SUPPORT_HIBERNATION | \ + PM_SUPPORT_POWEROFF) +#endif + + +#if PLATFORM_SHOW_INFO +static void platform_show_info(void) +{ + extern uint8_t __data_end__[]; + extern uint8_t _edata[]; + extern uint8_t __bss_start__[]; + extern uint8_t __bss_end__[]; + extern uint8_t __end__[]; + extern uint8_t end[]; + extern uint8_t __HeapLimit[]; + extern uint8_t __StackLimit[]; + extern uint8_t __StackTop[]; + extern uint8_t __stack[]; + extern uint8_t _estack[]; + + printf("__data_end__ %p\n", __data_end__); + printf("_edata %p\n", _edata); + printf("__bss_start__ %p\n", __bss_start__); + printf("__bss_end__ %p\n", __bss_end__); + printf("__end__ %p\n", __end__); + printf("end %p\n", end); + printf("__HeapLimit %p\n", __HeapLimit); + printf("__StackLimit %p\n", __StackLimit); + printf("__StackTop %p\n", __StackTop); + printf("__stack %p\n", __stack); + printf("_estack %p\n", _estack); + printf("\n"); + + printf("heap space [%p, %p), size %u\n\n", + __end__, _estack - PRJCONF_MSP_STACK_SIZE, + _estack - __end__ - PRJCONF_MSP_STACK_SIZE); + + printf("cpu clock %u Hz\n", HAL_GetCPUClock()); + printf("ahb1 clock %u Hz\n", HAL_GetAHB1Clock()); + printf("ahb2 clock %u Hz\n", HAL_GetAHB2Clock()); + printf("apb clock %u Hz\n", HAL_GetAPBClock()); + printf("HF clock %u Hz\n", HAL_GetHFClock()); + printf("LF clock %u Hz\n", HAL_GetLFClock()); + printf("\n"); +} +#endif /* PLATFORM_SHOW_INFO */ + +#ifdef __PRJ_CONFIG_XIP +static void platform_xip_init(void) +{ + uint32_t addr; + + addr = image_get_section_addr(IMAGE_APP_XIP_ID); + if (addr == IMAGE_INVALID_ADDR) { + FWK_ERR("no xip section\n"); + return; + } + + /* TODO: check section's validity */ + + HAL_Xip_Init(PRJCONF_IMG_FLASH, addr + IMAGE_HEADER_SIZE); +} +#endif /* __PRJ_CONFIG_XIP */ + +#if PRJCONF_WDG_EN +static void platform_wdg_feed(void *arg) +{ + FWK_DBG("feed wdg @ %u sec\n", OS_GetTime()); + HAL_WDG_Feed(); +} + +static void platform_wdg_start(void) +{ + WDG_InitParam param; + OS_Timer_t timer; + + /* init watchdog */ + param.event = WDG_EVT_RESET; + param.timeout = PRJCONF_WDG_TIMEOUT; + param.resetCycle = WDG_DEFAULT_RESET_CYCLE; + HAL_WDG_Init(¶m); + + /* create OS timer to feed watchdog */ + OS_TimerSetInvalid(&timer); + if (OS_TimerCreate(&timer, OS_TIMER_PERIODIC, platform_wdg_feed, NULL, + PRJCONF_WDG_FEED_PERIOD) != OS_OK) { + FWK_WRN("wdg timer create failed\n"); + HAL_WDG_DeInit(); + return; + } + + HAL_WDG_Start(); /* start watchdog */ + OS_TimerStart(&timer); /* start OS timer to feed watchdog */ +} +#endif /* PRJCONF_WDG_EN */ + +/* init basic platform hardware and services */ +__weak void platform_init_level0(void) +{ + HAL_Flash_Init(PRJCONF_IMG_FLASH); +#if PRJCONF_CE_EN + HAL_CE_Init(); +#endif + + img_ctrl_init(PRJCONF_IMG_FLASH, PRJCONF_IMG_ADDR, PRJCONF_IMG_SIZE); +#if (defined(__PRJ_CONFIG_XIP)) + platform_xip_init(); +#endif +} + +/* init standard platform hardware and services */ +__weak void platform_init_level1(void) +{ +#if PRJCONF_UART_EN + if ((BOARD_SUB_UART_ID < UART_NUM) && + (BOARD_SUB_UART_ID != BOARD_MAIN_UART_ID)) { + board_uart_init(BOARD_SUB_UART_ID); + } +#endif + +#if PRJCONF_SYS_CTRL_EN + sys_ctrl_create(); + #if PRJCONF_NET_EN + net_ctrl_init(); + #endif +#endif + + sysinfo_init(); + +#if PRJCONF_CONSOLE_EN + console_param_t cparam; + cparam.uart_id = BOARD_MAIN_UART_ID; + cparam.cmd_exec = main_cmd_exec; + console_start(&cparam); +#endif + +#if PRJCONF_PM_EN + pm_mode_platform_select(PRJCONF_PM_MODE); +#endif + +#if PRJCONF_NET_EN + net_sys_init(); + + struct sysinfo *sysinfo = sysinfo_get(); + net_sys_start(sysinfo->wlan_mode); + + #if PRJCONF_NET_PM_EN + pm_register_wlan_power_onoff(net_sys_onoff, PRJCONF_NET_PM_MODE); + #endif +#endif /* PRJCONF_NET_EN */ + +#if PRJCONF_WDG_EN + platform_wdg_start(); +#endif +} + +/* init extern platform hardware and services */ +__weak void platform_init_level2(void) +{ +#if PRJCONF_SPI_EN + board_spi_init(BOARD_SPI_PORT); +#endif + +#if PRJCONF_MMC_EN + SDC_InitTypeDef sdc_param; + #ifdef CONFIG_DETECT_CARD + sdc_param.cd_mode = PRJCONF_MMC_DETECT_MODE; + #endif + HAL_SDC_Init(0, &sdc_param); +#endif + +#if (PRJCONF_SOUNDCARD0_EN || PRJCONF_SOUNDCARD1_EN) + aud_mgr_init(); + snd_pcm_init(); + #if PRJCONF_SOUNDCARD0_EN + board_soundcard0_init(); + #endif + #if PRJCONF_SOUNDCARD1_EN + board_soundcard1_init(); + #endif +#endif +} + +void platform_init(void) +{ + printf("XRadio IoT WLAN SDK %s\n\n", SDK_VERSION_STR); +#if PLATFORM_SHOW_INFO + platform_show_info(); +#endif + + platform_init_level0(); + platform_init_level1(); + platform_init_level2(); +} diff --git a/platform/mcu/xr871/project/common/framework/platform_init.h b/platform/mcu/xr871/project/common/framework/platform_init.h new file mode 100644 index 0000000000..823748c8a6 --- /dev/null +++ b/platform/mcu/xr871/project/common/framework/platform_init.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _PLATFORM_INIT_H_ +#define _PLATFORM_INIT_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +void platform_init(void); + +#ifdef __cplusplus +} +#endif + +#endif /* _PLATFORM_INIT_H_ */ diff --git a/platform/mcu/xr871/project/common/framework/sys_ctrl/container.c b/platform/mcu/xr871/project/common/framework/sys_ctrl/container.c new file mode 100644 index 0000000000..ea79e307ba --- /dev/null +++ b/platform/mcu/xr871/project/common/framework/sys_ctrl/container.c @@ -0,0 +1,246 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#include +#include +#include +#include "kernel/os/os.h" +#include "sys/list.h" +#include "sys/param.h" +#include "sys/defs.h" +#include "container.h" + + +#define CONTAINER_DEBUG(fmt, arg...) //printf("[Container debug] <%s : %d> " fmt "\n", __func__, __LINE__, ##arg) +#define CONTAINER_ALERT(fmt, arg...) printf("[Container alert] <%s : %d> " fmt "\n", __func__, __LINE__, ##arg) +#define CONTAINER_ERROR(fmt, arg...) printf("[Container error] <%s : %d> " fmt "\n", __func__, __LINE__, ##arg) +#define CONTAINER_NOWAY() printf("[Container should not be here] <%s : %d> \n", __func__, __LINE__) +#define CONTAINER_NOTSUPPORT() CONTAINER_ALERT("not support command") + + +typedef struct list_node +{ + struct list_head node; + uint32_t arg; +} list_node; + +typedef struct sorted_list +{ + container_base base; + OS_Semaphore_t sem; + OS_Mutex_t lock; + struct list_head head; + struct list_head pool; + list_node *poolBuf; + int (*compare)(uint32_t newArg, uint32_t oldArg); +} sorted_list; + +typedef struct sorted_list_config +{ + uint32_t size; + int (*compare)(uint32_t oldArg, uint32_t newArg); +} sorted_list_config; + +static int sorted_list_init(struct container_base *base, uint32_t config) +{ + OS_Status ret1 = OS_FAIL; + OS_Status ret2 = OS_FAIL; + list_node *vec = NULL; + sorted_list_config *cfg = (sorted_list_config *)config; + sorted_list *impl = __containerof(base, sorted_list, base); + + INIT_LIST_HEAD(&impl->head); + INIT_LIST_HEAD(&impl->pool); + impl->base.size = cfg->size; + impl->compare = cfg->compare; + + ret1 = OS_SemaphoreCreateBinary(&impl->sem); + if (ret1 != OS_OK) + goto failed; + + ret2 = OS_MutexCreate(&impl->lock); + if (ret2 != OS_OK) + goto failed; + + vec = malloc(sizeof(list_node) * impl->base.size); + if (vec == NULL) + goto failed; + memset(vec, 0, sizeof(list_node) * impl->base.size); + impl->poolBuf = vec; + + for(int i = impl->base.size; i-- > 0; ) + { + INIT_LIST_HEAD(&vec[i].node); + list_add_tail(&vec[i].node, &impl->pool); + } + + return 0; + +failed: + if (vec != NULL) + free(vec); + if (ret1 == OS_OK) + OS_SemaphoreDelete(&impl->sem); + if (ret2 == OS_OK) + OS_MutexDelete(&impl->lock); + if (impl != NULL) + free(impl); + + return -1; +} + +static int sorted_list_deinit(struct container_base *base) +{ + sorted_list *impl = __containerof(base, sorted_list, base); + + /* TODO: flush the node left */ + + OS_SemaphoreDelete(&impl->sem); + OS_MutexDelete(&impl->lock); + if (impl->poolBuf != NULL) + free(impl->poolBuf); + if (impl != NULL) + free(impl); + + return 0; +} + +static int sorted_list_control(struct container_base *base, uint32_t cmd, uint32_t arg) +{ + sorted_list *impl = __containerof(base, sorted_list, base); + /* TODO: tbc... */ + (void)impl; + return -1; +} + +static int sorted_list_push(struct container_base *base, uint32_t arg, uint32_t timeout) +{ +#define BLOCK_WAIT_EACHTIME (3) + sorted_list *impl = __containerof(base, sorted_list, base); + list_node *node = NULL; + list_node *tmp = NULL; + + OS_MutexLock(&impl->lock, -1); /* TODO: OS should move to event_queue */ + + /* 1. get 1 node from pool */ + while ((node = list_first_entry_or_null(&impl->pool, list_node, node)) == NULL) + { + OS_MutexUnlock(&impl->lock); + OS_MSleep(BLOCK_WAIT_EACHTIME); + if (timeout <= BLOCK_WAIT_EACHTIME) { + CONTAINER_ALERT("list full and timeout"); + + return -1; + } + OS_MutexLock(&impl->lock, -1); + timeout -= BLOCK_WAIT_EACHTIME; + } + node->arg = arg; + + CONTAINER_DEBUG("get a node from pool"); + + /* 2. find a proper place and insert it */ + list_for_each_entry(tmp, &impl->head, node) + { + if (impl->compare(arg, tmp->arg) >= 0) { + tmp = list_prev_entry(tmp, node); + break; + } + } + list_add(&node->node, &tmp->node); + + CONTAINER_DEBUG("insert node to list"); + + /* 3. release sem to pop */ + OS_SemaphoreRelease(&impl->sem); + + OS_MutexUnlock(&impl->lock); + return 0; +} + +static int sorted_list_pop(struct container_base *base, uint32_t *arg, uint32_t timeout) +{ + sorted_list *impl = __containerof(base, sorted_list, base); + list_node *node = NULL; + OS_Status ret; + + ret = OS_SemaphoreWait(&impl->sem, timeout); + if (ret != OS_OK) + return -1; + + OS_MutexLock(&impl->lock, -1); + + /* 1. get node from list */ + node = list_first_entry_or_null(&impl->head, list_node, node); + if (node == NULL) + { + CONTAINER_ERROR("list empty but sem released!"); + OS_MutexUnlock(&impl->lock); + return -2; + } + *arg = node->arg; + + CONTAINER_DEBUG("get a node from list"); + + /* 2. remove the node from this and push it to pool */ + list_del(&node->node); + list_add_tail(&node->node, &impl->head); + + CONTAINER_DEBUG("remove a node from list and push it to pool"); + + OS_MutexUnlock(&impl->lock); + return 0; +} + +container_base *sorted_list_create(uint32_t size, int (*compare)(uint32_t newArg, uint32_t oldArg)) +{ + sorted_list *impl = malloc(sizeof(*impl)); + if (impl == NULL) + return NULL; + memset(impl, 0, sizeof(*impl)); + + sorted_list_config cfg; + cfg.compare = compare; + cfg.size = size; + + impl->base.control = sorted_list_control; + impl->base.deinit = sorted_list_deinit; + impl->base.pop = sorted_list_pop; + impl->base.push = sorted_list_push; + + if (sorted_list_init(&impl->base, (uint32_t)&cfg) != 0) + { + CONTAINER_ERROR("init failed"); + free(impl); + return NULL; + } + + return &impl->base; +} diff --git a/platform/mcu/xr871/project/common/framework/sys_ctrl/container.h b/platform/mcu/xr871/project/common/framework/sys_ctrl/container.h new file mode 100644 index 0000000000..226025192f --- /dev/null +++ b/platform/mcu/xr871/project/common/framework/sys_ctrl/container.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#ifndef CONTAINER_H_ +#define CONTAINER_H_ + +typedef struct container_base +{ + uint32_t size; + +// int (*init)(struct container_base *base, uint32_t config); + int (*deinit)(struct container_base *base); + int (*control)(struct container_base *base, uint32_t cmd, uint32_t arg); + int (*push)(struct container_base *base, uint32_t item, uint32_t timeout); + int (*pop)(struct container_base *base, uint32_t *item, uint32_t timeout); +} container_base; + +container_base *sorted_list_create(uint32_t size, int (*compare)(uint32_t newArg, uint32_t oldArg)); + +#endif /* CONTAINER_H_ */ diff --git a/platform/mcu/xr871/project/common/framework/sys_ctrl/event_queue.c b/platform/mcu/xr871/project/common/framework/sys_ctrl/event_queue.c new file mode 100644 index 0000000000..433ee117bd --- /dev/null +++ b/platform/mcu/xr871/project/common/framework/sys_ctrl/event_queue.c @@ -0,0 +1,245 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#include +#include +#include +#include "kernel/os/os.h" +#include "sys/list.h" +#include "sys/param.h" +#include "sys/defs.h" +#include "container.h" +#include "event_queue.h" + +#define EVTMSG_DEBUG(fmt, arg...) //printf("[event_msg debug] <%s : %d> " fmt "\n", __func__, __LINE__, ##arg) +#define EVTMSG_ALERT(fmt, arg...) printf("[event_msg alert] <%s : %d> " fmt "\n", __func__, __LINE__, ##arg) +#define EVTMSG_ERROR(fmt, arg...) printf("[event_msg error] <%s : %d> " fmt "\n", __func__, __LINE__, ##arg) +#define EVTMSG_NOWAY() printf("[event_msg should not be here] <%s : %d> \n", __func__, __LINE__) +#define EVTMSG_NOTSUPPORT() EVTMSG_ALERT("not support command") + + + +typedef struct prio_event_queue +{ + event_queue base; + container_base *container; +} prio_event_queue; + +static int complare_event_msg(uint32_t newArg, uint32_t oldArg) +{ + event_msg *newMsg = (event_msg *)newArg; + event_msg *oldMsg = (event_msg *)oldArg; + + return newMsg->event < oldMsg->event; +} + +static int prio_event_queue_deinit(struct event_queue *base) +{ + prio_event_queue *impl = __containerof(base, prio_event_queue, base); + + int ret = impl->container->deinit(impl->container); + if (ret == 0) + free(impl); + + return ret; +} + +static int prio_event_send(struct event_queue *base, struct event_msg *msg, uint32_t wait_ms) +{ + prio_event_queue *impl = __containerof(base, prio_event_queue, base); + + EVTMSG_DEBUG("send event: 0x%x", msg->event); + + /* TODO: cache some to faster create msg and less fragmented memory */ + struct event_msg *newMsg = malloc(sizeof(*newMsg)); + if (newMsg == NULL) + return -1; + memcpy(newMsg, msg, sizeof(*newMsg)); + + int ret = impl->container->push(impl->container, (uint32_t)newMsg, wait_ms); + if (ret != 0) + { + free(newMsg); + EVTMSG_ALERT("send event timeout"); + return -2; + } + + return 0; +} + +static int prio_event_recv(struct event_queue *base, struct event_msg *msg, uint32_t wait_ms) +{ + prio_event_queue *impl = __containerof(base, prio_event_queue, base); + event_msg *newMsg; + + int ret = impl->container->pop(impl->container, (uint32_t *)&newMsg, wait_ms); + if (ret != 0) + return -2; + + memcpy(msg, newMsg, sizeof(*newMsg)); + free(newMsg); + + EVTMSG_DEBUG("recv event: 0x%x", msg->event); + + return 0; +} + +struct event_queue *prio_event_queue_create(uint32_t queue_len) +{ + prio_event_queue *impl = malloc(sizeof(*impl)); + if (impl == NULL) + return NULL; + memset(impl, 0, sizeof(*impl)); + + impl->container = sorted_list_create(queue_len, complare_event_msg); + if (impl->container == NULL) + goto out; + impl->base.send = prio_event_send; + impl->base.recv = prio_event_recv; + impl->base.deinit = prio_event_queue_deinit; + + return &impl->base; + +out: + EVTMSG_ERROR("sorted_list_create failed"); + free(impl); + return NULL; +} + + +typedef struct normal_event_queue +{ + event_queue base; + OS_Queue_t queue; +} normal_event_queue; + +static int normal_event_queue_deinit(struct event_queue *base) +{ + normal_event_queue *impl = __containerof(base, normal_event_queue, base); + OS_Status ret; + +#if CTRL_MSG_VALIDITY_CHECK + if (!OS_QueueIsValid(&impl->queue)) { + EVTMSG_ALERT("%s(), invalid queue %p", __func__, g_ctrl_msg_queue.handle); + return 0; + } +#endif + + ret = OS_QueueDelete(&impl->queue); + if (ret != OS_OK) { + EVTMSG_ERROR("%s() failed, err 0x%x", __func__, ret); + return -1; + } + + free(impl); + + EVTMSG_DEBUG("%s()", __func__); + return 0; +} + +static int normal_event_send(struct event_queue *base, struct event_msg *msg, uint32_t wait_ms) +{ + normal_event_queue *impl = __containerof(base, normal_event_queue, base); + OS_Status ret; + + EVTMSG_DEBUG("send event: 0x%x", msg->event); + +#if CTRL_MSG_VALIDITY_CHECK + if (!OS_QueueIsValid(&impl->queue)) { + EVTMSG_ALERT("%s(), invalid queue %p", __func__, g_ctrl_msg_queue.handle); + return -1; + } +#endif + + ret = OS_QueueSend(&impl->queue, msg, wait_ms); + if (ret != OS_OK) { + EVTMSG_ERROR("%s() failed, err 0x%x", __func__, ret); + return -1; + } + return 0; +} + +static int normal_event_recv(struct event_queue *base, struct event_msg *msg, uint32_t wait_ms) +{ + normal_event_queue *impl = __containerof(base, normal_event_queue, base); + OS_Status ret; + +#if CTRL_MSG_VALIDITY_CHECK + if (!OS_QueueIsValid(&g_ctrl_msg_queue)) { + EVTMSG_ALERT("%s(), invalid queue %p", __func__, g_ctrl_msg_queue.handle); + return -1; + } +#endif + + ret = OS_QueueReceive(&impl->queue, msg, wait_ms); + if (ret != OS_OK) { + EVTMSG_ERROR("%s() failed, err 0x%x", __func__, ret); + return -1; + } + + EVTMSG_DEBUG("recv event: 0x%x", msg->event); + + return 0; +} + +struct event_queue *normal_event_queue_create(uint32_t queue_len) +{ + normal_event_queue *impl = malloc(sizeof(*impl)); + if (impl == NULL) + return NULL; + memset(impl, 0, sizeof(*impl)); + + impl->base.send = normal_event_send; + impl->base.recv = normal_event_recv; + impl->base.deinit = normal_event_queue_deinit; + + +#if CTRL_MSG_VALIDITY_CHECK + if (OS_QueueIsValid(&impl->queue)) { + EVTMSG_ALERT("control message queue already inited\n"); + return -1; + } +#endif + + if (OS_QueueCreate(&impl->queue, queue_len, sizeof(struct event_msg)) != OS_OK) { + EVTMSG_ERROR("%s() failed!", __func__); + goto out; + } + EVTMSG_DEBUG("%s()", __func__); + + return &impl->base; + +out: + if (impl != NULL) + free(impl); + return NULL; +} + + diff --git a/platform/mcu/xr871/project/common/framework/sys_ctrl/event_queue.h b/platform/mcu/xr871/project/common/framework/sys_ctrl/event_queue.h new file mode 100644 index 0000000000..9c1ca97595 --- /dev/null +++ b/platform/mcu/xr871/project/common/framework/sys_ctrl/event_queue.h @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#ifndef EVENT_QUEUE_H_ +#define EVENT_QUEUE_H_ + +typedef struct event_msg { +// uint16_t type; +// uint16_t subtype; /* subtype = 0 means all subtype */ + uint32_t event; + uint32_t data; + void (*destruct)(uint32_t data); +} event_msg; + +typedef struct event_queue +{ +// int (*init)(struct event_queue *base, uint32_t queue_len); + int (*deinit)(struct event_queue *base); + int (*send)(struct event_queue *base, struct event_msg *msg, uint32_t wait_ms); + int (*recv)(struct event_queue *base, struct event_msg *msg, uint32_t wait_ms); +} event_queue; + + +struct event_queue *prio_event_queue_create(uint32_t queue_len); + +struct event_queue *normal_event_queue_create(uint32_t queue_len); + +#endif /* EVENT_QUEUE_H_ */ diff --git a/platform/mcu/xr871/project/common/framework/sys_ctrl/observer.c b/platform/mcu/xr871/project/common/framework/sys_ctrl/observer.c new file mode 100644 index 0000000000..f28c442d45 --- /dev/null +++ b/platform/mcu/xr871/project/common/framework/sys_ctrl/observer.c @@ -0,0 +1,194 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#include +#include +#include +#include "sys/list.h" +#include "kernel/os/os.h" +#include "observer.h" + +#define OBSERVER_DEBUG(fmt, arg...) //printf("[Observer debug] <%s : %d> " fmt "\n", __func__, __LINE__, ##arg) +#define OBSERVER_ALERT(fmt, arg...) printf("[Observer alert] <%s : %d> " fmt "\n", __func__, __LINE__, ##arg) +#define OBSERVER_ERROR(fmt, arg...) printf("[Observer error] <%s : %d> " fmt "\n", __func__, __LINE__, ##arg) +#define OBSERVER_NOWAY() printf("[Observer should not be here] <%s : %d> \n", __func__, __LINE__) +#define OBSERVER_NOTSUPPORT() OBSERVER_ALERT("not support command") + + +typedef struct event_observer +{ + observer_base base; + OS_Semaphore_t sem; +} event_observer; + +static void event_trigger(struct observer_base *base, uint32_t event, uint32_t arg) +{ + event_observer *impl = __containerof(base, event_observer, base); + + OS_SemaphoreRelease(&impl->sem); +} + +observer_base *event_observer_create(uint32_t event) +{ + event_observer *obs = malloc(sizeof(*obs)); + if (obs == NULL) + return NULL; + memset(obs, 0, sizeof(*obs)); + + obs->base.event = event; +// obs->base.type = EVENT_OBSERVER; + obs->base.trigger = event_trigger; + INIT_LIST_HEAD(&obs->base.node); + OS_SemaphoreCreateBinary(&obs->sem); + + return &obs->base; +} + +OS_Status event_wait(observer_base *base, OS_Time_t timeout) +{ + event_observer *impl = __containerof(base, event_observer, base); + + return OS_SemaphoreWait(&impl->sem, timeout); +} + + +typedef struct callback_observer +{ + observer_base base; + void (*cb)(uint32_t event, uint32_t arg); +} callback_observer; + +static void trigger_callback(struct observer_base *base, uint32_t event, uint32_t arg) +{ + callback_observer *impl = __containerof(base, callback_observer, base); + + impl->cb(event, arg); +} + +observer_base *callback_observer_create(uint32_t event, void (*cb)(uint32_t event, uint32_t arg)) +{ + callback_observer *obs = malloc(sizeof(*obs)); + if (obs == NULL) + return NULL; + memset(obs, 0, sizeof(*obs)); + + obs->base.event = event; +// obs->base.type = CALLBACK_OBSERVER; + obs->base.trigger = trigger_callback; + INIT_LIST_HEAD(&obs->base.node); + obs->cb = cb; + + return &obs->base; +} + + +typedef struct thread_observer +{ + observer_base base; + OS_Thread_t thd; + void (*run)(uint32_t event, uint32_t arg); + void (*exception)(int ret); + uint32_t stack; + OS_Priority prio; + + uint32_t event; + uint32_t arg; +} thread_observer; + +static void wrap_thread(void *arg) +{ + thread_observer *impl = (thread_observer *)arg; + + impl->run(impl->event, impl->arg); + + OS_ThreadDelete(&impl->thd); +} + +static void trigger_thread(struct observer_base *base, uint32_t event, uint32_t arg) +{ + thread_observer *impl = __containerof(base, thread_observer, base); + OS_Status ret; + + impl->event = event; + impl->arg = arg; + + if (OS_ThreadIsValid(&impl->thd)) + { + OBSERVER_ALERT("thread still running or not init"); + return; + } + + if ((ret = OS_ThreadCreate(&impl->thd, "Trigger Thread", wrap_thread, impl, impl->prio, impl->stack)) != OS_OK) + { + OBSERVER_ERROR("thread create error, maybe no RAM to create"); + if (impl->exception != NULL) + impl->exception(ret); + } +} + +void thread_observer_throw(struct observer_base *base, void (*exception)(int ret)) +{ + thread_observer *impl = __containerof(base, thread_observer, base); + + impl->exception = exception; +} + +/* TODO: thread_observer_copy_data(struct observer_base *base, int (*copy)(uint32_t data)) */ + +observer_base *thread_observer_create(uint32_t event, void (*run)(uint32_t event, uint32_t arg), uint32_t stackSize, OS_Priority prio) +{ + thread_observer *obs = malloc(sizeof(*obs)); + if (obs == NULL) + return NULL; + memset(obs, 0, sizeof(*obs)); + + /* TODO: need mode in case of trigger serval times */ + obs->base.event = event; +// obs->base.type = THREAD_OBSERVER; + obs->base.trigger = trigger_thread; + INIT_LIST_HEAD(&obs->base.node); + obs->run = run; + obs->stack = stackSize; + obs->prio = prio; + + return &obs->base; +} + +int observer_destroy(observer_base *base) +{ + if (base == NULL) + return -1; + if (base->state != OBSERVER_ILDE) + return -1; + free(base); + return 0; +} + + diff --git a/platform/mcu/xr871/project/common/framework/sys_ctrl/observer.h b/platform/mcu/xr871/project/common/framework/sys_ctrl/observer.h new file mode 100644 index 0000000000..703f099f13 --- /dev/null +++ b/platform/mcu/xr871/project/common/framework/sys_ctrl/observer.h @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#ifndef OBSERVER_H_ +#define OBSERVER_H_ + +#include +#include "sys/list.h" +#include "kernel/os/os.h" + + +typedef enum observer_types +{ + EVENT_OBSERVER, + CALLBACK_OBSERVER, + THREAD_OBSERVER, +} observer_types; + +typedef enum observer_modes +{ + SINGLE_OBSERVER, + CONTINUE_OBSERVER, +} observer_modes; + +typedef struct observer_base +{ + struct list_head node; + uint32_t event; + int state; +// uint32_t type; +// observer_modes mode; + + void (*trigger)(struct observer_base *base, uint32_t event, uint32_t arg); +} observer_base; + +typedef enum observer_state +{ + OBSERVER_ILDE, + OBSERVER_ATTACHED, + OBSERVER_DETACHED, + OBSERVER_WORKING, +} observer_state; + +observer_base *event_observer_create(uint32_t event); + +OS_Status event_wait(observer_base *base, OS_Time_t timeout); + +observer_base *callback_observer_create(uint32_t event, void (*cb)(uint32_t event, uint32_t arg)); + +observer_base *thread_observer_create(uint32_t event, void (*run)(uint32_t event, uint32_t arg), uint32_t stackSize, OS_Priority prio); + +void thread_observer_throw(struct observer_base *base, void (*exception)(int ret)); + +int observer_destroy(observer_base *base); + +#endif /* OBSERVER_H_ */ diff --git a/platform/mcu/xr871/project/common/framework/sys_ctrl/publisher.c b/platform/mcu/xr871/project/common/framework/sys_ctrl/publisher.c new file mode 100644 index 0000000000..aa9eb8c320 --- /dev/null +++ b/platform/mcu/xr871/project/common/framework/sys_ctrl/publisher.c @@ -0,0 +1,212 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include "sys/list.h" +#include "sys/param.h" +#include "sys/defs.h" +#include "sys/interrupt.h" +#include "kernel/os/os.h" +#include "observer.h" +#include "event_queue.h" +#include "publisher.h" + +#define PUBLISHER_DEBUG(fmt, arg...) //printf("[Publisher debug] <%s : %d> " fmt "\n", __func__, __LINE__, ##arg) +#define PUBLISHER_ALERT(fmt, arg...) printf("[Publisher alert] <%s : %d> " fmt "\n", __func__, __LINE__, ##arg) +#define PUBLISHER_ERROR(fmt, arg...) printf("[Publisher error] <%s : %d> " fmt "\n", __func__, __LINE__, ##arg) +#define PUBLISHER_NOWAY() printf("[Publisher should not be here] <%s : %d> \n", __func__, __LINE__) +#define PUBLISHER_NOTSUPPORT() PUBLISHER_ALERT("not support command") + + +#define PUBLISHER_THREAD_STACKSIZE (2 * 1024) + +static void atomic_set(int *var, uint32_t cnt) +{ + arch_irq_disable(); + *var = cnt; + arch_irq_enable(); +} + +static int attach(struct publisher_base *base, observer_base *obs) +{ + int ret = 0; + + PUBLISHER_DEBUG("new observe event: 0x%x", obs->event); + + /* TODO: entry critical section to sync list */ + OS_RecursiveMutexLock(&base->lock, -1); + if (obs->state == OBSERVER_DETACHED) + obs->state = OBSERVER_ATTACHED; + else if (list_empty(&obs->node)) + { + arch_irq_disable(); + list_add_tail(&obs->node, &base->head); + obs->state = OBSERVER_ATTACHED; + arch_irq_enable(); + } + else + { + PUBLISHER_ALERT("new observe event: %u failed", obs->event); + ret = -1; + } + OS_RecursiveMutexUnlock(&base->lock); + /* TODO: exit critical section */ + + return ret; +} + +static int detach(struct publisher_base *base, observer_base *obs) +{ + /* TODO: entry critical section to sync list or return failed */ + OS_RecursiveMutexLock(&base->lock, -1); /* it can't call in interrupt, should be fixed */ + if (base->state == PUBLISHER_IDLE) + { + list_del(&obs->node); + obs->state = OBSERVER_ILDE; + } + else + { + atomic_set(&obs->state, OBSERVER_DETACHED); + } + OS_RecursiveMutexUnlock(&base->lock); + /* TODO: exit critical section */ + + PUBLISHER_DEBUG("remove observe event: %d", obs->event); + + return 0; +} + +static int notify(struct publisher_base *base, uint32_t event, uint32_t arg) +{ + observer_base *obs = NULL; + observer_base *itor = NULL; + observer_base *safe = NULL; + int cnt = 0; + + /* TODO: define some event to debug, for example, event -1 can be detect how many observer now. */ + + OS_RecursiveMutexLock(&base->lock, -1); + atomic_set(&base->state, PUBLISHER_WORKING); + + /* trigger observers */ + list_for_each_entry(itor, &base->head, node) + { + /* TODO: detect the observer if is locked by detach or sth else */ + if (base->compare(event, itor->event) == 0 && itor->state == OBSERVER_ATTACHED) + { + obs = itor; + obs->trigger(obs, event, arg); + cnt++; + } + } + + /* remove observers detached in trigger function */ + list_for_each_entry_safe(itor, safe, &base->head, node) + { + if (itor->state == OBSERVER_DETACHED) + { + list_del(&itor->node); + atomic_set(&itor->state, OBSERVER_ILDE); + } + } + + atomic_set(&base->state, PUBLISHER_IDLE); + OS_RecursiveMutexUnlock(&base->lock); + + if (obs == NULL) + { + PUBLISHER_DEBUG("no observer eyes on this event"); + return 0; + } + + return cnt; +} + +static void main_publisher(void *arg) +{ + publisher_base *base = (publisher_base *)arg; + struct event_msg msg; + int ret; + + while (1) + { + ret = base->queue->recv(base->queue, &msg, -1); + if (ret != 0) { + PUBLISHER_ERROR("queue error"); + continue; + } + + ret = base->notify(base, msg.event, msg.data); + PUBLISHER_DEBUG("event 0x%x notified %d observers", msg.event, ret); + + if (msg.destruct != NULL) + msg.destruct(msg.data); + } + +} + +/* TODO: add stack size */ +publisher_base *publisher_create(struct event_queue *queue, int (*compare)(uint32_t newEvent, uint32_t obsEvent), OS_Priority prio, uint32_t stack) +{ + publisher_base *base = malloc(sizeof(*base)); + if (base == NULL) + return NULL; + memset(base, 0, sizeof(*base)); + + OS_Status ret = OS_RecursiveMutexCreate(&base->lock); + if (ret != OS_OK) + goto failed; + + INIT_LIST_HEAD(&base->head); + base->queue = queue; + base->attach = attach; + base->detach = detach; + base->notify = notify; + base->compare = compare; + + if (OS_ThreadCreate(&base->thd, "Publish Thread", main_publisher, + base, prio, stack) != OS_OK) + { + PUBLISHER_ERROR("thread create error, maybe no RAM to create"); + goto failed; + } + + return base; + +failed: + if (ret == OS_OK) + OS_RecursiveMutexDelete(&base->lock); + if (base != NULL) + free(base); + + return NULL; +} + diff --git a/platform/mcu/xr871/project/common/framework/sys_ctrl/publisher.h b/platform/mcu/xr871/project/common/framework/sys_ctrl/publisher.h new file mode 100644 index 0000000000..55b9039fd5 --- /dev/null +++ b/platform/mcu/xr871/project/common/framework/sys_ctrl/publisher.h @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#ifndef PUBLISHER_H_ +#define PUBLISHER_H_ + +#include "sys/list.h" +#include "kernel/os/os.h" +#include "observer.h" + + +typedef struct publisher_base +{ + struct list_head head; + struct event_queue *queue; + OS_Thread_t thd; + OS_Mutex_t lock; // or uint32_t sync by atomic; + int state; + + int (*attach)(struct publisher_base *base, observer_base *obs); + int (*detach)(struct publisher_base *base, observer_base *obs); + int (*notify)(struct publisher_base *base, uint32_t event, uint32_t arg); + int (*compare)(uint32_t newEvent, uint32_t obsEvent); +} publisher_base; + +typedef enum publisher_state +{ + PUBLISHER_IDLE, + PUBLISHER_WORKING, +} publisher_state; + +publisher_base *publisher_create(struct event_queue *queue, int (*compare)(uint32_t newEvent, uint32_t obsEvent), + OS_Priority prio, uint32_t stack); + + +#endif /* PUBLISHER_H_ */ diff --git a/platform/mcu/xr871/project/common/framework/sys_ctrl/sys_ctrl.c b/platform/mcu/xr871/project/common/framework/sys_ctrl/sys_ctrl.c new file mode 100644 index 0000000000..9104caf77f --- /dev/null +++ b/platform/mcu/xr871/project/common/framework/sys_ctrl/sys_ctrl.c @@ -0,0 +1,139 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#include +#include +#include "sys/list.h" +#include "kernel/os/os.h" +#include "observer.h" +#include "publisher.h" +#include "event_queue.h" +#include "sys_ctrl.h" + + +#define SYS_CTRL_DEBUG(fmt, arg...) //printf("[SYS_CTRL debug] <%s : %d> " fmt "\n", __func__, __LINE__, ##arg) +#define SYS_CTRL_ALERT(fmt, arg...) printf("[SYS_CTRL alert] <%s : %d> " fmt "\n", __func__, __LINE__, ##arg) +#define SYS_CTRL_ERROR(fmt, arg...) printf("[SYS_CTRL error] <%s : %d> " fmt "\n", __func__, __LINE__, ##arg) +#define SYS_CTRL_NOWAY() printf("[SYS_CTRL should not be here] <%s : %d> \n", __func__, __LINE__) +#define SYS_CTRL_NOTSUPPORT() SYS_CTRL_ALERT("not support command") + + +static event_queue *g_sys_queue = NULL; +static publisher_base *g_sys_publisher = NULL; + +static int compare(uint32_t newEvent, uint32_t obsEvent) +{ + if (!CMP_EVENT_TYPE(newEvent, obsEvent) && (EVENT_SUBTYPE(obsEvent) == ALL_SUBTYPE || newEvent == obsEvent)) + return 0; + + SYS_CTRL_DEBUG("cmp -1, newEvent: 0x%x, obsEvent: 0x%x", newEvent, obsEvent); + + return -1; +} + +int sys_ctrl_create(void) +{ + uint32_t queue_len = PRJCONF_SYS_CTRL_QUEUE_LEN; + + if (g_sys_queue == NULL) +#if SYS_CTRL_PRIO_QUEUE + g_sys_queue = prio_event_queue_create(queue_len); +#else + g_sys_queue = normal_event_queue_create(queue_len); +#endif + if (g_sys_queue == NULL) + { + SYS_CTRL_ERROR("create queue failed"); + return -1; + } + + if (g_sys_publisher == NULL) + g_sys_publisher = publisher_create(g_sys_queue, compare, PRJCONF_SYS_CTRL_PRIO, PRJCONF_SYS_CTRL_STACK_SIZE); + if (g_sys_publisher == NULL) + { + SYS_CTRL_ERROR("create publisher failed"); + return -1; + } + + return 0; +} + +int sys_ctrl_attach(observer_base *obs) +{ + if (g_sys_publisher == NULL) + return -1; + + return g_sys_publisher->attach(g_sys_publisher, obs); +} + +int sys_ctrl_detach(observer_base *obs) +{ + if (g_sys_publisher == NULL) + return -1; + + return g_sys_publisher->detach(g_sys_publisher, obs); +} + +static __inline int event_send(event_queue *queue, uint16_t type, uint16_t subtype, uint32_t data, void (*destruct)(uint32_t data), uint32_t wait_ms) +{ + struct event_msg msg = {MK_EVENT(type, subtype), data, destruct}; + return queue->send(queue, &msg, wait_ms); +} + +/* +static void freeMsgData(uint32_t data) +{ + free((void *)data); +} +*/ + +int sys_event_send(uint16_t type, uint16_t subtype, uint32_t data, uint32_t wait_ms) +{ + if (g_sys_queue == NULL) + return -1; + + return event_send(g_sys_queue, type, subtype, data, NULL, wait_ms); +} + +/*int sys_event_send_with_free(uint16_t type, uint16_t subtype, uint32_t data, uint32_t wait_ms) +{ + if (g_sys_queue == NULL) + return -1; + + return event_send(g_sys_queue, type, subtype, data, freeMsgData, wait_ms); +}*/ + +int sys_event_send_with_destruct(uint16_t type, uint16_t subtype, uint32_t data, void (*destruct)(uint32_t data), uint32_t wait_ms) +{ + if (g_sys_queue == NULL) + return -1; + + return event_send(g_sys_queue, type, subtype, data, destruct, wait_ms); +} diff --git a/platform/mcu/xr871/project/common/framework/sys_ctrl/sys_ctrl.h b/platform/mcu/xr871/project/common/framework/sys_ctrl/sys_ctrl.h new file mode 100644 index 0000000000..b3deee9440 --- /dev/null +++ b/platform/mcu/xr871/project/common/framework/sys_ctrl/sys_ctrl.h @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#ifndef SYS_CTRL_H_ +#define SYS_CTRL_H_ + +#include +#include "event_queue.h" +#include "publisher.h" +#include "observer.h" + +#define SYS_CTRL_PRIO_QUEUE (0) + +#define ALL_SUBTYPE (0xFFFF) + +typedef enum ctrl_msg_type{ + CTRL_MSG_TYPE_SYSTEM = 0, + CTRL_MSG_TYPE_NETWORK, + CTRL_MSG_TYPE_VKEY, + CTRL_MSG_VOLUME, + TEST_SYS_CTRL, +} ctrl_msg_type; + +typedef enum key_msg_subtype { + CTRL_MSG_SUB_TYPE_AD_BUTTON = 0, + CTRL_MSG_SUB_TYPE_GPIO_BUTTON, + CTRL_MSG_SUB_TYPE_ALL = ALL_SUBTYPE, +} key_msg_subtype; + +/** @brief Get type from event */ +#define EVENT_TYPE(event) ((uint16_t)((event) >> 16)) + +/** @brief Get subtype from event */ +#define EVENT_SUBTYPE(event) ((uint16_t)((event) & 0xFFFF)) + +/** @brief Make event from type and subtype */ +#define MK_EVENT(type, subtype) \ + (((uint32_t)(type) << 16) | ((uint32_t)(subtype) & 0xFFFF)) + +#define CMP_EVENT_TYPE(event1, event2) ((event1 ^ event2) & 0xFFFF0000) + + +static __inline observer_base *sys_callback_observer_create(uint16_t type, uint16_t subtype, void (*cb)(uint32_t event, uint32_t arg)) +{ + return callback_observer_create(MK_EVENT(type, subtype), cb); +} + +int sys_ctrl_create(void); + +int sys_ctrl_attach(observer_base *obs); + +int sys_ctrl_detach(observer_base *obs); + +int sys_event_send(uint16_t type, uint16_t subtype, uint32_t data, uint32_t wait_ms); + +int sys_event_send_with_destruct(uint16_t type, uint16_t subtype, uint32_t data, void (*destruct)(uint32_t data), uint32_t wait_ms); + +#define sys_event_send_with_free(type, subtype, data, wait_ms) \ + sys_event_send_with_destruct(type, subtype, data, (void (*)(uint32_t))free, wait_ms) + + +#endif /* SYS_CTRL_H_ */ diff --git a/platform/mcu/xr871/project/common/framework/sysinfo.c b/platform/mcu/xr871/project/common/framework/sysinfo.c new file mode 100644 index 0000000000..78657cb19a --- /dev/null +++ b/platform/mcu/xr871/project/common/framework/sysinfo.c @@ -0,0 +1,222 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include "efpg/efpg.h" +#include "sys/fdcm.h" +#include "lwip/inet.h" +#include "lwip/ip_addr.h" +#include "sysinfo.h" +#include "sysinfo_debug.h" + +static struct sysinfo g_sysinfo; +static fdcm_handle_t *g_fdcm_hdl; + +static uint8_t m_sysinfo_mac_addr[] = { 0x00, 0x80, 0xE1, 0x29, 0xE8, 0xD1 }; + +static void sysinfo_init_mac_addr(void) +{ + struct sysinfo *info; + + switch (PRJCONF_MAC_ADDR_SOURCE) { + case SYSINFO_MAC_ADDR_CODE: + SYSINFO_DBG("mac addr source: code\n"); + memcpy(g_sysinfo.mac_addr, m_sysinfo_mac_addr, SYSINFO_MAC_ADDR_LEN); + return; + case SYSINFO_MAC_ADDR_EFUSE: + SYSINFO_DBG("mac addr source: eFuse\n"); + if (efpg_read(EFPG_FIELD_MAC, g_sysinfo.mac_addr) != 0) { + SYSINFO_WRN("failed to read MAC address from eFuse\n"); + goto random_mac_addr; + } + return; + case SYSINFO_MAC_ADDR_FLASH: + SYSINFO_DBG("mac addr source: flash\n"); + info = malloc(SYSINFO_SIZE); + if (info == NULL) { + SYSINFO_ERR("malloc failed\n"); + goto random_mac_addr; + } + if (fdcm_read(g_fdcm_hdl, info, SYSINFO_SIZE) != SYSINFO_SIZE) { + SYSINFO_WRN("failed to read MAC address from flash\n"); + free(info); + goto random_mac_addr; + } + memcpy(g_sysinfo.mac_addr, info->mac_addr, SYSINFO_MAC_ADDR_LEN); + free(info); + return; + default: + SYSINFO_ERR("invalid mac addr source\n"); + goto random_mac_addr; + } + +random_mac_addr: + SYSINFO_DBG("random mac address\n"); + + int i; + uint32_t chipid[4] = {0}; + + g_sysinfo.mac_addr[0] = 0x00; + g_sysinfo.mac_addr[1] = 0x54; + efpg_read(EFPG_FIELD_CHIPID, chipid); + + for (i = 2; i < SYSINFO_MAC_ADDR_LEN; i++) { + srand(chipid[i - 2]); + g_sysinfo.mac_addr[i] = rand() % 256; + } +} + +static void sysinfo_init_value(void) +{ + if (sysinfo_load() != 0) { + sysinfo_default(); + return; + } + + if (PRJCONF_MAC_ADDR_SOURCE != SYSINFO_MAC_ADDR_FLASH) { + sysinfo_init_mac_addr(); + } +} + +/** + * @brief Initialize the sysinfo module + * @return 0 on success, -1 on failure + */ +int sysinfo_init(void) +{ + g_fdcm_hdl = fdcm_open(PRJCONF_SYSINFO_FLASH, PRJCONF_SYSINFO_ADDR, PRJCONF_SYSINFO_SIZE); + if (g_fdcm_hdl == NULL) { + SYSINFO_ERR("fdcm open failed, hdl %p\n", g_fdcm_hdl); + return -1; + } + + sysinfo_init_value(); + + return 0; +} + +/** + * @brief DeInitialize the sysinfo module + * @return None + */ +void sysinfo_deinit(void) +{ + fdcm_close(g_fdcm_hdl); +} + +/** + * @brief Set default value to sysinfo + * @return 0 on success, -1 on failure + */ +int sysinfo_default(void) +{ + if (g_fdcm_hdl == NULL) { + SYSINFO_ERR("uninitialized, hdl %p\n", g_fdcm_hdl); + return -1; + } + + memset(&g_sysinfo, 0, SYSINFO_SIZE); + + /* MAC address */ + sysinfo_init_mac_addr(); + + /* wlan mode */ + g_sysinfo.wlan_mode = WLAN_MODE_STA; + + /* netif STA */ + g_sysinfo.sta_use_dhcp = 1; + + /* netif AP */ + IP4_ADDR(&g_sysinfo.netif_ap_param.ip_addr, 192, 168, 51, 1); + IP4_ADDR(&g_sysinfo.netif_ap_param.net_mask, 255, 255, 255, 0); + IP4_ADDR(&g_sysinfo.netif_ap_param.gateway, 192, 168, 51, 1); + + SYSINFO_DBG("set default value\n"); + + return 0; +} + +/** + * @brief Save sysinfo to flash + * @return 0 on success, -1 on failure + */ +int sysinfo_save(void) +{ + if (g_fdcm_hdl == NULL) { + SYSINFO_ERR("uninitialized, hdl %p\n", g_fdcm_hdl); + return -1; + } + + if (fdcm_write(g_fdcm_hdl, &g_sysinfo, SYSINFO_SIZE) != SYSINFO_SIZE) { + SYSINFO_ERR("fdcm write failed\n"); + return -1; + } + + SYSINFO_DBG("save sysinfo to flash\n"); + + return 0; +} + +/** + * @brief Load sysinfo from flash + * @return 0 on success, -1 on failure + */ +int sysinfo_load(void) +{ + if (g_fdcm_hdl == NULL) { + SYSINFO_ERR("uninitialized, hdl %p\n", g_fdcm_hdl); + return -1; + } + + if (fdcm_read(g_fdcm_hdl, &g_sysinfo, SYSINFO_SIZE) != SYSINFO_SIZE) { + SYSINFO_WRN("fdcm read failed\n"); + return -1; + } + + SYSINFO_DBG("load sysinfo from flash\n"); + + return 0; +} + +/** + * @brief Get the pointer of the sysinfo + * @return Pointer to the sysinfo, NULL on failure + */ +struct sysinfo *sysinfo_get(void) +{ + if (g_fdcm_hdl == NULL) { + SYSINFO_ERR("uninitialized, hdl %p\n", g_fdcm_hdl); + return NULL; + } + + return &g_sysinfo; +} + diff --git a/platform/mcu/xr871/project/common/framework/sysinfo.h b/platform/mcu/xr871/project/common/framework/sysinfo.h new file mode 100644 index 0000000000..3da9f509e1 --- /dev/null +++ b/platform/mcu/xr871/project/common/framework/sysinfo.h @@ -0,0 +1,127 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _SYSINFO_H_ +#define _SYSINFO_H_ + +#include +#include "lwip/netif.h" +#include "net/wlan/wlan.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Sysinfo MAC address source definition + */ +#define SYSINFO_MAC_ADDR_CODE (0x0U) +#define SYSINFO_MAC_ADDR_EFUSE (0x1U) +#define SYSINFO_MAC_ADDR_FLASH (0x2U) + +#define SYSINFO_MAC_ADDR_LEN (6) + +#define SYSINFO_SSID_LEN_MAX (32) +#define SYSINFO_PSK_LEN_MAX (65) + +/** + * @brief Sysinfo station wlan parameters definition + */ +struct sysinfo_wlan_sta_param { + uint8_t ssid[SYSINFO_SSID_LEN_MAX]; + uint8_t ssid_len; + + uint8_t psk[SYSINFO_PSK_LEN_MAX]; +}; + +/** + * @brief Sysinfo AP wlan parameters definition + */ +struct sysinfo_wlan_ap_param { + uint8_t ssid[SYSINFO_SSID_LEN_MAX]; + uint8_t ssid_len; + + uint8_t psk[SYSINFO_PSK_LEN_MAX]; + + uint8_t channel; +}; + +/** + * @brief Sysinfo net interface parameters definition + */ +struct sysinfo_netif_param { +#ifdef __CONFIG_LWIP_V1 + ip_addr_t ip_addr; + ip_addr_t net_mask; + ip_addr_t gateway; +#elif LWIP_IPV4 /* now only for IPv4 */ + ip4_addr_t ip_addr; + ip4_addr_t net_mask; + ip4_addr_t gateway; +#else + #error "IPv4 not support!" +#endif +}; + +/** + * @brief Sysinfo structure definition + */ +struct sysinfo { + uint32_t version; + + uint32_t sta_use_dhcp : 1; + + uint8_t mac_addr[SYSINFO_MAC_ADDR_LEN]; + + enum wlan_mode wlan_mode; + + struct sysinfo_wlan_sta_param wlan_sta_param; + struct sysinfo_wlan_ap_param wlan_ap_param; + + struct sysinfo_netif_param netif_sta_param; + struct sysinfo_netif_param netif_ap_param; +}; + +#define SYSINFO_SIZE sizeof(struct sysinfo) + +int sysinfo_init(void); +void sysinfo_deinit(void); + +int sysinfo_default(void); + +int sysinfo_save(void); +int sysinfo_load(void); + +struct sysinfo *sysinfo_get(void); + +#ifdef __cplusplus +} +#endif + +#endif /* _SYSINFO_H_ */ diff --git a/platform/mcu/xr871/project/common/framework/sysinfo_debug.h b/platform/mcu/xr871/project/common/framework/sysinfo_debug.h new file mode 100644 index 0000000000..81c09c935a --- /dev/null +++ b/platform/mcu/xr871/project/common/framework/sysinfo_debug.h @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _SYSINFO_DEBUG_H_ +#define _SYSINFO_DEBUG_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define SYSINFO_DBG_ON 0 +#define SYSINFO_WRN_ON 0 +#define SYSINFO_ERR_ON 1 +#define SYSINFO_ABORT_ON 0 + +#define SYSINFO_SYSLOG printf +#define SYSINFO_ABORT() do { } while (0) + +#define SYSINFO_LOG(flags, fmt, arg...) \ + do { \ + if (flags) \ + SYSINFO_SYSLOG(fmt, ##arg); \ + } while (0) + +#define SYSINFO_DBG(fmt, arg...) \ + SYSINFO_LOG(SYSINFO_DBG_ON, "[sysinfo] "fmt, ##arg) + +#define SYSINFO_WRN(fmt, arg...) \ + SYSINFO_LOG(SYSINFO_WRN_ON, "[sysinfo WRN] "fmt, ##arg) + +#define SYSINFO_ERR(fmt, arg...) \ + do { \ + SYSINFO_LOG(SYSINFO_ERR_ON, "[sysinfo ERR] %s():%d, "fmt, \ + __func__, __LINE__, ##arg); \ + if (SYSINFO_ABORT_ON) \ + SYSINFO_ABORT(); \ + } while (0) + +#ifdef __cplusplus +} +#endif + +#endif /* _SYSINFO_DEBUG_H_ */ diff --git a/platform/mcu/xr871/project/common/startup/gcc/exception.c b/platform/mcu/xr871/project/common/startup/gcc/exception.c new file mode 100644 index 0000000000..3717981e44 --- /dev/null +++ b/platform/mcu/xr871/project/common/startup/gcc/exception.c @@ -0,0 +1,231 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include "kernel/os/os.h" +#include "sys/param.h" +#include "driver/chip/chip.h" + +#ifndef __CONFIG_BOOTLOADER + +#define DUMP_BUF_LEN 64 + +#define readb(addr) (*((volatile unsigned char *)(addr))) +#define readw(addr) (*((volatile unsigned short *)(addr))) +#define readl(addr) (*((volatile unsigned int *)(addr))) + +#define CPU_REG_NVIC_ICSR (readl(0xE000ED04)) /* Int Ctrl State Reg. */ +#define CPU_REG_NVIC_SHCSR (readl(0xE000ED24)) /* Hard Fault Status Reg. */ +#define CPU_REG_NVIC_HFSR (readl(0xE000ED2C)) /* Hard Fault Status Reg. */ +#define CPU_REG_NVIC_DFSR (readl(0xE000ED30)) /* Debug Fault Status Reg. */ +#define CPU_REG_NVIC_MMFAR (readl(0xE000ED34)) /* Mem Manage Addr Reg. */ +#define CPU_REG_NVIC_BFAR (readl(0xE000ED38)) /* Bus Fault Addr Reg. */ +#define CPU_REG_FPU_FPSCR (readl(0xE000ED38)) /* FP status and control Reg. */ + +extern uint8_t __text_start__[]; +extern uint8_t __text_end__[]; +extern uint8_t _estack[]; + +#if configDEBUG_TRACE_TASK_MOREINFO +static char dbg_tasks_buf[1024]; +#endif + +/* defined for other modules to trace exception */ +volatile int exceptin_step; + +/* + * GET CURRENT EXCETOPM + * + * Description: get the source number of current exception, include all exceptions. + * Arguments : none. + * Returns : the source number of current exception. + * Note : + */ +static uint32_t intc_get_current_exception(void) +{ + return (CPU_REG_NVIC_ICSR & 0x3ff); +} + +static void exception_hex_dump(const uint32_t *addr, uint32_t num) +{ + uint32_t i; + + for (i = 0; i < num; i++) { + if ((i & 0x03) == 0x0) + printf("\n[%p]: ", addr); + printf("0x%08x ", *addr++); + } + printf("\n"); +} + +/* + * EXCEPTION ENTRY + * + * Description: the entry of CPU exception, mainly for CPU nmi, hard fault, + * memmanger fault, bus fault, usage fault, SVCall, debugmonitor exception. + * + * register list in stack: + * ---------LOW ADDR-------- + * R4 <-PSP(pstack) + * R5 + * R6 + * R7 + * R8 + * R9 + * R10 + * R11 <-PSP+4*7 + * R0 <-PSP+4*8 + * R1 + * R2 + * R3 + * R12 <-PSP+4*12 + * R14(LR) <-PSP+4*13 + * R15(PC) <-PSP+4*14 + * xPSR <-PSP+4*15 + * --------HIGH ADDR-------- + * Arguments : pstack:the pointer of stack, PSP before the exception happen. + * Returns : OK if process CPU exception succeeded, others if failed. + */ +int32_t exception_entry(uint32_t *pstack, uint32_t *msp, uint32_t *psp) +{ + uint32_t i; + uint32_t exceptionno = intc_get_current_exception(); + const uint32_t *add = pstack + 16; + uint32_t lr, pc; + uint32_t len; + + /* intno can't beyond then 16 */ + //ASSERT(exceptionno < 16); + + printf("\nexception:%d happen!!\n", exceptionno); + printf("appos pstack:0x%x msp:0x%x psp:0x%x\n", + (uint32_t)pstack, (uint32_t)msp, (uint32_t)psp); + + switch (exceptionno) { + case 2: + printf("NMI happen\n"); + break; + case 3: + printf("hard fault happen, HFSR:0x%x\n", CPU_REG_NVIC_HFSR); + printf("memm fault maybe happen, MFSR:0x%x, MMFAR:0x%x\n", + readb(0xE000ED28), CPU_REG_NVIC_MMFAR); + printf("bus fault maybe happen, BFSR:0x%x, BFAR:0x%x\n", + readb(0xE000ED29), CPU_REG_NVIC_BFAR); + printf("usage fault maybe happen, UFSR:0x%x\n", readw(0xE000ED2A)); + break; + case 4: + printf("memm fault happen, MFSR:0x%x, MMFAR:0x%x\n", + readb(0xE000ED28), CPU_REG_NVIC_MMFAR); + break; + case 5: + printf("bus fault happen, BFSR:0x%x, BFAR:0x%x\n", + readb(0xE000ED29), CPU_REG_NVIC_BFAR); + break; + case 6: + printf("usage fault happen, UFSR:0x%x\n", readw(0xE000ED2A)); + break; + case 11: + printf("SVCall fault happen\n"); + break; + case 12: + printf("SVCall fault happen, DFSR:0x%x\n", CPU_REG_NVIC_DFSR); + break; + default: + /* invalid exception nr */ + printf("invalid exception nr\n"); + } + + /* print R0-R3 */ + printf("CPU registers:\n"); + for (i = 0; i <= 3; i++) { + printf("R%02d:[%p]: 0x%08x\n", i, (pstack + (8 + i)), *(pstack + (8 + i))); + } + + /* print R4-R11 */ + for (i = 0; i <= 7; i++) { + printf("R%02d:[%p]: 0x%08x\n", i + 4, (pstack + i), *(pstack + i)); + } + + /* print R12, R14(LR), R15(PC), xPSR */ + printf("R12:[%p]: 0x%08x\n", (pstack + 12), *(pstack + 12)); + lr = *(pstack + 13); + printf("R14(LR):[%p]: 0x%08x\n", (pstack + 13), lr); + pc = *(pstack + 14); + printf("R15(PC):[%p]: 0x%08x\n", (pstack + 14), pc); + printf("xPSR:[%p]: 0x%08x\n", (pstack + 15), *(pstack + 15)); + printf("SHCSR:0x%08x step:%x\n", CPU_REG_NVIC_SHCSR, exceptin_step); +#if ((__FPU_PRESENT == 1) && (__FPU_USED == 1)) + printf("FPSCR:0x%08x\n", CPU_REG_FPU_FPSCR); +#endif + + printf("\nstack info:"); + if ((((uint32_t)add) + DUMP_BUF_LEN*4) <= (uint32_t)_estack) + len = DUMP_BUF_LEN; + else + len = (((uint32_t)_estack) - ((uint32_t)add))/4; + len = MIN(len, DUMP_BUF_LEN*2); + exception_hex_dump(add, len); + + printf("\n[LR]:0x%x", lr); + lr &= ~0x07; + if (lr > (uint32_t)__text_start__ && lr < (uint32_t)__text_end__) { + lr -= DUMP_BUF_LEN * 2; + exception_hex_dump((const uint32_t *)lr, DUMP_BUF_LEN); + } + + printf("\n[PC]:0x%x", pc); + pc &= ~0x07; + if (pc < (uint32_t)__text_end__) { + pc -= DUMP_BUF_LEN * 2; + pc = (pc < (uint32_t)__text_end__) ? pc : 0; + exception_hex_dump((const uint32_t *)pc, DUMP_BUF_LEN); + } + +#if configDEBUG_TRACE_TASK_MOREINFO + printf("\ntasks state:\n"); + vTaskList(dbg_tasks_buf); + printf("%s\n", dbg_tasks_buf); +#endif + + /* if happen fault, print important information and drop-dead halt */ + while (1) + ; + + return 0; +} + +void exception_panic(const char *file, const char *func, const int line) +{ + printf("panic at %s func:%s line:%d!!\n", file, func, line); + + __asm volatile ("bkpt 0"); +} + +#endif /* __CONFIG_BOOTLOADER */ diff --git a/platform/mcu/xr871/project/common/startup/gcc/retarget.c b/platform/mcu/xr871/project/common/startup/gcc/retarget.c new file mode 100644 index 0000000000..e92a537d35 --- /dev/null +++ b/platform/mcu/xr871/project/common/startup/gcc/retarget.c @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include "compiler.h" +#include "driver/chip/system_chip.h" +#include "driver/chip/hal_global.h" +#include "common/board/board.h" + +extern int stdout_init(void); + +void hardware_init_hook(void) +{ + HAL_BoardIoctlCbRegister(board_ioctl); + SystemInit(); +} + +int __wrap_main(void) +{ + SystemCoreClockUpdate(); + HAL_GlobalInit(); + __real_main(); + return -1; +} + +//#ifdef __CONFIG_MALLOC_USE_STDLIB +// +///* Linker defined symbol used by _sbrk to indicate where heap should start. */ +//extern uint8_t __end__[]; /* heap start address */ +//extern uint8_t _estack[]; /* heap end address */ +// +//static uint8_t *heap = __end__; +// +///* Dynamic memory allocation related syscall. */ +//void *_sbrk(int incr) +//{ +// uint8_t *prev_heap = heap; +// uint8_t *new_heap = heap + incr; +// +// /* avoid corrupting heap data by the increase of main stack (MSP) */ +// if (new_heap >= _estack - PRJCONF_MSP_STACK_SIZE) { +// printf("heap exhausted, incr %d, %p >= %p\n", +// incr, new_heap, _estack - PRJCONF_MSP_STACK_SIZE); +// return (void *)-1; +// } +// +// heap = new_heap; +// return (void *)prev_heap; +//} +// +//void heap_get_space(uint8_t **start, uint8_t **end, uint8_t **current) +//{ +// *start = __end__; +// *end = _estack - PRJCONF_MSP_STACK_SIZE; +// *current = heap; +//} +// +//#endif /* __CONFIG_MALLOC_USE_STDLIB */ +// +//void _exit(int return_code) +//{ +// printf("ERR: %s() not support\n", __func__); +// while (1); +//} diff --git a/platform/mcu/xr871/project/common/startup/gcc/retarget_main.c b/platform/mcu/xr871/project/common/startup/gcc/retarget_main.c new file mode 100644 index 0000000000..e09524024d --- /dev/null +++ b/platform/mcu/xr871/project/common/startup/gcc/retarget_main.c @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include "kernel/os/os_thread.h" + +extern int __real_main(void); + +static OS_Thread_t g_main_thread; + +static void main_task(void *arg) +{ + __real_main(); + OS_ThreadDelete(&g_main_thread); +} + +void main_task_start(void) +{ + if (OS_ThreadCreate(&g_main_thread, + "", + main_task, + NULL, + PRJCONF_MAIN_THREAD_PRIO, + PRJCONF_MAIN_THREAD_STACK_SIZE) != OS_OK) { + printf("[ERR] create main task failed\n"); + } + + OS_ThreadStartScheduler(); + + while (1) { + printf("error\n"); + } +} diff --git a/platform/mcu/xr871/project/common/startup/gcc/retarget_stdout.c b/platform/mcu/xr871/project/common/startup/gcc/retarget_stdout.c new file mode 100644 index 0000000000..b9e54e9839 --- /dev/null +++ b/platform/mcu/xr871/project/common/startup/gcc/retarget_stdout.c @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include /* for STDOUT_FILENO and STDERR_FILENO */ + +#include "compiler.h" +#include "kernel/os/os_mutex.h" +#include "driver/chip/hal_cmsis.h" +#include "common/board/board.h" + + +/* + * retarget for standard output/error + */ + +static OS_Mutex_t g_stdout_mutex; +static uint8_t g_stdout_enable = 1; +static UART_ID g_stdout_uart_id = UART_NUM; + +/* case of critical context + * - IRQ enable + * - FIQ enable + * - Execute in ISR context + * - Scheduler is not running + */ +static __always_inline int stdout_is_critical_context(void) +{ + return (__get_PRIMASK() || + __get_FAULTMASK() || + __get_IPSR() || + !OS_ThreadIsSchedulerRunning()); +} + +static void stdout_mutex_lock(void) +{ + if (stdout_is_critical_context()) { + return; + } + + if (OS_MutexIsValid(&g_stdout_mutex)) { + OS_RecursiveMutexLock(&g_stdout_mutex, OS_WAIT_FOREVER); + } else { + OS_RecursiveMutexCreate(&g_stdout_mutex); + OS_RecursiveMutexLock(&g_stdout_mutex, OS_WAIT_FOREVER); + } +} + +static void stdout_mutex_unlock(void) +{ + if (stdout_is_critical_context()) { + return; + } + + if (OS_MutexIsValid(&g_stdout_mutex)) { + OS_RecursiveMutexUnlock(&g_stdout_mutex); + } +} + +void stdout_enable(uint8_t en) +{ + g_stdout_enable = en; +} + +int stdout_init(void) +{ + if (g_stdout_uart_id < UART_NUM) { + return 0; + } + + if (board_uart_init(BOARD_MAIN_UART_ID) == HAL_OK) { + g_stdout_uart_id = BOARD_MAIN_UART_ID; + return 0; + } + return -1; +} + +int stdout_deinit(void) +{ + if (g_stdout_uart_id >= UART_NUM) { + return 0; + } + + if (board_uart_deinit(g_stdout_uart_id) == HAL_OK) { + g_stdout_uart_id = UART_NUM; + return 0; + } + return -1; +} + +int _write(int fd, char *buf, int count) +{ + int ret; + + if (fd != STDOUT_FILENO && fd != STDERR_FILENO) { + return -1; + } + + if (!g_stdout_enable || g_stdout_uart_id >= UART_NUM) + return -1; + + stdout_mutex_lock(); + ret = board_uart_write(g_stdout_uart_id, buf, count); + stdout_mutex_unlock(); + + return ret; +} diff --git a/platform/mcu/xr871/project/common/startup/gcc/startup.S b/platform/mcu/xr871/project/common/startup/gcc/startup.S new file mode 100644 index 0000000000..07bf9d7b26 --- /dev/null +++ b/platform/mcu/xr871/project/common/startup/gcc/startup.S @@ -0,0 +1,581 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + .syntax unified +#ifdef __CONFIG_CPU_CM4F + .cpu cortex-m4 + .fpu softvfp +#else + .cpu cortex-m3 +#endif + .thumb + +.global g_pfnVectors +.global Default_Handler + +/* start address for the initialization values of the .data section. +defined in linker script */ +.word _sidata +/* start address for the .data section. defined in linker script */ +.word _sdata +/* end address for the .data section. defined in linker script */ +.word _edata +/* start address for the .bss section. defined in linker script */ +.word _sbss +/* end address for the .bss section. defined in linker script */ +.word _ebss + + .equ NVIC_SCR, (0xe000e000 + 0xd10) + .equ NVIC_SYSTICK_CTRL, (0xe000e000 + 0x010) + .equ NVIC_ICSR, (0xe000e000 + 0xd04) + .equ GPRCM_CPUA_BOOT_FLAG, (0x40040000 + 0x100) + .equ GPRCM_CPUA_BOOT_ADDR, (0x40040000 + 0x104) + .equ GPRCM_CPUA_BOOT_ARG, (0x40040000 + 0x108) + .equ GPRCM_CPUA_BOOT_FLAG_MASK, (3) + .equ GPRCM_CPUA_BOOT_FLAG_WAKEUP, (1) + .equ GPRCM_SYSCLK1_CTRLS, (0x40040000 + 0x024) + .equ GPRCM_SYSCLK1_EN_DIV_MASK, (0x8000000f) + .equ GPRCM_SYSCLK1_LFCLK, (0x00010000) + +/*.equ BootRAM, 0xF108F85F*/ +/** + * @brief This is the code that gets called when the processor first + * starts execution following a reset event. Only the absolutely + * necessary set is performed, after which the application + * supplied main() routine is called. + * @param None + * @retval : None +*/ + + .section .text.Reset_Handler + .weak Reset_Handler + .type Reset_Handler, %function + .size Reset_Handler, .-Reset_Handler +Reset_Handler: + ldr r0, =_estack + mov sp, r0 /* set stack pointer */ + + cpsie i + cpsie f + bl _start + +LoopForever: + b LoopForever + + +.size Reset_Handler, .-Reset_Handler + +/** + * @brief This is the code that gets called when the processor receives an + * unexpected interrupt. This simply enters an infinite loop, preserving + * the system state for examination by a debugger. + * + * @param None + * @retval : None +*/ + .extern exception_entry + + .section .cpu_text,"ax",%progbits +Default_Handler: +#ifndef __CONFIG_BOOTLOADER + CPSID F + TST LR, #0x04 + ITE EQ + MRSEQ R0, MSP + MRSNE R0, PSP + STMDB.W R0!, {R4-R11} + MRS R1, MSP + MRS R2, PSP + BL exception_entry +#endif +Infinite_Loop: + b Infinite_Loop + .size Default_Handler, .-Default_Handler + +/****************************************************************************** +* +* The minimal vector table for a Cortex M3. Note that the proper constructs +* must be placed on this to ensure that it ends up at physical address +* 0x0000.0000. +* +******************************************************************************/ + .section .isr_vector,"a",%progbits + .type g_pfnVectors, %object + .size g_pfnVectors, .-g_pfnVectors + + +g_pfnVectors: + + .word _estack + .word Reset_Handler + .word NMI_Handler + .word HardFault_Handler + .word MemManage_Handler + .word BusFault_Handler + .word UsageFault_Handler + .word 0 + .word 0 + .word 0 + .word 0 + .word SVC_Handler + .word DebugMon_Handler + .word 0 + .word PendSV_Handler + .word SysTick_Handler + + /* External Interrupts */ + .word DMA_IRQHandler // 16 +#ifndef __CONFIG_BOOTLOADER + .word GPIOA_IRQHandler + .word SDC_IRQHandler + .word MBOX_A_IRQHandler + .word UART0_IRQHandler // 20 + .word UART1_IRQHandler + .word SPI0_IRQHandler + .word SPI1_IRQHandler + .word TWI0_IRQHandler + .word TWI1_IRQHandler + .word WDG_IRQHandler + .word TIMER0_IRQHandler + .word TIMER1_IRQHandler + .word RTC_SecAlarm_IRQHandler + .word RTC_WDayAlarm_IRQHandler // 30 + .word CSI_IRQHandler + .word I2S_IRQHandler + .word PWM_ECT_IRQHandler + .word CE_IRQHandler + .word GPADC_IRQHandler + .word GPIOB_IRQHandler + .word DMIC_IRQHandler + .word IRRX_IRQHandler + .word IRTX_IRQHandler + .word MBOX_N_IRQHandler // 40 + .word 0 + .word 0 + .word N_UART_IRQHandler +#else + .word 0 //GPIOA_IRQHandler + .word 0 //SDC_IRQHandler + .word 0 //MBOX_A_IRQHandler + .word UART0_IRQHandler // 20 + .word UART1_IRQHandler + .word SPI0_IRQHandler + .word 0 //SPI1_IRQHandler + .word 0 //TWI0_IRQHandler + .word 0 //TWI1_IRQHandler + .word 0 //WDG_IRQHandler + .word 0 //TIMER0_IRQHandler + .word 0 //TIMER1_IRQHandler + .word 0 //RTC_SecAlarm_IRQHandler + .word 0 //RTC_WDayAlarm_IRQHandler // 30 + .word 0 //CSI_IRQHandler + .word 0 //I2S_IRQHandler + .word 0 //PWM_ECT_IRQHandler + .word 0 //CE_IRQHandler + .word 0 //GPADC_IRQHandler + .word 0 //GPIOB_IRQHandler + .word 0 //DMIC_IRQHandler + .word 0 //IRRX_IRQHandler + .word 0 //IRTX_IRQHandler + .word 0 //MBOX_N_IRQHandler // 40 + .word 0 + .word 0 + .word 0 +#endif + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 // 50 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 + .word 0 // 60 + .word 0 + .word 0 + .word 0 + + +/******************************************************************************* +* +* Provide weak aliases for each Exception handler to the Default_Handler. +* As they are weak aliases, any function with the same name will override +* this definition. +* +*******************************************************************************/ + + .weak NMI_Handler + .thumb_set NMI_Handler,Default_Handler + + .weak HardFault_Handler + .thumb_set HardFault_Handler,Default_Handler + + .weak MemManage_Handler + .thumb_set MemManage_Handler,Default_Handler + + .weak BusFault_Handler + .thumb_set BusFault_Handler,Default_Handler + + .weak UsageFault_Handler + .thumb_set UsageFault_Handler,Default_Handler + + .weak SVC_Handler + .thumb_set SVC_Handler,Default_Handler + + .weak DebugMon_Handler + .thumb_set DebugMon_Handler,Default_Handler + +// .weak PendSV_Handler +// .thumb_set PendSV_Handler,Default_Handler + +// .weak SysTick_Handler +// .thumb_set SysTick_Handler,Default_Handler + + + .weak DMA_IRQHandler + .thumb_set DMA_IRQHandler,Default_Handler + + .weak GPIOA_IRQHandler + .thumb_set GPIOA_IRQHandler,Default_Handler + + .weak SDC_IRQHandler + .thumb_set SDC_IRQHandler,Default_Handler + + .weak MBOX_A_IRQHandler + .thumb_set MBOX_A_IRQHandler,Default_Handler + + .weak MBOX_N_IRQHandler + .thumb_set MBOX_N_IRQHandler,Default_Handler + + .weak UART0_IRQHandler + .thumb_set UART0_IRQHandler,Default_Handler + + .weak UART1_IRQHandler + .thumb_set UART1_IRQHandler,Default_Handler + + .weak SPI0_IRQHandler + .thumb_set SPI0_IRQHandler,Default_Handler + + .weak SPI1_IRQHandler + .thumb_set SPI1_IRQHandler,Default_Handler + + .weak TWI0_IRQHandler + .thumb_set TWI0_IRQHandler,Default_Handler + + .weak TWI1_IRQHandler + .thumb_set TWI1_IRQHandler,Default_Handler + + .weak WDG_IRQHandler + .thumb_set WDG_IRQHandler,Default_Handler + + .weak TIMER0_IRQHandler + .thumb_set TIMER0_IRQHandler,Default_Handler + + .weak TIMER1_IRQHandler + .thumb_set TIMER1_IRQHandler,Default_Handler + + .weak RTC_SecAlarm_IRQHandler + .thumb_set RTC_SecAlarm_IRQHandler,Default_Handler + + .weak RTC_WDayAlarm_IRQHandler + .thumb_set RTC_WDayAlarm_IRQHandler,Default_Handler + + .weak CSI_IRQHandler + .thumb_set CSI_IRQHandler,Default_Handler + + .weak I2S_IRQHandler + .thumb_set I2S_IRQHandler,Default_Handler + + .weak PWM_ECT_IRQHandler + .thumb_set PWM_ECT_IRQHandler,Default_Handler + + .weak CE_IRQHandler + .thumb_set CE_IRQHandler,Default_Handler + + .weak GPADC_IRQHandler + .thumb_set GPADC_IRQHandler,Default_Handler + + .weak GPIOC_IRQHandler + .thumb_set GPIOC_IRQHandler,Default_Handler + + .weak DMIC_IRQHandler + .thumb_set DMIC_IRQHandler,Default_Handler + + .weak IRRX_IRQHandler + .thumb_set IRRX_IRQHandler,Default_Handler + + .weak IRTX_IRQHandler + .thumb_set IRTX_IRQHandler,Default_Handler + + .weak N_UART_IRQHandler + .thumb_set N_UART_IRQHandler,Default_Handler + + .weak N_SPI_IRQHandler + .thumb_set N_SPI_IRQHandler,Default_Handler + + .weak N_WDT_IRQHandler + .thumb_set N_WDT_IRQHandler,Default_Handler + + .weak N_TIMER0_IRQHandler + .thumb_set N_TIMER0_IRQHandler,Default_Handler + + .weak N_TIMER1_IRQHandler + .thumb_set N_TIMER1_IRQHandler,Default_Handler + + .weak N_SDC_IRQHandler + .thumb_set N_SDC_IRQHandler,Default_Handler + + .weak N_WIFIC_IRQHandler + .thumb_set N_WIFIC_IRQHandler,Default_Handler + + .weak WKUP_TIMER0_IRQHandler + .thumb_set WKUP_TIMER0_IRQHandler,Default_Handler + + .weak WKUP_TIMER1_IRQHandler + .thumb_set WKUP_TIMER1_IRQHandler,Default_Handler + + /*------------------ void __cpu_sleep(int nouse) ------------------------*/ +#ifndef __CONFIG_BOOTLOADER + + .thumb_func + .section .cpu_text + .type __cpu_sleep, %function + .global __cpu_sleep + +__cpu_sleep: + .fnstart + .cantunwind + + PUSH {R0-R12, LR} + + /* clear pending and disable systick */ + LDR R2, =NVIC_SYSTICK_CTRL + LDR R5, [R2] + MOV R3, R5 + BIC R3, #0x03 + STR R3, [R2] + + LDR R2, =NVIC_ICSR + LDR R1, [R2] + AND R1, #0x4000000 + ASR R1, #1 + STR R1, [R2] + + /* switch to 32K/div */ + LDR R1, =GPRCM_SYSCLK1_CTRLS + LDR R0, [R1] + BIC R0, R0, #0x30000 + ORR R0, R0, #0x10000 + STR R0, [R1] + DSB + ISB + + WFI + WFI + WFI + + /* switch cpu clk to pll */ + LDR R1, =GPRCM_SYSCLK1_CTRLS + LDR R0, [R1] + BIC R0, R0, #0x30000 + ORR R0, R0, #0x20000 + STR R0, [R1] + DSB + ISB + + /* enable systick */ + LDR R2, =NVIC_SYSTICK_CTRL + STR R5, [R2] + + POP {R0-R12, PC} + + .fnend + .size __cpu_sleep, .-__cpu_sleep + + + /*------------------ void __cpu_suspend(int nouse) ------------------------*/ + .global check_wakeup_irqs + + .thumb_func + .section .cpu_text + .type __cpu_suspend, %function + .global __cpu_suspend + +__cpu_suspend: + .fnstart + .cantunwind + + PUSH {R0-R12, LR} + ISB + + LDR R0, =GPRCM_CPUA_BOOT_ARG + ISB + LDR R1, [R0] + MRS R0, MSP + ISB + STR R0, [R1] + + MRS R0, PSP + ISB + STR R0, [R1, #4] + + MRS R0, PRIMASK + STR R0, [R1, #12] + + MRS R0, FAULTMASK + STR R0, [R1, #16] + + MRS R0, BASEPRI + STR R0, [R1, #20] + + MRS R0, CONTROL + STR R0, [R1, #24] + + /* set deepsleep mode */ + LDR R0, =0x14 + LDR R1, =NVIC_SCR + ISB + STR R0, [R1] + + /* set bootflag */ + LDR R0, =0x429b0001 + LDR R1, =GPRCM_CPUA_BOOT_FLAG + ISB + STR R0, [R1] + + /* set resume address in thumb state */ + LDR R0, =resume + ORR.W R0, R0, #1 + LDR R1, =GPRCM_CPUA_BOOT_ADDR + ISB + STR R0, [R1] + + /* switch to 32K/div */ + LDR R1, =GPRCM_SYSCLK1_CTRLS + ISB + LDR R0, [R1] + BIC R0, R0, #0x30000 + ORR R0, R0, #0x10000 + STR R0, [R1] + DSB + ISB + NOP + NOP + NOP + + /* the WFE instruction will cause two kinds of CPU actions: + * 1. EVNET_REGISTER = 1, WFE will clear the EVENT_REGISTER and the + * CPU executes the next instruction. + * 2. EVENT_REGISTER = 0, WFE will make the CPU go to SLEEP state. + */ + /* first time executing WFE instruction, there are some different + * situations as follows: + * 1. if there are interrupts pending and be cleared already, + * the WFE will only clear the CPU EVENT_REGISTER. + * 2. if there are new interrupts pending after ar400_deepsleep_lock + * operation, the WFE will only clear the CPU EVENT_REGISTER. + * 3. if the SEV/NMI/DEBUG events coming before now, WFE will only + * clear the CPU EVENT_REGISTER. + * 4. if there are no SEV/NMI/DEBUG events before and no interrupts + * pending too, WFE wil make the CPU go to the SLEEP state. + */ + WFE + + /* read the NVIC SET_PENDING_REGISTER to check whether there are + * any new pending interrupts after ar400_deepsleep_lock operation + * which make the first WFE executing failed. + * 1. If ther are some new pending interrupts, jump to the RESUME_ENTRY + * and abandon the next WFE execution. + * 2. If there is no new pending interrupts, we execute WFE instruction + * twice to ensure the CPU goes to SLEEP state successfully. + */ + BL check_wakeup_irqs + CMP R0, #0 + BNE resume + ISB + NOP + NOP + NOP + + WFE + NOP + +resume: + /* switch cpu clk to pll */ + LDR R1, =GPRCM_SYSCLK1_CTRLS + ISB + LDR R0, [R1] + BIC R0, R0, #0x30000 + ORR R0, R0, #0x20000 + STR R0, [R1] + DSB + ISB + + /* restore cpu contex */ + LDR R0, =GPRCM_CPUA_BOOT_ARG + ISB + LDR R1, [R0] + + LDR R0, [R1] + MSR MSP, R0 + ISB + + LDR R0, [R1,#4] + MSR PSP, R0 + ISB + + LDR R0, [R1, #12] + MSR PRIMASK, R0 + + LDR R0, [R1, #16] + MSR FAULTMASK, R0 + + LDR R0, [R1, #20] + MSR BASEPRI, R0 + + LDR R0, [R1, #24] + MSR CONTROL, R0 + ISB + NOP + + POP {R0-R12, PC} + NOP + + .fnend + .size __cpu_suspend, .-__cpu_suspend + +#endif diff --git a/platform/mcu/xr871/project/main/main.c b/platform/mcu/xr871/project/main/main.c new file mode 100644 index 0000000000..f6228e1011 --- /dev/null +++ b/platform/mcu/xr871/project/main/main.c @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#include "common/framework/platform_init.h" + +int main(void) +{ + aos_startup(); +} diff --git a/platform/mcu/xr871/project/main/prj_config.h b/platform/mcu/xr871/project/main/prj_config.h new file mode 100644 index 0000000000..6205153544 --- /dev/null +++ b/platform/mcu/xr871/project/main/prj_config.h @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _PRJ_CONFIG_H_ +#define _PRJ_CONFIG_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * project base config + */ +#define PRJCONF_MSP_STACK_SIZE (1 * 1024) /* stack size for IRQ service */ + +/* main thread */ +#define PRJCONF_MAIN_THREAD_PRIO OS_THREAD_PRIO_APP +#define PRJCONF_MAIN_THREAD_STACK_SIZE (1 * 1024) + +/* sys ctrl */ +#define PRJCONF_SYS_CTRL_EN 1 +#define PRJCONF_SYS_CTRL_PRIO OS_THREAD_PRIO_SYS_CTRL +#define PRJCONF_SYS_CTRL_STACK_SIZE (2 * 1024) +#define PRJCONF_SYS_CTRL_QUEUE_LEN (6) + +/* image */ +#define PRJCONF_IMG_FLASH (0) +#define PRJCONF_IMG_ADDR (0x00000000) +#define PRJCONF_IMG_SIZE ((1 << 20) - (4 << 10)) + +/* sysinfo */ +#define PRJCONF_SYSINFO_FLASH (0) +#define PRJCONF_SYSINFO_ADDR ((1 << 20) - (4 << 10)) +#define PRJCONF_SYSINFO_SIZE (4 << 10) + +/* MAC address source */ +#define PRJCONF_MAC_ADDR_SOURCE SYSINFO_MAC_ADDR_EFUSE + +/* watchdog hardware and service */ +#define PRJCONF_WDG_EN 0 +#define PRJCONF_WDG_TIMEOUT WDG_TIMEOUT_16SEC +#define PRJCONF_WDG_FEED_PERIOD (10 * 1000) /* in ms, MUST less than PRJCONF_WDG_TIMEOUT */ + +/* + * project hardware feature (enable/disable) + */ +#define PRJCONF_UART_EN 0 /* uart */ +#define PRJCONF_CE_EN 0 /* h/w crypto engine */ +#define PRJCONF_SPI_EN 1 /* spi */ +#define PRJCONF_MMC_EN 0 /* mmc */ +#define PRJCONF_MMC_DETECT_MODE CARD_ALWAYS_PRESENT /* mmc detect mode */ +#define PRJCONF_SOUNDCARD0_EN 1 /* sound card0, external audio codec */ +#define PRJCONF_SOUNDCARD1_EN 0 /* sound card1, internal dmic */ + +/* + * project service feature + */ +#define PRJCONF_CONSOLE_EN 0 /* console */ + +/* app pm mode + * - to override the default app pm mode, define PRJCONF_PM_MODE + */ +#define PRJCONF_PM_EN 1 /* app pm mode enable/disable */ +//#define PRJCONF_PM_MODE (PM_SUPPORT_SLEEP | PM_SUPPORT_STANDBY) + +/* network */ +#define PRJCONF_NET_EN 1 /* network and wlan */ + +/* net pm mode + * - to override the default net pm mode, define PRJCONF_NET_PM_MODE + */ +#define PRJCONF_NET_PM_EN 1 +//#define PRJCONF_NET_PM_MODE (PM_SUPPORT_HIBERNATION | PM_SUPPORT_POWEROFF) + +#ifdef __cplusplus +} +#endif + +#endif /* _PRJ_CONFIG_H_ */ diff --git a/platform/mcu/xr871/project/project.mk b/platform/mcu/xr871/project/project.mk new file mode 100644 index 0000000000..f210760f70 --- /dev/null +++ b/platform/mcu/xr871/project/project.mk @@ -0,0 +1,24 @@ +NAME := project + +$(NAME)_TYPE := kernel +$(NAME)_SOURCES := common/board/board.c \ + common/board/board_common.c \ + common/board/xr871_evb_main/board_config.c \ + common/framework/sys_ctrl/container.c \ + common/framework/sys_ctrl/event_queue.c \ + common/framework/sys_ctrl/observer.c \ + common/framework/sys_ctrl/publisher.c \ + common/framework/sys_ctrl/sys_ctrl.c \ + common/framework/img_ctrl.c \ + common/framework/net_ctrl.c \ + common/framework/net_sys.c \ + common/framework/platform_init.c \ + common/framework/sysinfo.c \ + common/startup/gcc/exception.c \ + common/startup/gcc/retarget.c \ + common/startup/gcc/startup.S \ + common/cmd/cmd_wlan.c \ + common/cmd/cmd_util.c \ + main/main.c + +$(NAME)_ASMFLAGS += -c -x assembler-with-cpp diff --git a/platform/mcu/xr871/src/audio/audio_manager/audio_manager.c b/platform/mcu/xr871/src/audio/audio_manager/audio_manager.c new file mode 100644 index 0000000000..4d70843863 --- /dev/null +++ b/platform/mcu/xr871/src/audio/audio_manager/audio_manager.c @@ -0,0 +1,223 @@ +#include +#include +#include "kernel/os/os_mutex.h" +#include "audio/manager/audio_manager.h" + +#define MANAGER_NULL 0 + +#if !MANAGER_NULL + +#ifndef __LIKELY_ +#define likely(x) __builtin_expect(!!(x), 1) +#endif +#ifndef __UNLIKELY_ +#define unlikely(x) __builtin_expect(!!(x), 0) +#endif + +#define BUG_ON(c) if (unlikely((c)!=0)) { \ + printf("Badness in %s at %s:%d/n", __func__, __FILE__, __LINE__);\ + return -1;\ + } +mgrctl_ctx g_mc; + +int aud_mgr_maxvol() +{ + return VOLUME_MAX_LEVEL; +} + +static int aud_set_mute(mgrctl_ctx* mc, int mute) +{ + BUG_ON(mute > 1 || mute < 0); + HAL_CODEC_MUTE_STATUS_Init(mute); + if (mc->playback == 0) + return 0; + + AUDIO_Device dev = mc->current_outdev; + if (HAL_CODEC_Mute(dev,mute) != 0) + return -1; + + return 0; +} + +static int aud_set_vol(mgrctl_ctx* mc, int level) +{ + BUG_ON(level > VOLUME_MAX_LEVEL || level < VOLUME_LEVEL0); + + int volume = level; + AUDIO_Device dev = mc->current_outdev; + + HAL_CODEC_INIT_VOLUME_Set(dev, volume); + if (mc->playback == 0) { + return 0; + } + + if (HAL_CODEC_VOLUME_LEVEL_Set(dev,volume) != 0) + return -1; + + return 0; +} + +static int aud_set_outdev(mgrctl_ctx* mc, int dev) +{ + BUG_ON(dev > AUDIO_DEVICE_SPEAKER || dev == 0); + + AUDIO_Device device = dev; + if (mc->current_outdev == device) + return 0; + else + mc->current_outdev = device; + + BUG_ON(mc->playback == 0); + + if (HAL_CODEC_ROUTE_Set(device) != 0) + return -1; + + return 0; +} + +static int aud_set_indev(mgrctl_ctx* mc, int dev) +{ + BUG_ON(dev != AUDIO_DEVICE_HEADPHONEMIC && dev != AUDIO_DEVICE_MAINMIC); + + AUDIO_Device device = dev; + if (mc->current_indev == device) + return 0; + else + mc->current_indev = device; + + BUG_ON(mc->record == 0); + + if (HAL_CODEC_ROUTE_Set(device) != 0) + return -1; + + return 0; +} + +static int __set_volume(mgrctl* m, int vol) +{ + mgrctl_ctx* mc; + mc = (mgrctl_ctx*)m; + if (aud_set_vol(mc, vol) != 0) + return -1; + return 0; +} + +static int __set_inpath(mgrctl* m, int dev) +{ + mgrctl_ctx* mc; + mc = (mgrctl_ctx*)m; + if (aud_set_indev(mc, dev) != 0) + return -1; + + return 0; +} + +static int __set_outpath(mgrctl* m, int dev) +{ + int ret = 0; + mgrctl_ctx* mc; + mc = (mgrctl_ctx*)m; + mc = (mgrctl_ctx*)m; + if (aud_set_outdev(mc, dev) != 0) + return -1; + return ret; +} + +static int __set_mute(mgrctl* m, int mute) +{ + mgrctl_ctx* mc; + mc = (mgrctl_ctx*)m; + if (aud_set_mute(mc, mute) != 0) + return -1; + return 0; +} + +static struct mgrctl_ops mgr_ops = +{ + .volume = __set_volume, + .in_path = __set_inpath, + .out_path = __set_outpath, + .mute = __set_mute, +}; + +static int set_mute(mgrctl* m, int mute) +{ + return m->ops->mute(m, mute); +} + +static int set_volume(mgrctl* m, int vol) +{ + return m->ops->volume(m, vol); +} + +static int set_inpath(mgrctl* m, int dev) +{ + return m->ops->in_path(m, dev); +} + +static int set_outpath(mgrctl* m, int dev) +{ + return m->ops->out_path(m, dev); +} + +/** + * event: + * 0: volume event + * 1: dev event + */ +int aud_mgr_handler(int event, int val) +{ + BUG_ON(event >= AUDIO_DEVICE_MANAGER_NONE); + + mgrctl_ctx* mc = &g_mc; + MANAGER_MUTEX_LOCK(&(mc->lock)); + + switch (event) { + case AUDIO_DEVICE_MANAGER_VOLUME : + set_volume(&(mc->base), val); + break; + case AUDIO_DEVICE_MANAGER_MUTE: + set_mute(&(mc->base), val); + break; + case AUDIO_DEVICE_MANAGER_PATH : + if (val <= AUDIO_DEVICE_SPEAKER) + set_outpath(&(mc->base), val); + else + set_inpath(&(mc->base), val); + break; + } + + MANAGER_MUTEX_UNLOCK(&(mc->lock)); + return 0; +} + +mgrctl_ctx * aud_mgr_ctx() +{ + mgrctl_ctx* mc = &g_mc; + return mc; +} + +int aud_mgr_init() +{ + mgrctl_ctx* mc = &g_mc; + memset(mc, 0, sizeof(*mc)); + + mc->current_outdev = AUDIO_DEVICE_SPEAKER; + mc->current_indev = AUDIO_DEVICE_MAINMIC; + mc->base.ops = &mgr_ops; + + if (MANAGER_MUTEX_INIT(&(mc->lock)) != 0) + return -1; + mc->is_initialize = 1; + return 0; +} + +int aud_mgr_deinit() +{ + mgrctl_ctx* mc = &g_mc; + mc->is_initialize = 0; + memset(mc, 0, sizeof(*mc)); + MANAGER_NUTEX_DESTROY(&(mc->lock)); + return 0; +} +#endif /* MANAGER_NULL */ diff --git a/platform/mcu/xr871/src/audio/audio_manager/audio_manager.mk b/platform/mcu/xr871/src/audio/audio_manager/audio_manager.mk new file mode 100644 index 0000000000..435f22b4b3 --- /dev/null +++ b/platform/mcu/xr871/src/audio/audio_manager/audio_manager.mk @@ -0,0 +1,8 @@ + + +NAME := audio_manager + +$(NAME)_TYPE := kernel +$(NAME)_SOURCES := \ + audio_manager.c + diff --git a/platform/mcu/xr871/src/audio/audio_pcm/audio_pcm.c b/platform/mcu/xr871/src/audio/audio_pcm/audio_pcm.c new file mode 100644 index 0000000000..8de0cad6f1 --- /dev/null +++ b/platform/mcu/xr871/src/audio/audio_pcm/audio_pcm.c @@ -0,0 +1,468 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include +#include +#include +#include "driver/chip/hal_codec.h" +#include "driver/chip/hal_i2s.h" +#include "driver/chip/hal_dmic.h" +#include "driver/chip/hal_i2c.h" +#include "audio/pcm/audio_pcm.h" +#include "audio/manager/audio_manager.h" +#include "./../../driver/chip/hal_os.h" + +#define Oops(x) do {printf("[PCM]"); printf x; } while (0) +#define AUDIO_OUT_DEVICE_DEFAULT AUDIO_DEVICE_HEADPHONE +#define AUDIO_IN_DEVICE_DEFAULT AUDIO_DEVICE_MAINMIC + +#ifndef PCM_ASSERT +#define PCM_WARN_MESSAGE(x) do {printf(x);} while(0) + +#define PCM_ASSERT(message, assertion) do { if(!(assertion)) { \ + PCM_WARN_MESSAGE(message); \ + return -1; \ + } \ + } while(0) +#endif + +struct play_priv { + struct pcm_config *config; + unsigned char *cache; + unsigned int length; + unsigned int trigger; +}; + +struct cap_priv { + struct pcm_config *config; +}; + +struct audio_priv { + struct play_priv play_priv; + struct cap_priv cap_priv; + HAL_Mutex play_lock; + HAL_Mutex write_lock; + HAL_Mutex cap_lock; +}; + +static struct audio_priv snd_pcm_priv; + +#define pcm_lock(n) HAL_MutexLock(&((snd_pcm_priv).n##_lock), OS_WAIT_FOREVER) +#define pcm_unlock(n) HAL_MutexUnlock(&((snd_pcm_priv).n##_lock)) +#define pcm_lock_init(n) HAL_MutexInit(&((snd_pcm_priv).n##_lock)) +#define pcm_lock_deinit(n) HAL_MutexDeinit(&((snd_pcm_priv).n##_lock)) + +#define PA_STABILITY_TIME 150 //ms + +void* pcm_zalloc(unsigned int size) +{ + void *p; + if ((p = malloc(size)) != NULL){ + memset(p,0,size); + return p; + } + return NULL; +} + +void pcm_free(void *p) +{ + if (p) { + free(p); + p = NULL; + } + return; +} + +unsigned int pcm_get_buffer_size(struct pcm_config *config) +{ + return config->period_count * config->period_size; +} + +unsigned int pcm_format_to_bits(enum pcm_format format) +{ + switch (format) { + case PCM_FORMAT_S32_LE: + case PCM_FORMAT_S24_LE: + return 32; + case PCM_FORMAT_S16_LE: + return 16; + default: + Oops(("invalid pcm format...\n")); + return 0; + }; +} + +unsigned int pcm_frames_to_bytes(struct pcm_config *config, unsigned int frames) +{ + return frames * config->channels * + (pcm_format_to_bits(config->format) >> 3); +} + +int snd_pcm_write(struct pcm_config *config, unsigned int card, void *data, unsigned int count) +{ + PCM_ASSERT("Invalid card.\n", (card == SOUND_CARD_EXTERNAL_AUDIOCODEC)); + int len = 0, ret = 0, buf_size = 0, size = 0; + uint8_t *data_ptr = NULL; + + if (pcm_lock(write) != 0) { + Oops(("Obtain write lock err.\n")); + return -1; + } + + buf_size = pcm_frames_to_bytes(config,pcm_get_buffer_size(config))/2;; + data_ptr = data; + size = count; + + struct play_priv *ppriv = &(snd_pcm_priv.play_priv); + if (ppriv->length != 0) { + len = buf_size - ppriv->length; + if (ppriv->cache == NULL) { + pcm_unlock(write); + return -1; + } + if (len > size) { + memcpy(ppriv->cache + ppriv->length, data_ptr, size); + ppriv->length += size; + pcm_unlock(write); + return count; + } else + memcpy(ppriv->cache + ppriv->length, data_ptr, len); + + HAL_I2S_Write_DMA((void *)ppriv->cache, buf_size); + ppriv->length = 0; + } + + data_ptr = data_ptr +len; + size -= len; + + ret = HAL_I2S_Write_DMA(data_ptr, size); + + if (ret > 0 && ret != size) { + data_ptr += ret; + ppriv->length = size - ret; + memcpy(ppriv->cache, data_ptr, ppriv->length); + }else if (ret == size) { + ppriv->length = 0; + } else { + ppriv->length = size; + memcpy(ppriv->cache, data_ptr, ppriv->length); + } + +#if 0 + + #include "fs/fatfs/ff.h" + FRESULT result; + FATFS fs; + FIL file; + memset(&fs, 0, sizeof(fs)); + unsigned int writenum = 0; + fs.drv = 1; + if ((result = f_mount(&fs, "0:/", 1)) != FR_OK) // = "0:" = "0:/" = "" + printf("failed to mount\n"); + else if ((result = f_open(&file, "0:/5.pcm", FA_OPEN_ALWAYS|FA_READ|FA_WRITE)) != FR_OK) + printf("[music file]failed to open,%s\n","0:/5.pcm"); + if ((result = f_write(&file, data, count, &writenum)) != FR_OK) + printf("write failed(%d).\n",result); + + f_close(&file); + +#endif + pcm_unlock(write); + return count; +} + +int snd_pcm_flush(struct pcm_config *config, unsigned int card) +{ + PCM_ASSERT("Invalid card.\n", (card == SOUND_CARD_EXTERNAL_AUDIOCODEC)); + + struct play_priv *ppriv = &(snd_pcm_priv.play_priv); + PCM_ASSERT("Cache is NULL.\n", (ppriv->cache != NULL)); + int buf_size = pcm_frames_to_bytes(config,pcm_get_buffer_size(config)); + + pcm_lock(write); + + buf_size = buf_size / 2; + if (ppriv->length != 0){ + memset(ppriv->cache + ppriv->length, 0, buf_size - ppriv->length); + HAL_I2S_Write_DMA((void *)ppriv->cache, buf_size); + ppriv->length = 0; + } + + int delay = 0; + memset(ppriv->cache, 0, buf_size); + for(delay = 0; delay < 2; delay ++) { + HAL_I2S_Write_DMA((void *)ppriv->cache, buf_size); + } + pcm_unlock(write); + + return 0; +} + +int snd_pcm_read(struct pcm_config *config, unsigned int card, void *data, unsigned int count) +{ + PCM_ASSERT("Invalid card.\n", (card < SOUND_CARD_NULL)); + void *buf = data; + int size = 0; + int buf_size = pcm_frames_to_bytes(config,pcm_get_buffer_size(config))/2;; + if (buf_size > count) + return -1; + size = (count / buf_size) * buf_size; + if (card == SOUND_CARD_EXTERNAL_AUDIOCODEC) + return HAL_I2S_Read_DMA(buf, size); + else if (card == SOUND_CARD_INTERNAL_DMIC) + return HAL_DMIC_Read_DMA(buf, size); + return -1; +} + +int snd_pcm_open(struct pcm_config *config, unsigned int card, unsigned int flags) +{ + PCM_ASSERT("Wrong card and flags param.\n", ((card == 1) && (PCM_OUT == flags)) != 1); + mgrctl_ctx *mgr_ctx = aud_mgr_ctx(); + if (card == AUDIO_CARD0) { + DATA_Param codec_data; + memset(&codec_data, 0, sizeof(codec_data)); + + MANAGER_MUTEX_LOCK(&(mgr_ctx->lock)); + if (mgr_ctx->is_initialize) + codec_data.audioDev = (PCM_OUT == flags) ? mgr_ctx->current_outdev : mgr_ctx->current_indev; + else + codec_data.audioDev = (PCM_OUT == flags) ? AUDIO_OUT_DEVICE_DEFAULT : AUDIO_IN_DEVICE_DEFAULT; + MANAGER_MUTEX_UNLOCK(&(mgr_ctx->lock)); + + codec_data.sampleRate = config->rate; + + if (HAL_CODEC_Open(&codec_data) != HAL_OK) { + Oops(("Codec open failed..\n")); + return -1; + } + + I2S_DataParam i2s_data; + memset(&i2s_data, 0, sizeof(i2s_data)); + i2s_data.direction = (PCM_OUT == flags) ? PLAYBACK : RECORD; + i2s_data.bufSize = pcm_frames_to_bytes(config,pcm_get_buffer_size(config)); + i2s_data.channels = config->channels; + + if (i2s_data.direction == PLAYBACK) { + if (pcm_lock(play) != 0) { + Oops(("obtain play lock err...\n")); + return -1; + } + struct play_priv *ppriv = &(snd_pcm_priv.play_priv); + ppriv->cache = pcm_zalloc(i2s_data.bufSize/2); + if (ppriv->cache == NULL) { + pcm_unlock(play); + Oops(("obtain play cache failed...\n")); + return -1; + } + ppriv->length = 0; + ppriv->config = config; + + } else { + if (pcm_lock(cap) != 0) { + Oops(("obtain cap lock err...\n")); + return -1; + } + + struct cap_priv *cpriv = &(snd_pcm_priv.cap_priv); + cpriv->config = config; + } + i2s_data.resolution = (config->format == PCM_FORMAT_S16_LE) ? I2S_SR16BIT : I2S_SR32BIT; + + switch (config->rate) { + case 48000: + i2s_data.sampleRate = I2S_SR48K; + break; + case 44100: + i2s_data.sampleRate = I2S_SR44K; + break; + case 8000: + i2s_data.sampleRate = I2S_SR8K; + break; + case 12000: + i2s_data.sampleRate = I2S_SR12K; + break; + case 16000: + i2s_data.sampleRate = I2S_SR16K; + break; + case 24000: + i2s_data.sampleRate = I2S_SR24K; + break; + case 32000: + i2s_data.sampleRate = I2S_SR32K; + break; + case 11025: + i2s_data.sampleRate = I2S_SR11K; + break; + case 22050: + i2s_data.sampleRate = I2S_SR22K; + break; + + default: + break; + } + + if (HAL_I2S_Open(&i2s_data) != HAL_OK) { + Oops(("I2S open failed..\n")); + return -1; + } + OS_MSleep(5); + if (i2s_data.direction == PLAYBACK) { + if (HAL_CODEC_MUTE_STATUS_Get() == 0) { + if (codec_data.audioDev == AUDIO_DEVICE_SPEAKER) + OS_MSleep(PA_STABILITY_TIME); + HAL_CODEC_Trigger(codec_data.audioDev, 1); + } + } + MANAGER_MUTEX_LOCK(&(mgr_ctx->lock)); + if (i2s_data.direction == PLAYBACK) + mgr_ctx->playback = 1; + else + mgr_ctx->record = 1; + MANAGER_MUTEX_UNLOCK(&(mgr_ctx->lock)); + + } else { + + DMIC_DataParam dmic_data; + memset(&dmic_data, 0, sizeof(dmic_data)); + if (pcm_lock(cap) != 0) { + Oops(("obtain cap lock err...\n")); + return -1; + } + switch (config->rate) { + case 48000: + dmic_data.sampleRate = DMIC_SR48KHZ; + break; + case 44100: + dmic_data.sampleRate = DMIC_SR44KHZ; + break; + case 24000: + dmic_data.sampleRate = DMIC_SR24KHZ; + break; + case 22050: + dmic_data.sampleRate = DMIC_SR22KHZ; + break; + case 12000: + dmic_data.sampleRate = DMIC_SR12KHZ; + break; + case 11025: + dmic_data.sampleRate = DMIC_SR11KHZ; + break; + case 32000: + dmic_data.sampleRate = DMIC_SR32KHZ; + break; + case 16000: + dmic_data.sampleRate = DMIC_SR16KHZ; + break; + case 8000: + dmic_data.sampleRate = DMIC_SR8KHZ; + break; + + default: + break; + } + dmic_data.bufSize = pcm_frames_to_bytes(config,pcm_get_buffer_size(config)); + dmic_data.channels = config->channels; + dmic_data.resolution = (config->format == PCM_FORMAT_S16_LE) ? DMIC_RES16BIT : DMIC_RES24BIT; + + if (HAL_DMIC_Open(&dmic_data) != HAL_OK) { + Oops(("Dmic open failed..\n")); + pcm_unlock(cap); + return -1; + } + + MANAGER_MUTEX_LOCK(&(mgr_ctx->lock)); + mgr_ctx->record = 1; + MANAGER_MUTEX_UNLOCK(&(mgr_ctx->lock)); + } + return 0; +} + +int snd_pcm_close(unsigned int card, unsigned int flags) +{ + PCM_ASSERT("Wrong card and flags param..\n", ((card == 1) && (PCM_OUT == flags)) != 1); + int dir = (PCM_OUT == flags) ? PLAYBACK : RECORD; + mgrctl_ctx *mgr_ctx = aud_mgr_ctx(); + if (card == AUDIO_CARD0) { + AUDIO_Device dev; + + MANAGER_MUTEX_LOCK(&(mgr_ctx->lock)); + if (mgr_ctx->is_initialize) + dev = mgr_ctx->current_outdev; + else + dev = AUDIO_OUT_DEVICE_DEFAULT; + MANAGER_MUTEX_UNLOCK(&(mgr_ctx->lock)); + + HAL_CODEC_Trigger(dev, 0); + HAL_CODEC_Close(dir); + HAL_I2S_Close(dir); + + if (PCM_OUT == flags) { + pcm_free(snd_pcm_priv.play_priv.cache); + memset(&(snd_pcm_priv.play_priv), 0, sizeof(struct play_priv)); + pcm_unlock(play); + + } else { + memset(&(snd_pcm_priv.cap_priv), 0, sizeof(struct cap_priv)); + pcm_unlock(cap); + } + + MANAGER_MUTEX_LOCK(&(mgr_ctx->lock)); + if (PCM_OUT == flags) + mgr_ctx->playback = 0; + else + mgr_ctx->record = 0; + MANAGER_MUTEX_UNLOCK(&(mgr_ctx->lock)); + } else { + MANAGER_MUTEX_LOCK(&(mgr_ctx->lock)); + mgr_ctx->record = 0; + MANAGER_MUTEX_UNLOCK(&(mgr_ctx->lock)); + HAL_DMIC_Close(); + memset(&(snd_pcm_priv.cap_priv), 0, sizeof(struct cap_priv)); + pcm_unlock(cap); + } + return 0; +} + +int snd_pcm_init() +{ + struct audio_priv* audio_priv = &snd_pcm_priv; + + memset(audio_priv, 0, sizeof(*audio_priv)); + pcm_lock_init(play); + pcm_lock_init(write); + pcm_lock_init(cap); + return 0; +} + +int snd_pcm_deinit() +{ + pcm_lock_deinit(play); + pcm_lock_deinit(write); + pcm_lock_deinit(cap); + return 0; +} diff --git a/platform/mcu/xr871/src/audio/audio_pcm/audio_pcm.mk b/platform/mcu/xr871/src/audio/audio_pcm/audio_pcm.mk new file mode 100644 index 0000000000..f289a9b845 --- /dev/null +++ b/platform/mcu/xr871/src/audio/audio_pcm/audio_pcm.mk @@ -0,0 +1,8 @@ + + +NAME := audio_pcm + +$(NAME)_TYPE := kernel +$(NAME)_SOURCES := \ + audio_pcm.c + diff --git a/platform/mcu/xr871/src/cedarx/Cdx2.0Plugin/include/AudioDec_Decode.h b/platform/mcu/xr871/src/cedarx/Cdx2.0Plugin/include/AudioDec_Decode.h new file mode 100644 index 0000000000..7ca0d83554 --- /dev/null +++ b/platform/mcu/xr871/src/cedarx/Cdx2.0Plugin/include/AudioDec_Decode.h @@ -0,0 +1,123 @@ +#ifndef AUDIODEC_DECODE_H +#define AUDIODEC_DECODE_H +#include +#include "unistd.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ +#define cdx_mutex_lock(x) pthread_mutex_lock(x) +#define cdx_mutex_unlock(x) pthread_mutex_unlock(x) + +typedef void AudioDecoderLib; + +typedef struct CedarAudioCodec +{ + const char *name; + struct __AudioDEC_AC320 *(*init)(void); + int (*exit)(struct __AudioDEC_AC320 *p); + int flag; +} CedarAudioCodec; + + +typedef struct InitCodecInfo +{ + const char *handle; + const char *name; + const char *init; + const char *exit; + int flag; + struct __AudioDEC_AC320 *(*decInit)(void); + int (*decExit)(struct __AudioDEC_AC320 *p); +} InitCodecInfo; + +typedef struct RaFormatInofStruct +{ + unsigned int ulSampleRate; + unsigned int ulActualRate; + unsigned short usBitsPerSample; + unsigned short usNumChannels; + unsigned short usAudioQuality; + unsigned short usFlavorIndex; + unsigned int ulBitsPerFrame; + unsigned int ulGranularity; + unsigned int ulOpaqueDataSize; + unsigned char* pOpaqueData; +} RaFormatInfoT; +/*********************************************************************************/ + +typedef enum INPUTBSFILLMODE +{ + BSFILL_MODE_UNKONE = 0, + BSFILL_MODE_NORMAL, + BSFILL_MODE_HDRWRAP, + BSFILL_MODE_BSWRAP, +}INPUTBSFILLMODE; + +typedef struct CedarInputBsManage +{ + INPUTBSFILLMODE mode; + unsigned char *pHoloStartAddr0; + int nHoloLen0; + unsigned char *pHoloStartAddr1; + int nHoloLen1; +}CedarInputBsManage; + +typedef struct InputStreamBuf +{ + CedarInputBsManage InputBsManage; + int CedarAbsPackHdrLen; + unsigned char CedarAbsPackHdr[16]; +}InputStreamBufT; + +typedef struct AudioDecoderContextStructLib +{ + pthread_mutex_t mutex_audiodec_thread; + int nAudioInfoFlags; + InputStreamBufT Streambuffer; + Ac320FileRead DecFileInfo; + com_internal pInternal; + + CedarAudioCodec *pCedarCodec; + void *libhandle; + AudioDEC_AC320 *pCedarAudioDec; +#ifdef CEDARX_SUPPORT_SOUNDTOUCH + SoundTouchAPI *pSTouchAPI; + STPrivData* STpriv_data; +#endif + const char *handlename; +}AudioDecoderContextLib; + + +int ParseRequestAudioBitstreamBuffer(AudioDecoderLib* pDecoder, + int nRequireSize, + unsigned char** ppBuf, + int* pBufSize, + unsigned char** ppRingBuf, + int* pRingBufSize, + int* nOffset); +int ParseUpdateAudioBitstreamData(AudioDecoderLib* pDecoder, + int nFilledLen, + int64_t nTimeStamp, + int nOffset); +int ParseAudioStreamDataSize(AudioDecoderLib* pDecoder); +void BitstreamQueryQuality(AudioDecoderLib* pDecoder, int* pValidPercent, int* vbv); +void ParseBitstreamSeekSync(AudioDecoderLib* pDecoder, int64_t nSeekTime, int nGetAudioInfoFlag); + +int InitializeAudioDecodeLib(AudioDecoderLib* pDecoder, + AudioStreamInfo* pAudioStreamInfo, + BsInFor *pBsInFor); +int DecodeAudioFrame(AudioDecoderLib* pDecoder, + char* ppBuf, + int* pBufSize); +int DestroyAudioDecodeLib(AudioDecoderLib* pDecoder); + +void SetAudiolibRawParam(AudioDecoderLib* pDecoder, int commond); + +AudioDecoderLib* CreateAudioDecodeLib(void); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif//AUDIODEC_DECODE_H diff --git a/platform/mcu/xr871/src/cedarx/Cdx2.0Plugin/include/CDX_Common.h b/platform/mcu/xr871/src/cedarx/Cdx2.0Plugin/include/CDX_Common.h new file mode 100644 index 0000000000..c7921e9490 --- /dev/null +++ b/platform/mcu/xr871/src/cedarx/Cdx2.0Plugin/include/CDX_Common.h @@ -0,0 +1,45 @@ +/******************************************************************************* +-- -- +-- CedarX Multimedia Framework -- +-- -- +-- the Multimedia Framework for Linux/Android System -- +-- -- +-- This software is confidential and proprietary and may be used -- +-- only as expressly authorized by a licensing agreement from -- +-- Softwinner Products. -- +-- -- +-- (C) COPYRIGHT 2011 SOFTWINNER PRODUCTS -- +-- ALL RIGHTS RESERVED -- +-- -- +-- The entire notice above must be reproduced -- +-- on all copies and should not be removed. -- +-- -- +*******************************************************************************/ +#ifndef CDX_Common_H_ +#define CDX_Common_H_ + +typedef enum CEDARX_AUDIO_CHANNEL_TYPE +{ + CEDARX_AUDIO_CHANNEL_STEREO = 0, + CEDARX_AUDIO_CHANNEL_LEFT , + CEDARX_AUDIO_CHANNEL_RIGHT, +} CEDARX_AUDIO_CHANNEL_TYPE; + +typedef enum CDX_COMP_PRIV_FLAGS +{ + CDX_COMP_PRIV_FLAGS_REINIT = 1, + CDX_COMP_PRIV_FLAGS_STREAMEOF = 2, +}CDX_COMP_PRIV_FLAGS; + +enum CEDARXAUDIOFLAGSENUM +{ + ADEC_DISABLE_AAC_PACKING = (1<<0), +}; + + +typedef enum CDX_DECODE_MODE { + CDX_DECODER_MODE_NORMAL = 0, + CDX_DECODER_MODE_RAWMUSIC, +}CDX_DECODE_MODE; + +#endif \ No newline at end of file diff --git a/platform/mcu/xr871/src/cedarx/Cdx2.0Plugin/include/CDX_Fileformat.h b/platform/mcu/xr871/src/cedarx/Cdx2.0Plugin/include/CDX_Fileformat.h new file mode 100644 index 0000000000..6a1d5ef39c --- /dev/null +++ b/platform/mcu/xr871/src/cedarx/Cdx2.0Plugin/include/CDX_Fileformat.h @@ -0,0 +1,107 @@ +#ifndef CDX_FILEFORMAT_H_ +#define CDX_FILEFORMAT_H_ + +typedef enum CDX_MEDIA_FILE_FORMAT { + CDX_MEDIA_FILE_FMT_UNSUPPORT = -1, + CDX_MEDIA_FILE_FMT_UNKOWN = 0, + CDX_MEDIA_FILE_FMT_AVI, + CDX_MEDIA_FILE_FMT_RM, + CDX_MEDIA_FILE_FMT_RMVB, + CDX_MEDIA_FILE_FMT_MP4, + CDX_MEDIA_FILE_FMT_M4V, + CDX_MEDIA_FILE_FMT_3GP, + CDX_MEDIA_FILE_FMT_MOV, + CDX_MEDIA_FILE_FMT_FLV, + CDX_MEDIA_FILE_FMT_MPG, + CDX_MEDIA_FILE_FMT_VOB, + CDX_MEDIA_FILE_FMT_MOD, + CDX_MEDIA_FILE_FMT_PMP, + CDX_MEDIA_FILE_FMT_WMV, + CDX_MEDIA_FILE_FMT_ASF, + CDX_MEDIA_FILE_FMT_MKV, + CDX_MEDIA_FILE_FMT_PSR, + CDX_MEDIA_FILE_FMT_RAM, + CDX_MEDIA_FILE_FMT_SCM, + CDX_MEDIA_FILE_FMT_OGM, + CDX_MEDIA_FILE_FMT_M4P, + CDX_MEDIA_FILE_FMT_M4B, + CDX_MEDIA_FILE_FMT_TP, + CDX_MEDIA_FILE_FMT_TPR, + CDX_MEDIA_FILE_FMT_TS, + CDX_MEDIA_FILE_FMT_PVA, + CDX_MEDIA_FILE_FMT_PSS, + CDX_MEDIA_FILE_FMT_MPE, + CDX_MEDIA_FILE_FMT_WV, + CDX_MEDIA_FILE_FMT_M2TS, + CDX_MEDIA_FILE_FMT_EVO, + CDX_MEDIA_FILE_FMT_RPM, + CDX_MEDIA_FILE_FMT_3GPP, + CDX_MEDIA_FILE_FMT_3G2, + CDX_MEDIA_FILE_FMT_3GP2, + CDX_MEDIA_FILE_FMT_QT, + CDX_MEDIA_FILE_FMT_WMP, + CDX_MEDIA_FILE_FMT_WM, + CDX_MEDIA_FILE_FMT_AMV, + CDX_MEDIA_FILE_FMT_DSM, + CDX_MEDIA_FILE_FMT_M1V, + CDX_MEDIA_FILE_FMT_M2V, + CDX_MEDIA_FILE_FMT_SMK, + CDX_MEDIA_FILE_FMT_BIK, + CDX_MEDIA_FILE_FMT_RAT, + CDX_MEDIA_FILE_FMT_VG2, + CDX_MEDIA_FILE_FMT_IVF, + CDX_MEDIA_FILE_FMT_VP6, + CDX_MEDIA_FILE_FMT_VP7, + CDX_MEDIA_FILE_FMT_D2V, + CDX_MEDIA_FILE_FMT_M2P, + CDX_MEDIA_FILE_FMT_VID, + CDX_MEDIA_FILE_FMT_PMP2, + CDX_MEDIA_FILE_FMT_MTS, + CDX_MEDIA_FILE_FMT_MP3, + CDX_MEDIA_FILE_FMT_WAV, + CDX_MEDIA_FILE_FMT_WMA, + CDX_MEDIA_FILE_FMT_APE, + CDX_MEDIA_FILE_FMT_FLAC, + CDX_MEDIA_FILE_FMT_OGG, + CDX_MEDIA_FILE_FMT_RA, + CDX_MEDIA_FILE_FMT_MP1, + CDX_MEDIA_FILE_FMT_MP2, + CDX_MEDIA_FILE_FMT_AAC, + CDX_MEDIA_FILE_FMT_AC3, + CDX_MEDIA_FILE_FMT_DTS, + CDX_MEDIA_FILE_FMT_AIF, + CDX_MEDIA_FILE_FMT_AIFF, + CDX_MEDIA_FILE_FMT_AIFC, + CDX_MEDIA_FILE_FMT_AMR, + CDX_MEDIA_FILE_FMT_MAC, + CDX_MEDIA_FILE_FMT_TTA, + CDX_MEDIA_FILE_FMT_M4A, + CDX_MEDIA_FILE_FMT_CDA, + CDX_MEDIA_FILE_FMT_AU, + CDX_MEDIA_FILE_FMT_ACC, + CDX_MEDIA_FILE_FMT_MIDI, + CDX_MEDIA_FILE_FMT_MID, + CDX_MEDIA_FILE_FMT_RMI, + CDX_MEDIA_FILE_FMT_MP5, + CDX_MEDIA_FILE_FMT_MPA, + CDX_MEDIA_FILE_FMT_MPGA, + CDX_MEDIA_FILE_FMT_ACT, + CDX_MEDIA_FILE_FMT_ATRC, + CDX_MEDIA_FILE_FMT_WEBM, + CDX_MEDIA_FILE_FMT_M3U, + CDX_MEDIA_FILE_FMT_AWTS, + CDX_MEDIA_FILE_FMT_WVM,//87 + /*for test raw data */ + CDX_MEDIA_FILE_FMT_RAW, + + CDX_MEDIA_FILE_FMT_AUDIO = (1<<16), + CDX_MEDIA_FILE_FMT_NETWORK = (2<<16), + CDX_MEDIA_FILE_FMT_NETWORK_RTSP = (4<<16), + CDX_MEDIA_FILE_FMT_IDXSUB = (8<<16), + CDX_MEDIA_FILE_FMT_NETWORK_SFT = (16<<16), + CDX_MEDIA_FILE_FMT_WIFI_DISPLAY = (32<<16), + CDX_MEDIA_FILE_FMT_STREAMINGSRC = (64<<16), + CDX_MEDIA_FILE_FMT_NETWORK_OTHERS = (128<<16), +} CDX_MEDIA_FILE_FORMAT; + +#endif /* CDX_FILEFORMAT_H_ */ diff --git a/platform/mcu/xr871/src/cedarx/Cdx2.0Plugin/include/GetAudio_format.h b/platform/mcu/xr871/src/cedarx/Cdx2.0Plugin/include/GetAudio_format.h new file mode 100644 index 0000000000..448c89ffc1 --- /dev/null +++ b/platform/mcu/xr871/src/cedarx/Cdx2.0Plugin/include/GetAudio_format.h @@ -0,0 +1,184 @@ +/****************************************************************************** +* file:GetAudio_format.h 2008-12-24 10:47:12 +* +*author lszhang +* +* +* +*******************************************************************************/ +#ifndef _GETAUFIO_FORMAT_H_ +#define _GETAUFIO_FORMAT_H_ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include +typedef enum __AW_AUDIO_FORMAT +{ + SW_AUDIO_FORMAT_UNKNOWN = 0, /* �޷�ʶ�� */ + + SW_AUDIO_FORMAT_AAC, + SW_AUDIO_FORMAT_AC3, + SW_AUDIO_FORMAT_APE, + SW_AUDIO_FORMAT_DTS, + SW_AUDIO_FORMAT_FLAC, + SW_AUDIO_FORMAT_MP3, + SW_AUDIO_FORMAT_OGG, + SW_AUDIO_FORMAT_RA, + SW_AUDIO_FORMAT_WAV, + SW_AUDIO_FORMAT_WMA, + SW_AUDIO_FORMAT_AMR, + SW_AUDIO_FORMAT_ATRC, + SW_AUDIO_FORMAT_MID, + + SW_AUDIO_FORMAT_ = -1 + +} __sw_audio_format_e; + +typedef enum +{ + IMG_FORMAT_BMP =0, + IMG_FORMAT_JPG, + IMG_FORMAT_GIF, + IMG_FORMAT_PNG, + IMG_FORMAT_UNSUPPORT = -1 +}__w_img_format_t; + +typedef enum __A_AUDIO_FONTTYPE +{ + A_AUDIO_FONTTYPE_ISOIEC8859_1 = 0, //ISO/IEC 8859-1 + A_AUDIO_FONTTYPE_UTF_16LE,// + A_AUDIO_FONTTYPE_UTF_16BE, + A_AUDIO_FONTTYPE_UTF_8,// + A_AUDIO_FONTTYPE_ISOIEC8859_2,// + A_AUDIO_FONTTYPE_ISOIEC8859_3,// + A_AUDIO_FONTTYPE_ISOIEC8859_4,// + A_AUDIO_FONTTYPE_ISOIEC8859_5,// + A_AUDIO_FONTTYPE_ISOIEC8859_6, + A_AUDIO_FONTTYPE_ISOIEC8859_7, + A_AUDIO_FONTTYPE_ISOIEC8859_8, + A_AUDIO_FONTTYPE_ISOIEC8859_9, + A_AUDIO_FONTTYPE_ISOIEC8859_10, + A_AUDIO_FONTTYPE_ISOIEC8859_11, + A_AUDIO_FONTTYPE_ISOIEC8859_12, + A_AUDIO_FONTTYPE_ISOIEC8859_13, + A_AUDIO_FONTTYPE_ISOIEC8859_14, + A_AUDIO_FONTTYPE_ISOIEC8859_15, + A_AUDIO_FONTTYPE_ISOIEC8859_16, + A_AUDIO_FONTTYPE_WINDOWS_1250, + A_AUDIO_FONTTYPE_WINDOWS_1251,// + A_AUDIO_FONTTYPE_WINDOWS_1252, + A_AUDIO_FONTTYPE_WINDOWS_1253, + A_AUDIO_FONTTYPE_WINDOWS_1254, + A_AUDIO_FONTTYPE_WINDOWS_1255, + A_AUDIO_FONTTYPE_WINDOWS_1256, + A_AUDIO_FONTTYPE_WINDOWS_1257, + A_AUDIO_FONTTYPE_WINDOWS_1258, + A_AUDIO_FONTTYPE_KOI8_R, + A_AUDIO_FONTTYPE_KOI8_U, + A_AUDIO_FONTTYPE_GB2312, + A_AUDIO_FONTTYPE_GBK, + A_AUDIO_FONTTYPE_BIG5, + + + A_AUDIO_FONTTYPE_ = -1 +}__a_audio_fonttype_e; + +typedef struct __ID3_IMAGE_INFO +{ + int length; //��ݳ��� + int FileLocation; //�ļ�ƫ��λ�� + __w_img_format_t img_format; //ͼƬ��ʽ + int pic_type; //picture type; + int img_high; //Ԥ����ͼƬ�߶� + int img_width; //Ԥ����ͼƬ��� + int otherdata; //Ԥ�� + +}__id3_image_info_t; + + +typedef struct __AUDIO_FILE_INFO +{ + unsigned int ulSampleRate; // ������ sample rate + unsigned int ulBitRate; // ������ bit rate��λ��BPS + unsigned int ulChannels; // ����� channel + unsigned int ulDuration; // ����ʱ�� duration ��λ��ms + unsigned int ulBitsSample; // �����λ�� sample 8/16/24/32 + unsigned int ulCharEncode; // 0:GB2312.1:UNICODE + + int ulAudio_name_sz; // ��Ƶ��ʽ˵�� + signed char *ulAudio_name; // mp3 /RealAudio Cook.sipo. / aac-lc.... + __a_audio_fonttype_e ulAudio_nameCharEncode; // + + int ulGenre_sz; // ���� + signed char *ulGenre; // pop soft... + __a_audio_fonttype_e ulGenreCharEncode; + + int ultitle_sz; // ������ + signed char *ultitle; + __a_audio_fonttype_e ultitleCharEncode; + + int ulauthor_sz; // �ݳ��� + signed char *ulauthor; + __a_audio_fonttype_e ulauthorCharEncode; + + __sw_audio_format_e ulFormat; + int ulAlbum_sz; // ר�� + signed char *ulAlbum; + __a_audio_fonttype_e ulAlbumCharEncode; + + int ulYear_sz; // ��Ʒ��� + signed char *ulYear; + __a_audio_fonttype_e ulYearCharEncode; + + int ulAPic_sz; // attached picture + __id3_image_info_t *ulAPic; + __a_audio_fonttype_e ulAPicCharEncode; + + int ulUslt_sz; // ��ͬ���ĸ��/�ı� ���� + signed char* ulUslt; // int ulFileLocation�� + __a_audio_fonttype_e ulUsltCharEncode; + + int ulSylt_sz; // ͬ���ĸ��/�ı� + signed char* ulSylt; // int ulFileLocation�� + __a_audio_fonttype_e ulSyltCharEncode; + + int ulbufflag; //0:���ļ����з����������buf�������ݽ��з��� + int ulbuf_length; //buf length; + char* ulbuf; //����buf��������. + int data[3]; + int64_t offset; //audio data ļеƫid3Ҳaudio dataΪ˽һļijaudio + int64_t length; //audio data lenthЧaudioݵijȡԸֵ0˼Ч + int64_t readlen; //ǰȡٸЧaudio ݣ൱ļƫλüȥaudio dataļеƫ + + signed char *ulBSINFO; // temporary buffer of read data + int InforBufLeftLength; + signed char *InforBuf; // ���ڴ洢 ulAudio_name ultitle ulauthor����Ϣ��buffer + +}audio_file_info_t; + + +int GetAudioFormat(const char *pFilename,int *A_Audio_Format);//return 1 :succed 0 :fail +int GetAudioInfo(const char *pFilename, audio_file_info_t *AIF); //return 1 :succed 0 :fail +/* +function :GetAudioFormatData +˵ļǰֽڿжļ͡ +˵ptr ļ׵ַ + buflenݳȡ + A_Audio_Formatļļʽ +ֵ1 :succed 0 :fail +*/ +//simple format from first 16bytes +int GetAudioFormatData(unsigned char *ptr,int buflen,int *A_Audio_Format);//return 1 :succed 0 :fail + +int GetAudioDataInfo(const char *pFilename, audio_file_info_t *AIF,signed char* buf,int datalen); //return 1 :succed 0 :fail +int GetAudioFileInfo(FILE *Bitstream, audio_file_info_t *AIF); //return 1 :succed 0 :fail +int GetAudioFileInfoOffset(FILE *Bitstream,int64_t offset,int64_t length,audio_file_info_t *AIF); //return 1 :succed 0 :fail +#ifdef __cplusplus +} +#endif + +#endif //_GETAUFIO_FORMAT_H_ + diff --git a/platform/mcu/xr871/src/cedarx/Cdx2.0Plugin/include/PostProcessCom.h b/platform/mcu/xr871/src/cedarx/Cdx2.0Plugin/include/PostProcessCom.h new file mode 100644 index 0000000000..662ad24e9b --- /dev/null +++ b/platform/mcu/xr871/src/cedarx/Cdx2.0Plugin/include/PostProcessCom.h @@ -0,0 +1,39 @@ + +#ifndef _POST_PROCESS_COM_H_ +#define _POST_PROCESS_COM_H_ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#define MaxSpecGroup (10) + +//Ϊ1024ٲţҪĵַͬ +typedef struct __PostProcessSt +{ + short fadeinoutflag; //0:normal 1:fade in 2:fade out + short VPS; //-40 - 100, ٶΪ: (1+vps%),ԱٷΧԭʼٶȵ0.6~2 + short spectrumflag; //0 : disable 1:enable + short spectrumOutNum;//Ƶ + short SpectrumOutval[MaxSpecGroup][32];//ΧΪ 0-20melis10θ߶ʾ32ƵΡ1024һƵף32ƵΣ + short UserEQ[11]; //0-10 + int channal; //1:mono 2:stereo + int samplerate; + + short *InputPcmBuf; + int InputPcmLen;//bufferlenth + + short *OutputPcmBuf; + int OutputPcmBufTotLen;//outputpcmbuf samplebufferlenth 2channels OutputPcmBufTotLen + int OutputPcmLen; + +}PostProcessSt; + +extern int AudioEQdo_auPostProc(PostProcessSt *PostProInfo); //0:fail 1:succed + +#ifdef __cplusplus +} +#endif + +#endif //_POST_PROCESS_COM_H_ diff --git a/platform/mcu/xr871/src/cedarx/Cdx2.0Plugin/include/STTypes.h b/platform/mcu/xr871/src/cedarx/Cdx2.0Plugin/include/STTypes.h new file mode 100644 index 0000000000..fce5eabeac --- /dev/null +++ b/platform/mcu/xr871/src/cedarx/Cdx2.0Plugin/include/STTypes.h @@ -0,0 +1,152 @@ +//////////////////////////////////////////////////////////////////////////////// +/// +/// Common type definitions for SoundTouch audio processing library. +/// +/// Author : Copyright (c) Olli Parviainen +/// Author e-mail : oparviai 'at' iki.fi +/// SoundTouch WWW: http://www.surina.net/soundtouch +/// +//////////////////////////////////////////////////////////////////////////////// +// +// Last changed : $Date: 2011-07-16 11:45:37 +0300 (Sat, 16 Jul 2011) $ +// File revision : $Revision: 3 $ +// +// $Id: STTypes.h 119 2011-07-16 08:45:37Z oparviai $ +// +//////////////////////////////////////////////////////////////////////////////// +// +// License : +// +// SoundTouch audio processing library +// Copyright (c) Olli Parviainen +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +//////////////////////////////////////////////////////////////////////////////// + +#ifndef STTypes_H +#define STTypes_H + +#ifdef __cplusplus +extern "C" { +#endif + +typedef unsigned int uint; +typedef unsigned long ulong; + +#ifdef __GNUC__ + // In GCC, include soundtouch_config.h made by config scritps + //#include "soundtouch_config.h" +#endif + +#ifndef _WINDEF_ + // if these aren't defined already by Windows headers, define now + + typedef int BOOL; + + #define FALSE 0 + #define TRUE 1 + +#endif // _WINDEF_ + + +//namespace soundtouch +//{ + /// Activate these undef's to overrule the possible sampletype + /// setting inherited from some other header file: + //#undef SOUNDTOUCH_INTEGER_SAMPLES + //#undef SOUNDTOUCH_FLOAT_SAMPLES + + #if !(SOUNDTOUCH_INTEGER_SAMPLES || SOUNDTOUCH_FLOAT_SAMPLES) + + /// Choose either 32bit floating point or 16bit integer sampletype + /// by choosing one of the following defines, unless this selection + /// has already been done in some other file. + //// + /// Notes: + /// - In Windows environment, choose the sample format with the + /// following defines. + /// - In GNU environment, the floating point samples are used by + /// default, but integer samples can be chosen by giving the + /// following switch to the configure script: + /// ./configure --enable-integer-samples + /// However, if you still prefer to select the sample format here + /// also in GNU environment, then please #undef the INTEGER_SAMPLE + /// and FLOAT_SAMPLE defines first as in comments above. + #define SOUNDTOUCH_INTEGER_SAMPLES 1 //< 16bit integer samples + //#define SOUNDTOUCH_FLOAT_SAMPLES 1 //< 32bit float samples + + #endif + + #if (WIN32 || __i386__ || __x86_64__) + /// Define this to allow X86-specific assembler/intrinsic optimizations. + /// Notice that library contains also usual C++ versions of each of these + /// these routines, so if you're having difficulties getting the optimized + /// routines compiled for whatever reason, you may disable these optimizations + /// to make the library compile. + + #define SOUNDTOUCH_ALLOW_X86_OPTIMIZATIONS 1 + + #endif + + // If defined, allows the SIMD-optimized routines to take minor shortcuts + // for improved performance. Undefine to require faithfully similar SIMD + // calculations as in normal C implementation. + #define SOUNDTOUCH_ALLOW_NONEXACT_SIMD_OPTIMIZATION 1 + + + #ifdef SOUNDTOUCH_INTEGER_SAMPLES + // 16bit integer sample type + typedef short SAMPLETYPE; + // data type for sample accumulation: Use 32bit integer to prevent overflows + typedef long LONG_SAMPLETYPE; + + #ifdef SOUNDTOUCH_FLOAT_SAMPLES + // check that only one sample type is defined + #error "conflicting sample types defined" + #endif // SOUNDTOUCH_FLOAT_SAMPLES + + #ifdef SOUNDTOUCH_ALLOW_X86_OPTIMIZATIONS + // Allow MMX optimizations + #define SOUNDTOUCH_ALLOW_MMX 1 + #endif + + #else + + // floating point samples + typedef float SAMPLETYPE; + // data type for sample accumulation: Use double to utilize full precision. + typedef double LONG_SAMPLETYPE; + + #ifdef SOUNDTOUCH_ALLOW_X86_OPTIMIZATIONS + // Allow SSE optimizations + #define SOUNDTOUCH_ALLOW_SSE 1 + #endif + + #endif // SOUNDTOUCH_INTEGER_SAMPLES +//}; + + +// When this #define is active, eliminates a clicking sound when the "rate" or "pitch" +// parameter setting crosses from value <1 to >=1 or vice versa during processing. +// Default is off as such crossover is untypical case and involves a slight sound +// quality compromise. +//#define SOUNDTOUCH_PREVENT_CLICK_AT_RATE_CROSSOVER 1 +#ifdef __cplusplus +} + +#endif + +#endif diff --git a/platform/mcu/xr871/src/cedarx/Cdx2.0Plugin/include/TouchToStretch.h b/platform/mcu/xr871/src/cedarx/Cdx2.0Plugin/include/TouchToStretch.h new file mode 100644 index 0000000000..da350a3f34 --- /dev/null +++ b/platform/mcu/xr871/src/cedarx/Cdx2.0Plugin/include/TouchToStretch.h @@ -0,0 +1,45 @@ + +#ifndef TOUCH_TO_STRETCH_H +#define TOUCH_TO_STRETCH_H + #ifdef __cplusplus + extern "C" { + #endif +#include "STTypes.h" + +typedef void STPrivData; + +typedef struct RunParameter_s +{ + int VoiceChangeFlag; + int sample_rate; + int channel_number; + float tempoDelta;//'t=xx'[-60.0 60.0] + float pitchDelta;//'p=xx'[-95.0 5000.0] + float rateDelta;//'r=xx'[-95.0 5000.0] + int quick;//'q','-quick' + int noAntiAlias;//'n','-naa' + float goalBPM;//'b','bpm=xx' + int detectBPM;//'b,'bpm=xx' + int speech;//'s','-speech' +}RunParameters; + +typedef enum SetParam_{ + ALL, + TEMPO, + PITCH, + VPS, +}SetParam; +void RunParameters_RunParameters(RunParameters *params); +void RunParameters_checkLimits(RunParameters *params); +void setupSoundTouch(STPrivData* priv_data, const RunParameters *params, SetParam para); +//void processSoundTouch( FILE *inFile, FILE *outFile, const RunParameters *params); +int processSoundTouch( SAMPLETYPE *inbufer, SAMPLETYPE *outbuffer, int nSamples,STPrivData* priv_data); +void* SouchTouchInit(); +void SouchTouchDestroy(STPrivData* priv_data); + + + #ifdef __cplusplus + } + #endif +#endif//TOUCH_TO_STRETCH_H + diff --git a/platform/mcu/xr871/src/cedarx/Cdx2.0Plugin/include/ad_cedarlib_com.h b/platform/mcu/xr871/src/cedarx/Cdx2.0Plugin/include/ad_cedarlib_com.h new file mode 100644 index 0000000000..40c3a058b5 --- /dev/null +++ b/platform/mcu/xr871/src/cedarx/Cdx2.0Plugin/include/ad_cedarlib_com.h @@ -0,0 +1,142 @@ +#ifndef __BsInfoType__ +#define __BsInfoType__ +#include "adecoder.h" + +typedef struct __AFRAME_INFO +{ + unsigned char *startAddr; + unsigned int ptsValid;//0:锟斤拷前时锟斤拷锟斤拷锟叫э拷锟�锟斤拷锟斤拷前时锟斤拷锟斤拷锟叫� + int64_t pts; + unsigned int len; + unsigned int readlen; +}aframe_info_t; + +typedef struct __ASTREAM_FIFO +{ + aframe_info_t *inFrames; + unsigned int rdIdx;// + unsigned int wtIdx;// + int ValidchunkCnt;//锟斤拷效锟斤拷packet锟斤拷目//0:锟斤拷前帧没锟斤拷锟斤拷荩锟�:锟斤拷前帧锟斤拷锟斤拷锟� unsigned int maxchunkNum; + int64_t NowPTSTime; + int64_t dLastValidPTS; + unsigned int maxchunkNum; +}astream_fifo_t; + + +typedef struct __Ac320FileRead +{ + unsigned int FileLength; //锟侥硷拷锟杰筹拷锟斤拷 + unsigned int FileReadingOpsition; //锟侥硷拷锟铰达拷要锟斤拷锟斤拷位锟矫碉拷偏锟斤拷 + unsigned int FileWritingOpsition; //buffer锟铰达拷要写锟斤拷位锟矫碉拷偏锟斤拷 + unsigned int need_offset; + unsigned short updataoffsetflag; + unsigned short BigENDFlag; //1 big 0 little + unsigned char *bufStart; //buffer锟阶碉拷址 + int BufToTalLen; //锟杰筹拷锟斤拷 + + unsigned char *bufReadingPtr; //锟斤拷锟节讹拷锟斤拷指锟斤拷 + int BufLen; //锟斤拷效锟斤拷莩锟斤拷锟� + unsigned char *bufWritingPtr; //锟斤拷锟节讹拷锟斤拷指锟斤拷 + int BufValideLen; //锟斤拷锟洁长锟斤拷 + //unsigned int ReadingOffset; + astream_fifo_t frmFifo; + //add for android + void* tmpGlobalAudioDecData; + +}Ac320FileRead; + +#define AUD_TAG_INF_SIZE 512 +typedef struct com_internal_struct +{ + //0x88 + unsigned int *pdecInfoSet; + unsigned int *pBackupBufferPoint; + unsigned int ulBitstreamAddPtr; + unsigned int ulDspVersionNum; + unsigned int ulPlayStartTime; + unsigned int ulPlayEndTime; + unsigned int ulBreakSentenceEnergy; + unsigned int ulBreakSentenceTime; + unsigned int ulFileSumData; + unsigned int ulForwardBackSilenceTime; + unsigned int ulForwardBackPlayTime; + unsigned int *SptrumoutAdd; + unsigned int *UserEqinputAdd; + + unsigned short ulBackupLength; + + unsigned char ulNormalPlay; //=ulFadeInOutFinish + unsigned char ulDecComm; + unsigned char ulFadeinFlag; + //0x8b + unsigned char ulFadeInOutFinish; + //0x89 + unsigned int ulTotalTimeMS; + + unsigned int ulAverageBit; + int /*unsigned short*/ ulSampleRate; //?why not this data is fact data; + //0x8a + unsigned char ulExitMean; + unsigned int ulDebugInfo; + //0x60 + unsigned int ulNowTimeMS; + unsigned int ulNowTimeMSAPoint; + unsigned int ulBitrate; + unsigned char ulMode; + unsigned char ulFFREVDoing; + //0x70 + signed char ulVpsSet; + unsigned int/*char*/ ulFFREVStep; + unsigned char ulFFREVFlag; + unsigned char ulABSet; + unsigned char ulEQFlag; + unsigned short ulUserEq[10]; + unsigned char ulspectrum; + unsigned short Hmspetrum;//1-8 +// unsigned short ulspectrumval[8][32]; //reducted for saving memory + unsigned char ulChannelControl; + unsigned char ulVolumeSet; + //0x61 a sentence end + //0x62 dsp mips limit + //0x63 dsp alive minimum time + //0x40 decoder need new bitstream to decode + unsigned int ulReadBitstreamLen; + int ulSkipPageLength; + unsigned char urFadeOutFlag; + unsigned char urFileFlag; + unsigned int urTrueReadLen; + unsigned int ulFilecurrentpage; + //0x41 + unsigned char ulIndex; + unsigned int ulDestinationAdd; + //0x42 + unsigned char ulIndex1; + unsigned int ulDestinationAdd1; + //0x43 + unsigned int bWithVideo; + unsigned int ulDestinationAdd2; + //0x21 + unsigned int *ulBufferAddress; + unsigned int ulBufferLength; + + unsigned short NeedSetPlayTimeFlag; + unsigned int framecount; + + // unsigned char AudTagInfo[AUD_TAG_INF_SIZE]; //reducted for saving memory + +} com_internal; + +typedef struct __AudioDEC_AC320 +{ + Ac320FileRead *FileReadInfo; + BsInFor *BsInformation; + com_internal *DecoderCommand; + int Decinitedflag; + int (*DecInit)(struct __AudioDEC_AC320 *p); + int (*DecFrame)(struct __AudioDEC_AC320 *p,char *OutBuffer,int *OutBuffLen); + int (*DecExit)(struct __AudioDEC_AC320 *p); + +}AudioDEC_AC320; + + +#endif diff --git a/platform/mcu/xr871/src/cedarx/Cdx2.0Plugin/include/adecoder.h b/platform/mcu/xr871/src/cedarx/Cdx2.0Plugin/include/adecoder.h new file mode 100644 index 0000000000..214207b5d9 --- /dev/null +++ b/platform/mcu/xr871/src/cedarx/Cdx2.0Plugin/include/adecoder.h @@ -0,0 +1,438 @@ +/* +* Copyright (c) 2008-2016 Allwinner Technology Co. Ltd. +* All rights reserved. +* +* File : adecoder.h +* Description : +* History : +* Author : gvc-al2 +* Date : long long ago +*/ + +#ifndef ADECODER_H +#define ADECODER_H + +#include "unistd.h" +#include + +#define OS_LINUX +//#include +//typedef signed long long int64_t; +//typedef int int32_t; + +#ifdef __cplusplus +extern "C" { +#endif + +#define AUDIO_BITSTREAM_BUFFER_SIZE (1024 * 6)//(1024*4096) +#define AUDIO_BITSTREAM_BUFFER_MAX_FRAME_NUM (6)//(4096) +#define AUDIO_PCM_BUFFER_SIZE (16 * 1024)//(512*1024) + +typedef enum __AUDIO_DEC_RESULT +{ + ERR_AUDIO_DEC_EXIT = -4, // exit + ERR_AUDIO_DEC_ENDINGCHKFAIL = -3, //big ending or little ending check failed + ERR_AUDIO_DEC_ABSEND = -2, //audio bitstream decode end + ERR_AUDIO_DEC_ONEFRMFAIL = -1, //decode one frame failed, can try again + + ERR_AUDIO_DEC_NONE = 0, //decode successed, no error + ERR_AUDIO_DEC_FFREVRETURN = 1, //return from fast-forward or fast-reverse + ERR_AUDIO_DEC_RETAPOINT = 2, //return from A point under A/B play mode + ERR_AUDIO_DEC_RETTAG = 3, //return from the first frame of tag play + ERR_AUDIO_DEC_VIDEOJUMP = 4, //0X88 maybe return 4 or 0 + ERR_AUDIO_DEC_NO_BITSTREAM = 5, //No enough bitstream ,try again + ERR_AUDIO_DEC_ + +} __audio_dec_result_t; + +enum EAUDIOCODECFORMAT +{ + AUDIO_CODEC_FORMAT_UNKNOWN = 0, + AUDIO_CODEC_FORMAT_MP1, + AUDIO_CODEC_FORMAT_MP2, + AUDIO_CODEC_FORMAT_MP3, + AUDIO_CODEC_FORMAT_MPEG_AAC_LC, + AUDIO_CODEC_FORMAT_AC3, //* not supported. + AUDIO_CODEC_FORMAT_DTS, //* not supported. + AUDIO_CODEC_FORMAT_LPCM_V, + AUDIO_CODEC_FORMAT_LPCM_A, + AUDIO_CODEC_FORMAT_ADPCM, + AUDIO_CODEC_FORMAT_PCM, + AUDIO_CODEC_FORMAT_WMA_STANDARD, + AUDIO_CODEC_FORMAT_FLAC, + AUDIO_CODEC_FORMAT_APE, + AUDIO_CODEC_FORMAT_OGG, + AUDIO_CODEC_FORMAT_RAAC, + AUDIO_CODEC_FORMAT_COOK, + AUDIO_CODEC_FORMAT_SIPR, + AUDIO_CODEC_FORMAT_ATRC, + AUDIO_CODEC_FORMAT_AMR, + AUDIO_CODEC_FORMAT_RA, + AUDIO_CODEC_FORMAT_PPCM, //* not supported. + AUDIO_CODEC_FORMAT_WMA_LOSS, //* not supported. + AUDIO_CODEC_FORMAT_WMA_PRO, //* not supported. + AUDIO_CODEC_FORMAT_MP3_PRO, //* not supported. + AUDIO_CODEC_FORMAT_ALAC, + AUDIO_CODEC_FORMAT_G729, + AUDIO_CODEC_FORMAT_DSD, + AUDIO_CODEC_FORMAT_OPUS, + AUDIO_CODEC_FORMAT_MLP = AUDIO_CODEC_FORMAT_AC3, + AUDIO_CODEC_FORMAT_ = -1, +}; + +//**************************************************************************// +//* Below are definition for sub codec types. +//**************************************************************************// +#define ABS_EDIAN_FLAG_MASK ((unsigned int)(1<<16)) +#define ABS_EDIAN_FLAG_BIG ((unsigned int)(1<<16)) + +/* Windows WAVE File Encoding Tags */ +#define WAVE_FORMAT_UNKNOWN 0x0000 /* Unknown Format */ +#define WAVE_FORMAT_PCM 0x0001 /* PCM */ +#define WAVE_FORMAT_ADPCM 0x0002 /* Microsoft ADPCM Format */ +#define WAVE_FORMAT_IEEE_FLOAT 0x0003 /* IEEE Float */ +#define WAVE_FORMAT_VSELP 0x0004 /* Compaq Computer's VSELP */ +#define WAVE_FORMAT_IBM_CVSD 0x0005 /* IBM CVSD */ +#define WAVE_FORMAT_ALAW 0x0006 /* ALAW */ +#define WAVE_FORMAT_MULAW 0x0007 /* MULAW */ +#define WAVE_FORMAT_DTS 0x0008 /* Microsoft Corporation */ +#define WAVE_FORMAT_DRM 0x0009 /* Microsoft Corporation */ +#define WAVE_FORMAT_WMAVOICE9 0x000A /* Microsoft Corporation */ +#define WAVE_FORMAT_WMAVOICE10 0x000B /* Microsoft Corporation */ +#define WAVE_FORMAT_OKI_ADPCM 0x0010 /* OKI ADPCM */ +#define WAVE_FORMAT_DVI_ADPCM 0x0011 /* Intel's DVI ADPCM */ +#define WAVE_FORMAT_IMA_ADPCM (WAVE_FORMAT_DVI_ADPCM) /* Intel Corporation */ +#define WAVE_FORMAT_MEDIASPACE_ADPCM 0x0012 /* Videologic's MediaSpace ADPCM*/ +#define WAVE_FORMAT_SIERRA_ADPCM 0x0013 /* Sierra ADPCM */ +#define WAVE_FORMAT_G723_ADPCM 0x0014 /* G.723 ADPCM */ +#define WAVE_FORMAT_DIGISTD 0x0015 /* DSP Solution's DIGISTD */ +#define WAVE_FORMAT_DIGIFIX 0x0016 /* DSP Solution's DIGIFIX */ +#define WAVE_FORMAT_DIALOGIC_OKI_ADPCM 0x0017 /* Dialogic OKI ADPCM */ +#define WAVE_FORMAT_MEDIAVISION_ADPCM 0x0018 /* MediaVision ADPCM */ +#define WAVE_FORMAT_CU_CODEC 0x0019 /* HP CU */ +#define WAVE_FORMAT_YAMAHA_ADPCM 0x0020 /* Yamaha ADPCM */ +#define WAVE_FORMAT_SONARC 0x0021 /* Speech Compression's Sonarc */ +#define WAVE_FORMAT_TRUESPEECH 0x0022 /* DSP Group's True Speech */ +#define WAVE_FORMAT_ECHOSC1 0x0023 /* Echo Speech's EchoSC1 */ +#define WAVE_FORMAT_AUDIOFILE_AF36 0x0024 /* Audiofile AF36 */ +#define WAVE_FORMAT_APTX 0x0025 /* APTX */ +#define WAVE_FORMAT_AUDIOFILE_AF10 0x0026 /* AudioFile AF10 */ +#define WAVE_FORMAT_PROSODY_1612 0x0027 /* Prosody 1612 */ +#define WAVE_FORMAT_LRC 0x0028 /* LRC */ +#define WAVE_FORMAT_AC2 0x0030 /* Dolby AC2 */ +#define WAVE_FORMAT_GSM610 0x0031 /* GSM610 */ +#define WAVE_FORMAT_MSNAUDIO 0x0032 /* MSNAudio */ +#define WAVE_FORMAT_ANTEX_ADPCME 0x0033 /* Antex ADPCME */ +#define WAVE_FORMAT_CONTROL_RES_VQLPC 0x0034 /* Control Res VQLPC */ +#define WAVE_FORMAT_DIGIREAL 0x0035 /* Digireal */ +#define WAVE_FORMAT_DIGIADPCM 0x0036 /* DigiADPCM */ +#define WAVE_FORMAT_CONTROL_RES_CR10 0x0037 /* Control Res CR10 */ +#define WAVE_FORMAT_VBXADPCM 0x0038 /* NMS VBXADPCM */ +#define WAVE_FORMAT_ROLAND_RDAC 0x0039 /* Roland RDAC */ +#define WAVE_FORMAT_ECHOSC3 0x003A /* EchoSC3 */ +#define WAVE_FORMAT_ROCKWELL_ADPCM 0x003B /* Rockwell ADPCM */ +#define WAVE_FORMAT_ROCKWELL_DIGITALK 0x003C /* Rockwell Digit LK */ +#define WAVE_FORMAT_XEBEC 0x003D /* Xebec */ +#define WAVE_FORMAT_G721_ADPCM 0x0040 /* Antex Electronics G.721 */ +#define WAVE_FORMAT_G728_CELP 0x0041 /* G.728 CELP */ +#define WAVE_FORMAT_MSG723 0x0042 /* MSG723 */ +#define WAVE_FORMAT_MPEG 0x0050 /* MPEG Layer 1,2 */ +#define WAVE_FORMAT_RT24 0x0051 /* RT24 */ +#define WAVE_FORMAT_PAC 0x0051 /* PAC */ +#define WAVE_FORMAT_MPEGLAYER3 0x0055 /* MPEG Layer 3 */ +//#define WAVE_FORMAT_CIRRUS 0x0059 /* Cirrus */ +#define WAVE_FORMAT_LUCENT_G723 0x0059 /* Lucent Technologies */ +#define WAVE_FORMAT_CIRRUS 0x0060 /* Cirrus Logic */ +#define WAVE_FORMAT_ESPCM 0x0061 /* ESPCM */ +#define WAVE_FORMAT_VOXWARE 0x0062 /* Voxware (obsolete) */ +#define WAVE_FORMAT_CANOPUS_ATRAC 0x0063 /* Canopus Atrac */ +#define WAVE_FORMAT_G726_ADPCM 0x0064 /* G.726 ADPCM */ +#define WAVE_FORMAT_G722_ADPCM 0x0065 /* G.722 ADPCM */ +#define WAVE_FORMAT_DSAT 0x0066 /* DSAT */ +#define WAVE_FORMAT_DSAT_DISPLAY 0x0067 /* DSAT Display */ +#define WAVE_FORMAT_VOXWARE_BYTE_ALIGNED 0x0069 /* Voxware Byte Aligned (obsolete) */ +#define WAVE_FORMAT_VOXWARE_AC8 0x0070 /* Voxware AC8 (obsolete) */ +#define WAVE_FORMAT_VOXWARE_AC10 0x0071 /* Voxware AC10 (obsolete) */ +#define WAVE_FORMAT_VOXWARE_AC16 0x0072 /* Voxware AC16 (obsolete) */ +#define WAVE_FORMAT_VOXWARE_AC20 0x0073 /* Voxware AC20 (obsolete) */ +#define WAVE_FORMAT_VOXWARE_RT24 0x0074 /* Voxware MetaVoice (obsolete) */ +#define WAVE_FORMAT_VOXWARE_RT29 0x0075 /* Voxware MetaSound (obsolete) */ +#define WAVE_FORMAT_VOXWARE_RT29HW 0x0076 /* Voxware RT29HW (obsolete) */ +#define WAVE_FORMAT_VOXWARE_VR12 0x0077 /* Voxware VR12 (obsolete) */ +#define WAVE_FORMAT_VOXWARE_VR18 0x0078 /* Voxware VR18 (obsolete) */ +#define WAVE_FORMAT_VOXWARE_TQ40 0x0079 /* Voxware TQ40 (obsolete) */ +#define WAVE_FORMAT_SOFTSOUND 0x0080 /* Softsound */ +#define WAVE_FORMAT_VOXWARE_TQ60 0x0081 /* Voxware TQ60 (obsolete) */ +#define WAVE_FORMAT_MSRT24 0x0082 /* MSRT24 */ +#define WAVE_FORMAT_G729A 0x0083 /* G.729A */ +#define WAVE_FORMAT_MVI_MV12 0x0084 /* MVI MV12 */ +#define WAVE_FORMAT_DF_G726 0x0085 /* DF G.726 */ +#define WAVE_FORMAT_DF_GSM610 0x0086 /* DF GSM610 */ +#define WAVE_FORMAT_ISIAUDIO 0x0088 /* ISIAudio */ +#define WAVE_FORMAT_ONLIVE 0x0089 /* Onlive */ +#define WAVE_FORMAT_SBC24 0x0091 /* SBC24 */ +#define WAVE_FORMAT_DOLBY_AC3_SPDIF 0x0092 /* Dolby AC3 SPDIF */ +#define WAVE_FORMAT_MEDIASONIC_G723 0x0093 /* MediaSonic */ +#define WAVE_FORMAT_PROSODY_8KBPS 0x0094 /* Aculab plc */ +#define WAVE_FORMAT_ZYXEL_ADPCM 0x0097 /* ZyXEL ADPCM */ +#define WAVE_FORMAT_PHILIPS_LPCBB 0x0098 /* Philips LPCBB */ +#define WAVE_FORMAT_PACKED 0x0099 /* Packed */ +#define WAVE_FORMAT_MALDEN_PHONYTALK 0x00A0 /* Malden Electronics Ltd. */ +#define WAVE_FORMAT_RHETOREX_ADPCM 0x0100 /* Rhetorex ADPCM */ +#define WAVE_FORMAT_IRAT 0x0101 /* BeCubed Software's IRAT */ +#define WAVE_FORMAT_VIVO_G723 0x0111 /* Vivo G.723 */ +#define WAVE_FORMAT_VIVO_SIREN 0x0112 /* Vivo Siren */ +#define WAVE_FORMAT_DIGITAL_G723 0x0123 /* Digital G.723 */ +#define WAVE_FORMAT_SANYO_LD_ADPCM 0x0125 /* Sanyo Electric Co., Ltd. */ +#define WAVE_FORMAT_SIPROLAB_ACEPLNET 0x0130 /* Sipro Lab Telecom Inc. */ +#define WAVE_FORMAT_SIPROLAB_ACELP4800 0x0131 /* Sipro Lab Telecom Inc. */ +#define WAVE_FORMAT_SIPROLAB_ACELP8V3 0x0132 /* Sipro Lab Telecom Inc. */ +#define WAVE_FORMAT_SIPROLAB_G729 0x0133 /* Sipro Lab Telecom Inc. */ +#define WAVE_FORMAT_SIPROLAB_G729A 0x0134 /* Sipro Lab Telecom Inc. */ +#define WAVE_FORMAT_SIPROLAB_KELVIN 0x0135 /* Sipro Lab Telecom Inc. */ +#define WAVE_FORMAT_G726ADPCM 0x0140 /* Dictaphone Corporation */ +#define WAVE_FORMAT_QUALCOMM_PUREVOICE 0x0150 /* Qualcomm, Inc. */ +#define WAVE_FORMAT_QUALCOMM_HALFRATE 0x0151 /* Qualcomm, Inc. */ +#define WAVE_FORMAT_TUBGSM 0x0155 /* Ring Zero Systems, Inc. */ +#define WAVE_FORMAT_MSAUDIO1 0x0160 /* Microsoft Corporation */ +#define WAVE_FORMAT_WMAUDIO2 0x0161 /* Microsoft Corporation */ +#define WAVE_FORMAT_WMAUDIO3 0x0162 /* Microsoft Corporation */ +#define WAVE_FORMAT_WMAUDIO_LOSSLESS 0x0163 /* Microsoft Corporation */ +#define WAVE_FORMAT_WMASPDIF 0x0164 /* Microsoft Corporation */ +#define WAVE_FORMAT_UNISYS_NAP_ADPCM 0x0170 /* Unisys Corp. */ +#define WAVE_FORMAT_UNISYS_NAP_ULAW 0x0171 /* Unisys Corp. */ +#define WAVE_FORMAT_UNISYS_NAP_ALAW 0x0172 /* Unisys Corp. */ +#define WAVE_FORMAT_UNISYS_NAP_16K 0x0173 /* Unisys Corp. */ +#define WAVE_FORMAT_CREATIVE_ADPCM 0x0200 /* Creative ADPCM */ +#define WAVE_FORMAT_CREATIVE_FASTSPEECH8 0x0202 /* Creative FastSpeech8 */ +#define WAVE_FORMAT_CREATIVE_FASTSPEECH10 0x0203 /* Creative FastSpeech10 */ +#define WAVE_FORMAT_UHER_ADPCM 0x0210 /* UHER informatic GmbH */ +#define WAVE_FORMAT_QUARTERDECK 0x0220 /* Quarterdeck */ +#define WAVE_FORMAT_ILINK_VC 0x0230 /* I-link Worldwide */ +#define WAVE_FORMAT_RAW_SPORT 0x0240 /* Aureal Semiconductor */ +#define WAVE_FORMAT_ESST_AC3 0x0241 /* ESS Technology, Inc. */ +#define WAVE_FORMAT_GENERIC_PASSTHRU 0x0249 +#define WAVE_FORMAT_IPI_HSX 0x0250 /* Interactive Products, Inc. */ +#define WAVE_FORMAT_IPI_RPELP 0x0251 /* Interactive Products, Inc. */ +#define WAVE_FORMAT_CS2 0x0260 /* Consistent Software */ +#define WAVE_FORMAT_SONY_SCX 0x0270 /* Sony Corp. */ +#define WAVE_FORMAT_FM_TOWNS_SND 0x0300 /* FM Towns Snd */ +#define WAVE_FORMAT_BTV_DIGITAL 0x0400 /* BTV Digital */ +#define WAVE_FORMAT_QDESIGN_MUSIC 0x0450 /* QDesign Corporation */ +#define WAVE_FORMAT_VME_VMPCM 0x0680 /* VME VMPCM */ +#define WAVE_FORMAT_TPC 0x0681 /* AT&T Labs, Inc. */ +#define WAVE_FORMAT_OLIGSM 0x1000 /* OLIGSM */ +#define WAVE_FORMAT_OLIADPCM 0x1001 /* OLIADPCM */ +#define WAVE_FORMAT_OLICELP 0x1002 /* OLICELP */ +#define WAVE_FORMAT_OLISBC 0x1003 /* OLISBC */ +#define WAVE_FORMAT_OLIOPR 0x1004 /* OLIOPR */ +#define WAVE_FORMAT_LH_CODEC 0x1100 /* LH Codec */ +#define WAVE_FORMAT_NORRIS 0x1400 /* Norris */ +//#define WAVE_FORMAT_ISIAUDIO 0x1401 /* ISIAudio */ +#define WAVE_FORMAT_SOUNDSPACE_MUSICOMPRESS 0x1500 /* Soundspace Music Compression */ +#define WAVE_FORMAT_MPEG_ADTS_AAC 0x1600 /* Microsoft Corporation */ +#define WAVE_FORMAT_MPEG_RAW_AAC 0x1601 /* Microsoft Corporation */ +#define WAVE_FORMAT_NOKIA_MPEG_ADTS_AAC 0x1608 /* Microsoft Corporation */ +#define WAVE_FORMAT_NOKIA_MPEG_RAW_AAC 0x1609 /* Microsoft Corporation */ +#define WAVE_FORMAT_VODAFONE_MPEG_ADTS_AAC 0x160A /* Microsoft Corporation */ +#define WAVE_FORMAT_VODAFONE_MPEG_RAW_AAC 0x160B /* Microsoft Corporation */ +#define WAVE_FORMAT_DVM 0x2000 /* DVM */ +#define WAVE_FORMAT_EXTENSIBTSMIRACAST 0xFFFC /* LSZHANG TS Miracast WIFI*/ +#define WAVE_FORMAT_EXTENSIBTS 0xFFFD /* LSZHANG TS */ +#define WAVE_FORMAT_EXTENSIBLE 0xFFFE /* SubFormat */ +#define WAVE_FORMAT_DVD_LPCM 0xFFA0 /* Added by Khan for DVD-LPCM*/ + +#define WAVE_FORMAT_DEVELOPMENT 0xFFFF /* Development */ + +//user define adpcm codec type from video file +#define ADPCM_CODEC_ID_IMA_QT 0xE000 +#define ADPCM_CODEC_ID_IMA_WAV 0xE001 /* from avi file format */ +#define ADPCM_CODEC_ID_IMA_DK3 0xE002 +#define ADPCM_CODEC_ID_IMA_DK4 0xE003 +#define ADPCM_CODEC_ID_IMA_WS 0xE004 +#define ADPCM_CODEC_ID_IMA_SMJPEG 0xE005 +#define ADPCM_CODEC_ID_MS 0xE006 +#define ADPCM_CODEC_ID_4XM 0xE007 +#define ADPCM_CODEC_ID_XA 0xE008 +#define ADPCM_CODEC_ID_ADX 0xE009 +#define ADPCM_CODEC_ID_EA 0xE00A +#define ADPCM_CODEC_ID_G726 0xE00B +#define ADPCM_CODEC_ID_CT 0xE00C +#define ADPCM_CODEC_ID_SWF 0xE00D /* from flv/swf file format */ +#define ADPCM_CODEC_ID_YAMAHA 0xE00E +#define ADPCM_CODEC_ID_SBPRO_4 0xE00F +#define ADPCM_CODEC_ID_SBPRO_3 0xE010 +#define ADPCM_CODEC_ID_SBPRO_2 0xE011 + +#define AMR_FORMAT_NONE 0 // undefine amr format +#define AMR_FORMAT_NARROWBAND 1 // narrow band amr format +#define AMR_FORMAT_WIDEBAND 2 // wide band amr format + +#define ADECODER_MAX_LANG_CHAR_SIZE (64) + +typedef struct Cedar_raw_data CdxPlaybkCfg; + +enum AUIDO_RAW_DATA_TYPE +{ + AUDIO_RAW_DATA_UNKOWN = 0, + AUDIO_RAW_DATA_PCM = 1, + AUDIO_RAW_DATA_AC3 = 2, + AUDIO_RAW_DATA_MPEG1 = 3, + AUDIO_RAW_DATA_MP3 = 4, + AUDIO_RAW_DATA_MPEG2 = 5, + AUDIO_RAW_DATA_AAC = 6, + AUDIO_RAW_DATA_DTS = 7, + AUDIO_RAW_DATA_ATRAC = 8, + AUDIO_RAW_DATA_ONE_BIT_AUDIO = 9, + AUDIO_RAW_DATA_DOLBY_DIGITAL_PLUS = 10, + AUDIO_RAW_DATA_DTS_HD = 11, + AUDIO_RAW_DATA_MAT = 12, + AUDIO_RAW_DATA_DST = 13, + AUDIO_RAW_DATA_WMAPRO = 14 +}; + +struct Cedar_raw_data +{ + int nRoutine;//UI set 0:pcm;1:hdmi raw data;2:spdif raw data; + int nNeedDirect;//if init flag 0:no init raw;1:init raw + int nChannels; + int nSamplerate; + int nBitpersample; + enum AUIDO_RAW_DATA_TYPE nDataType; +}; + +typedef struct AUDIOSTREAMINFO +{ + enum EAUDIOCODECFORMAT eCodecFormat; + int eSubCodecFormat; // + int nChannelNum; + int nBitsPerSample; + int nSampleRate; + int nAvgBitrate; + int nMaxBitRate; + unsigned int nFileSize; + int eAudioBitstreamSource; + int eDataEncodeType; + unsigned char strLang[ADECODER_MAX_LANG_CHAR_SIZE]; + int nCodecSpecificDataLen; + char* pCodecSpecificData; + int nFlags; + int nBlockAlign; + CdxPlaybkCfg raw_data; +}AudioStreamInfo; + +typedef struct __BsInFor +{ + int TotalplayTime; + int NowPlayTime; + int Samplerate; + int bitrate; + int chan; + int CBRflag;//1 cbr 0 vbr + int framesize; + int framecount; + int oldfs; + int oldbs; + int firstflag; + int framepcms; + int modeflag;//flag 0bits: 1:raw data 0:pcm + int ulMode;//decode flag 0:pcm ,>raw data out ,1:hdmi,2:spdif + int bitpersample;//bits per sample + //add for control from ad_cedar_ctx + int nBitStreamUnderFlow; + int nShowBitsReturn; + int out_channels; + int out_samplerate; + //global + int nDecodeMode; + int nDemuxType; + int nIsHwCodec; + int64_t NowPTSTime; + +}BsInFor; + +typedef struct AudioDecoder AudioDecoder; +struct AudioDecoder +{ + int nPrivFlag;//0:play,1:reset,2:file end + int nGetAudioInfoFlag; + int nAudioChannel; + int volumegain; + int mute; + int nEnableResample; + int RawPlayFlag; +}; + +int ParserRequestBsBuffer(AudioDecoder* pDecoder, + int nRequireSize, + unsigned char** ppBuf, + int* pBufSize, + unsigned char** ppRingBuf, + int* pRingBufSize, + int* nOffset); + +int ParserUpdateBsBuffer(AudioDecoder* pDecoder, + int nFilledLen, + int64_t nTimeStamp, + int nOffset); + +void BsQueryQuality(AudioDecoder* pDecoder, + int* pValidPercent, + int* vbv); + +int AudioStreamDataSize(AudioDecoder* pDecoder); + +int AudioStreamBufferSize(void); + +int AudioStreamBufferMaxFrameNum(void); + +int AudioPCMDataSize(AudioDecoder* pDecoder); + +int DecRequestPcmBuffer(AudioDecoder* pDecoder, char **pOutWritePtr); + +int DecUpdatePcmBuffer(AudioDecoder* pDecoder, int nPcmOutSize); + +int PlybkRequestPcmBuffer(AudioDecoder* pDecoder, unsigned char **pOutReadPtr, int *psize); + +int PlybkUpdatePcmBuffer(AudioDecoder* pDecoder, int nPcmOutSize); + +int64_t PlybkRequestPcmPts(AudioDecoder* pDecoder); + +void PcmQueryQuality(AudioDecoder* pDecoder, int* pValidPercent, int* vbv); + +void AudioDecoderSeek(AudioDecoder* pDecoder, int64_t nSeekTime); + +int InitializeAudioDecoder(AudioDecoder* pDecoder, + AudioStreamInfo* pAudioStreamInfo, + BsInFor* pBsInFor); + +int ResetAudioDecoder(AudioDecoder* pDecoder, int64_t nSeekTime); + +int DecodeAudioStream(AudioDecoder* pDecoder, + AudioStreamInfo* pAudioStreamInfo, + char* ppBuf, + int* pBufSize); + +int DestroyAudioDecoder(AudioDecoder* pDecoder); + +AudioDecoder* CreateAudioDecoder(void); +#ifdef OS_LINUX +void SetRawPlayParam(AudioDecoder* pDecoder,void *self,int flag); +#else +void SetRawPlayParam(AudioDecoder* pDecoder,void *self); +#endif + +#ifdef CEDARX_SUPPORT_SOUNDTOUCH +void AudioSoundTouchSetPitch(AudioDecoder* pDecoder, float pitchDelta); +#endif + +#if VOLUME_SUPPORT +#ifdef OS_LINUX +int SetAudioVolume(AudioDecoder* pDecoder, int32_t nVolume); +#endif +#endif +#ifdef __cplusplus +} +#endif + +#endif diff --git a/platform/mcu/xr871/src/cedarx/Cdx2.0Plugin/include/auGaincom.h b/platform/mcu/xr871/src/cedarx/Cdx2.0Plugin/include/auGaincom.h new file mode 100644 index 0000000000..35cfed4b78 --- /dev/null +++ b/platform/mcu/xr871/src/cedarx/Cdx2.0Plugin/include/auGaincom.h @@ -0,0 +1,15 @@ + +#ifndef ___AUGAINCOM_H__ +#define ___AUGAINCOM_H__ + +typedef struct ___AudioGain__{ +//input para + int preamp;//-20 -- 20 db + int InputChan; + short *InputPtr; + int InputLen;//total byte + int OutputChan;//0 1: only 2 3: double left 4:double right + short *OutputPtr; +}AudioGain; +int do_AudioGain(AudioGain *AGX); +#endif//___AUGAINCOM_H__ \ No newline at end of file diff --git a/platform/mcu/xr871/src/cedarx/Cdx2.0Plugin/include/aumixcom.h b/platform/mcu/xr871/src/cedarx/Cdx2.0Plugin/include/aumixcom.h new file mode 100644 index 0000000000..6849f175f0 --- /dev/null +++ b/platform/mcu/xr871/src/cedarx/Cdx2.0Plugin/include/aumixcom.h @@ -0,0 +1,32 @@ + +#ifndef ___aumixcommon__ +#define ___aumixcommon__ +typedef struct __PcmInfo__{ +//input infor + unsigned int SampleRate; + short *PCMPtr; + unsigned int PcmLen; + unsigned int Chan; +}PcmInfo; + +typedef struct ___AudioMix__{ +//input para + PcmInfo *InputA; + PcmInfo *InputB; + PcmInfo *TempBuf; + PcmInfo *Output; + unsigned int ByPassflag; + //output para + short *MixbufPtr; + unsigned int MixLen; + unsigned int Mixch; + void* RESI; + +}AudioMix; +#if MIX_SUPPORT +int do_AuMIX(AudioMix *AMX); +#endif +void* Init_ResampleInfo(); +void Destroy_ResampleInfo(void * ri); + +#endif diff --git a/platform/mcu/xr871/src/cedarx/Cdx2.0Plugin/include/cedar_abs_packet_hdr.h b/platform/mcu/xr871/src/cedarx/Cdx2.0Plugin/include/cedar_abs_packet_hdr.h new file mode 100644 index 0000000000..4f4d88df3a --- /dev/null +++ b/platform/mcu/xr871/src/cedarx/Cdx2.0Plugin/include/cedar_abs_packet_hdr.h @@ -0,0 +1,63 @@ +/* +********************************************************************************************************* +* eMOD +* the Easy Portable/Player Operation System +* mod_mmp sub-system +* +* (c) Copyright 2008-2009, Kevin.Z China +* All Rights Reserved +* +* File : cedar_abs_packet_hdr.h +* Version: V1.0 +* By : kevin.z +* Date : 2009-4-17 10:23 +********************************************************************************************************* +*/ +#ifndef _CEDAR_ABS_PACKET_HDR_H_ +#define _CEDAR_ABS_PACKET_HDR_H_ + +#include "adecoder.h" +#include "ad_cedarlib_com.h" + +int AdCedarBuildAACPacketHdr(unsigned char* extraData, + int extraDataLen , + int uPacketLen, + unsigned char* pBuf, + int* pHdrLen, + int channels, + int sampleRate); + +int AdCedarUpdateAACPacketHdr(unsigned char* pBuf, int uHdrLen, int uPacketLen); + +int split_xiph_headers(unsigned char* codec_priv, + int codec_priv_size, + char** extradata, + int* extradata_size); + +int SetAudioBsHeader_PCM(AudioStreamInfo* tmpAbsFmt, Ac320FileRead* FileReadInfo); + +/* +-------------------------------------------------------------------------------- +AAC ADTS HEADER BIT INFORMATION DEFINE, BASED ON BIT STREAM +-------------------------------------------------------------------------------- +syncword:12 0xfff +id :1 0x0 +layer :2 0x0 +protect :1 0x1 +profile :2 +fsidx :4 +priv :1 0x0 +chncfg :3 +origcpy :1 0x0 +home :1 0x0 + +cpybit :1 0x0 +cpystart:1 0x0 +frmlen :13 +buffull :11 0x7ff +rawblk :2 0x1 +-------------------------------------------------------------------------------- +*/ + + +#endif //_CEDAR_ABS_PACKET_HDR_H_ diff --git a/platform/mcu/xr871/src/cedarx/Cdx2.0Plugin/include/log.h b/platform/mcu/xr871/src/cedarx/Cdx2.0Plugin/include/log.h new file mode 100644 index 0000000000..44e772abf4 --- /dev/null +++ b/platform/mcu/xr871/src/cedarx/Cdx2.0Plugin/include/log.h @@ -0,0 +1,119 @@ +#ifndef LOG_H +#define LOG_H +#ifndef LOG_TAG +#define LOG_TAG "AllwinnerAudioCodec" +#endif + +#include "cdx_malloc_dbg.h" + + +// temp define +#define MP3_SUPPORT (1) +#define AMR_SUPPORT (1) + +#define OPTION_LOG_LEVEL_CLOSE +#define IOT_CEDARX_DECODE_LIB (1) +#define VOLUME_SUPPORT (0) +#define MIX_SUPPORT (0) +#define OS_LINUX + +#define OS_FREERTOS + +#ifdef OS_ANDROID + #include + + #define LOG_LEVEL_ERROR ANDROID_LOG_ERROR + #define LOG_LEVEL_WARNING ANDROID_LOG_WARN + #define LOG_LEVEL_INFO ANDROID_LOG_INFO + #define LOG_LEVEL_VERBOSE ANDROID_LOG_VERBOSE + #define LOG_LEVEL_DEBUG ANDROID_LOG_DEBUG + + #define AWLOG(level, fmt, arg...) \ + LOG_PRI(level, LOG_TAG, "<%s:%u>: "fmt, __FUNCTION__, __LINE__, ##arg) + +#elif (defined OS_LINUX) + #include + #include + + #ifndef LOG_LEVEL_ERROR + #define LOG_LEVEL_ERROR "error " + #endif + #ifndef LOG_LEVEL_WARNING + #define LOG_LEVEL_WARNING "warning" + #endif + #ifndef LOG_LEVEL_INFO + #define LOG_LEVEL_INFO "info " + #endif + #ifndef LOG_LEVEL_VERBOSE + #define LOG_LEVEL_VERBOSE "verbose" + #endif + #ifndef LOG_LEVEL_DEBUG + #define LOG_LEVEL_DEBUG "debug " + #endif + + int wrap_printf(const char *fmt, ...); + #define AWLOG(level, fmt, arg...) \ + wrap_printf("%s: %s <%s:%u>: "fmt"\n", level, LOG_TAG, __FUNCTION__, __LINE__, ##arg)/* printf("%s: %s <%s:%u>: "fmt"\n", level, LOG_TAG, __FUNCTION__, __LINE__, ##arg) */ +#elif (defined OS_FREERTOS) + #include + #include + + #define LOG_LEVEL_ERROR "error " + #define LOG_LEVEL_WARNING "warning" + #define LOG_LEVEL_INFO "info " + #define LOG_LEVEL_VERBOSE "verbose" + #define LOG_LEVEL_DEBUG "debug " + + #define AWLOG(level, fmt, arg...) \ + printf("%s: %s <%s:%u>: "fmt"\n", level, LOG_TAG, __func__, __LINE__, ##arg) +#else + #error "invalid configuration of os." +#endif + +#define logd(fmt, arg...) //AWLOG(LOG_LEVEL_DEBUG, fmt, ##arg) + +#ifdef OPTION_LOG_LEVEL_CLOSE + +#define loge(fmt, arg...) +#define logw(fmt, arg...) +#define logi(fmt, arg...) +#define logv(fmt, arg...) + +#elif (defined OPTION_LOG_LEVEL_ERROR) + +#define loge(fmt, arg...) AWLOG(LOG_LEVEL_ERROR, "\033[40;31m"fmt"\033[0m", ##arg) +#define logw(fmt, arg...) +#define logi(fmt, arg...) +#define logv(fmt, arg...) + +#elif (defined OPTION_LOG_LEVEL_WARNING) + +#define loge(fmt, arg...) AWLOG(LOG_LEVEL_ERROR, "\033[40;31m"fmt"\033[0m", ##arg) +#define logw(fmt, arg...) AWLOG(LOG_LEVEL_WARNING, fmt, ##arg) +#define logi(fmt, arg...) +#define logv(fmt, arg...) + +#elif (defined OPTION_LOG_LEVEL_DEFAULT) + +#define loge(fmt, arg...) AWLOG(LOG_LEVEL_ERROR, "\033[40;31m"fmt"\033[0m", ##arg) +#define logw(fmt, arg...) AWLOG(LOG_LEVEL_WARNING, fmt, ##arg) +#define logi(fmt, arg...) AWLOG(LOG_LEVEL_INFO, fmt, ##arg) +#define logv(fmt, arg...) + +#elif (defined OPTION_LOG_LEVEL_DETAIL) + +#define loge(fmt, arg...) AWLOG(LOG_LEVEL_ERROR, "\033[40;31m"fmt"\033[0m", ##arg) +#define logw(fmt, arg...) AWLOG(LOG_LEVEL_WARNING, fmt, ##arg) +#define logi(fmt, arg...) AWLOG(LOG_LEVEL_INFO, fmt, ##arg) +#define logv(fmt, arg...) AWLOG(LOG_LEVEL_VERBOSE, fmt, ##arg) + +#else + #error "invalid configuration of debug level." +#endif + +#define printf wrap_printf +#define PRINTF printf +#define CDX_FAILED() //PRINTF("[%s failed] line %d\n", __func__, __LINE__) + +#endif + diff --git a/platform/mcu/xr871/src/cedarx/Cdx2.0Plugin/include/rw_data_api.h b/platform/mcu/xr871/src/cedarx/Cdx2.0Plugin/include/rw_data_api.h new file mode 100644 index 0000000000..436df1ff3f --- /dev/null +++ b/platform/mcu/xr871/src/cedarx/Cdx2.0Plugin/include/rw_data_api.h @@ -0,0 +1,31 @@ +#ifndef RW_DATA_API_H +#define RW_DATA_API_H +#include +#include +#include +#include "ad_cedarlib_com.h" +#include "AudioDec_Decode.h" + +#ifdef __cplusplus +extern "C" { +#endif + +int ShowAbsBits(void *buf, int Len, Ac320FileRead *FileInfo); + +void FlushAbsBits(int Len, Ac320FileRead *FileInfo); + +int FREAD_AC320(void *buf, int Len, Ac320FileRead *FileInfo); + +int FSeek_AC320(int Len, Ac320FileRead *FileInfo); + +int FSeek_AC320_Origin(int Len, Ac320FileRead *FileInfo, int origin); + +void FlushPtsAtOnce(Ac320FileRead *FileInfo); + +int BigLitEndianCheck(Ac320FileRead *FileInfo, short data); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/platform/mcu/xr871/src/cedarx/Cdx2.0Plugin/tools/memcheck/memcheck.h b/platform/mcu/xr871/src/cedarx/Cdx2.0Plugin/tools/memcheck/memcheck.h new file mode 100644 index 0000000000..1d6b67bd4a --- /dev/null +++ b/platform/mcu/xr871/src/cedarx/Cdx2.0Plugin/tools/memcheck/memcheck.h @@ -0,0 +1,32 @@ +#ifndef MEMCHECK_H +#define MEMCHECK_H +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ +#include +#include +#include + +#define RECSIZE 8*1024 + +typedef struct _mymeminfo mymeminfo; + +struct _mymeminfo +{ + int size; + unsigned int ptr; + const char* name; + int line; + int malloc; + int free; +}; + +void* ckmalloc(int size, const char* name, int line); +void ckfree(void* ptr); +int getmallocsz(); +int getfreesz(); +void showmeinfo(); +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif \ No newline at end of file diff --git a/platform/mcu/xr871/src/cedarx/Cdx2.0Plugin/tools/memcheck/memname.h b/platform/mcu/xr871/src/cedarx/Cdx2.0Plugin/tools/memcheck/memname.h new file mode 100644 index 0000000000..88896cabb2 --- /dev/null +++ b/platform/mcu/xr871/src/cedarx/Cdx2.0Plugin/tools/memcheck/memname.h @@ -0,0 +1,7 @@ +#ifndef MEMNAME_H +#define MEMNAME_H + +#define malloc(a) ckmalloc(a, __func__, __LINE__) +#define free(a) ckfree(a) + +#endif \ No newline at end of file diff --git a/platform/mcu/xr871/src/cedarx/CdxEncPlugin/Android.mk b/platform/mcu/xr871/src/cedarx/CdxEncPlugin/Android.mk new file mode 100644 index 0000000000..7767b75d8a --- /dev/null +++ b/platform/mcu/xr871/src/cedarx/CdxEncPlugin/Android.mk @@ -0,0 +1,38 @@ +LOCAL_PATH:= $(call my-dir) + +include $(CLEAR_VARS) + +include $(LOCAL_PATH)/../../../config.mk + +LOCAL_MODULE_TAGS := optional + +LOCAL_SRC_FILES:= \ + $(notdir $(wildcard $(LOCAL_PATH)/*.c)) \ + $(notdir $(wildcard $(LOCAL_PATH)/*.cpp)) + +LOCAL_C_INCLUDES := \ + $(LOCAL_PATH) \ + $(LOCAL_PATH)/include \ + $(LOCAL_PATH)/../../../VE/include \ + $(LOCAL_PATH)/../../../MEMORY/include \ + $(LOCAL_PATH)/../../../ \ + + +LOCAL_SHARED_LIBRARIES := \ + libcutils \ + libutils \ + libVE \ + libMemAdapter + +LOCAL_LDLIBS := -llog + +LOCAL_LDFLAGS += $(LOCAL_PATH)/lib_a4.4/static/libaacenc.a \ + $(LOCAL_PATH)/lib_a4.4/static/libadpcmenc.a \ + $(LOCAL_PATH)/lib_a4.4/static/libmp3enc.a \ +# +# $(LOCAL_PATH)/lib_a4.4/static/libpcmenc.a + + +LOCAL_MODULE:= libaencoder + +include $(BUILD_SHARED_LIBRARY) diff --git a/platform/mcu/xr871/src/cedarx/CdxEncPlugin/Makefile b/platform/mcu/xr871/src/cedarx/CdxEncPlugin/Makefile new file mode 100644 index 0000000000..f4381fab2d --- /dev/null +++ b/platform/mcu/xr871/src/cedarx/CdxEncPlugin/Makefile @@ -0,0 +1,176 @@ +AudioEncBASE = /home/$(USER)/SVNAudioLib/dev/audiodec/codec/CdxEncPlugin +AudioLibBASE = /home/$(USER)/SVNAudioLib/dev/audiodec/codec/AudioLib + +include $(AudioEncBASE)/config.mk + +################################################################################ +## set flags for golobal compile and link setting. +################################################################################ + +CONFIG_FOR_COMPILE = +CONFIG_FOR_LINK = + +################################################################################ +## set target. +################################################################################ + +TargetName = libaencoder.so +Target = $(LIBPATH)/$(TargetName) + +################################################################################ +## build configures print. +################################################################################ + +$(warning "#################################################") +$(warning "# Allwinner audio codec compile information ") +$(warning "# ") +$(warning "# OS_PLATFORM is $(OS_PLATFORM)") +$(warning "# CONFIG_CC is $(CONFIG_CC)") +$(warning "# CONFIG_LOG_LEVEL is $(CONFIG_LOG_LEVEL)") +$(warning "# OS_DEBUG_CONFIG is $(OS_DEBUG_CONFIG)") +$(warning "# OS_CHECK_MEMORY_CONFIG is $(OS_CHECK_MEMORY_CONFIG)") +$(warning "# TargetName is $(TargetName)") +$(warning "# Target generated at $(LIBPATH)") +$(warning "# ") +$(warning "#################################################") + +################################################################################ +## set build path and build target. +################################################################################ + +ifneq ($(LIBPATH),wildcard($(LIBPATH))) +a := $(shell mkdir -p $(LIBPATH)) +endif +ifneq ($(BUILDPATH),wildcard($(BUILDPATH))) +a := $(shell mkdir -p $(BUILDPATH)) +endif + +################################################################################ +## set the source files, object files and dependency files +################################################################################ + +## set the source files. +SourceFiles = aencoder.c pcm_enc.c +$(warning "SourceFiles : $(SourceFiles)") + +################################################################################ +## set obj files, mkdir respect build dir +################################################################################ + +SourcePath = $(foreach src_dir, $(SourceFiles), $(dir $(src_dir))) +$(foreach src_dir, $(SourcePath), $(shell mkdir -p $(BUILDPATH)/$(src_dir))) + +ObjectFiles = $(addprefix $(BUILDPATH)/, $(addsuffix .o ,$(basename $(SourceFiles)))) + +## set the include path for compile flags. +SourceIncludePath = $(foreach dir,$(SourcePath),-I$(dir)) \ + -I../../../ \ + -I./include \ + +## set compile flags +CompileFlags = $(CONFIG_FOR_COMPILE) $(SourceIncludePath) -O1 -fPIC -ldl +#-mfpu=neon + +ifeq ($(OS_PLATFORM), OS_LINUX) +CompileFlags += -DOS_LINUX $(EXTRA_FLAG) +endif + +ifeq ($(OS_DEBUG_CONFIG), true) +CompileFlags += -D__AD_CEDAR_DBG_WRITEOUT_BITSTREAM +CompileFlags += -D__AD_CEDAR_DBG_WRITEOUT_PCM_BUFFER +endif + +ifeq ($(CONFIG_LOG_LEVEL), OPTION_LOG_LEVEL_CLOSE) +CompileFlags += -DOPTION_LOG_LEVEL_CLOSE +endif + +ifeq ($(CONFIG_LOG_LEVEL), OPTION_LOG_LEVEL_ERROR) +CompileFlags += -DOPTION_LOG_LEVEL_ERROR +endif + +ifeq ($(CONFIG_LOG_LEVEL), OPTION_LOG_LEVEL_WARNING) +CompileFlags += -DOPTION_LOG_LEVEL_WARNING +endif + +ifeq ($(CONFIG_LOG_LEVEL), OPTION_LOG_LEVEL_DEFAULT) +CompileFlags += -DOPTION_LOG_LEVEL_DEFAULT +endif + +ifeq ($(CONFIG_LOG_LEVEL), OPTION_LOG_LEVEL_DETAIL) +CompileFlags += -DOPTION_LOG_LEVEL_DETAIL +endif + +## set link flags +LoadFlags = $(CONFIG_FOR_LINK) -ldl -shared + +LoadFlags += -lpthread -laacenc -lmp3enc -lpcmenc -lm +#-lstdc++ -Wl,--whole-archive -Wl,--no-whole-archive + +ifeq ($(CONFIG_CC), OPTION_CC_GNUEABIHF) +LoadFlags += -Llib/build_by_arm_linux_gnueabihf_static +endif + +ifeq ($(CONFIG_CC),OPTION_CC_GNUEABI) +LoadFlags += -Llib/linuxgnueabi/ +endif + +ifeq ($(CONFIG_CC),OPTION_CC_UCGNUEABI) +LoadFlags += -Llib/uclgnueabi/ +endif + +ifeq ($(CONFIG_CC),OPTION_CC_LINUX_UCGNUEABI) +LoadFlags += -Llib/linuxgnueabi/ +endif + +ifeq ($(CONFIG_CC),OPTION_CC_LINUX_MUSLGNUEABI) +LoadFlags +=-Llib/arm926_musl +#LoadFlags +=-L./lib/muslgnueabi +endif + +ifeq ($(CONFIG_CC),OPTION_CC_LINUX_MUSLGNUEABI64) +LoadFlags +=-Llib/muslgnueabi64 +endif + +ifeq ($(CONFIG_CC),OPTION_CC_AARCH64_OPENWRT_MUSL) +LoadFlags +=-Llib/aarch64_openwrt_linux_musl_gcc +endif + +################################################################################ +## make commands, all/clean/cleanall +################################################################################ + +## define commands for make, sush as all, clean +.PHONY: all clean cleantarget cleanall +all:$(Target) + @echo Make $(TargetName) over... + @echo Target path : $(LIBPATH) + +clean: + -rm -rf $(ObjectFiles) + -rm -rf $(Target) + +cleanall: clean + -rm -rf $(BUILDPATH) + + +################################################################################ +## define target , ld target. +################################################################################ + +## compile source files to object files. +$(BUILDPATH)/%.o:%.c + $(CC) $(CompileFlags) -o $@ -c $< + +## link object files to the target share library. +$(Target):$(ObjectFiles) + $(LD) -o $@ $^ $(LoadFlags) + + +## include the .d files to set dependency rules. +ifneq ($(MAKECMDGOALS),clean) +ifneq ($(MAKECMDGOALS),cleantarget) +ifneq ($(MAKECMDGOALS),cleanall) +-include $(DependFiles) +endif +endif +endif diff --git a/platform/mcu/xr871/src/cedarx/CdxEncPlugin/aacencApi.h b/platform/mcu/xr871/src/cedarx/CdxEncPlugin/aacencApi.h new file mode 100644 index 0000000000..6ca73a7b30 --- /dev/null +++ b/platform/mcu/xr871/src/cedarx/CdxEncPlugin/aacencApi.h @@ -0,0 +1,18 @@ + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef __AAC_ENC_LIB_API__ +#define __AAC_ENC_LIB_API__ + +#include + +extern struct __AudioENC_AC320 *AudioAACENCEncInit(); +extern int AudioAACENCEncExit(struct __AudioENC_AC320 *p); + +#endif // __AAC_ENC_LIB_API__ + +#ifdef __cplusplus +} +#endif diff --git a/platform/mcu/xr871/src/cedarx/CdxEncPlugin/aenc_sw_lib.h b/platform/mcu/xr871/src/cedarx/CdxEncPlugin/aenc_sw_lib.h new file mode 100644 index 0000000000..a4bc7abd50 --- /dev/null +++ b/platform/mcu/xr871/src/cedarx/CdxEncPlugin/aenc_sw_lib.h @@ -0,0 +1,106 @@ +/* +******************************************************************************** +* eMOD +* the Easy Portable/Player Develop Kits +* mod_herb sub-system +* (module name, e.g.mpeg4 decoder plug-in) module +* +* (c) Copyright 2009-2010, Allwinner Microelectronic Co., Ltd. +* All Rights Reserved +* +* File : aenc_sw_lib.h +* Version: V1.0 +* By : Eric_wang +* Date : 2010-4-26 +* Description: +******************************************************************************** +*/ +#ifdef __cplusplus +extern "C" { +#endif + +#include "aencoder.h" + +#ifndef _AENC_SW_LIB_H_ +#define _AENC_SW_LIB_H_ + +#define FIFO_LEVEL (4)//(32) + +#define BS_BUFFER_SIZE (12*1024)//(128*1024) +//#define PCM_FRAME_SIZE (32*1024) +#define BS_HEADER_SIZE (1*1024) + +//#define OUT_BUFFER_SIZE (512*1024) +#define OUT_ENCODE_BUFFER_SIZE (2*1024)//(4*1024) //byte +#define MAXDECODESAMPLE (1024) + +//encode input data information +typedef struct __audio_enc_inf_t +{ + int InSamplerate; //fs + int InChan; //pcm chan 1:mon 2:stereo + int bitrate; //bs + int SamplerBits; //only for 16bits + int OutSamplerate; //fs,now OutSamplerate must equal InSamplerate + int OutChan; // chan + int frame_style; //for aac: 0:add head,1:raw data; for pcm: 2:mpegTs pcm(big endian), other: common pcm(little endian) + +}__audio_enc_inf_t; + +//#define BsBuffLEN 20*1024 +typedef struct __PCM_BUF_MANAGER +{ + unsigned char *pBufStart; //buffer׵ַ + int uBufTotalLen; //ܳ + unsigned char *pBufReadPtr; //ڶָ + int uDataLen; //Чݳ + unsigned char *pBufWritPtr; //дָ + int uFreeBufSize; //೤ + int uDataFlowflag; //Ƿ񳬹Ԥ룬10֡0ûг + void *parent; // encode use,not lib use; + +} __pcm_buf_manager_t; + + +typedef struct __COM_INTERNAL_PARAMETER +{ + unsigned int ulNowTimeMS; //ǰʱms + unsigned int ulPCMgainSet; //PCM + unsigned int framecount; //frames + + unsigned int ulEncCom; //0: normal enc 1: ff end 5: stop + unsigned char BsHeaderBuf[BS_HEADER_SIZE]; + unsigned int ValidHeaderLen; + unsigned int *pEncInfoSet; // encode lib use; + +} __com_internal_prameter_t; + + +typedef struct __AudioENC_AC320 +{ + __pcm_buf_manager_t *pPcmBufManager; + __audio_enc_inf_t *AudioBsEncInf; + __com_internal_prameter_t *EncoderCom; + int Encinitedflag; + + int (*EncInit)(struct __AudioENC_AC320 *p); + int (*EncFrame)(struct __AudioENC_AC320 *p,char *OutBuffer,int *OutBuffLen); + int (*EncExit)(struct __AudioENC_AC320 *p); + +}AudioEnc_AC320; + +extern struct __AudioENC_AC320 *EncInit(void); +extern int EncExit(struct __AudioENC_AC320 *p); + +struct __AudioENC_AC320 *AudioMP3ENCEncInit(void); +int AudioMP3ENCEncExit(struct __AudioENC_AC320 *p); + +//Ҫϲģʵֵĺ +extern int GetPcmDataSize(__pcm_buf_manager_t *pPcmMan); +extern int ReadPcmDataForEnc(void *pBuf, int uGetLen, __pcm_buf_manager_t *pPcmMan); + +#endif /* _AENC_SW_LIB_H_ */ + +#ifdef __cplusplus +} +#endif diff --git a/platform/mcu/xr871/src/cedarx/CdxEncPlugin/amrencApi.h b/platform/mcu/xr871/src/cedarx/CdxEncPlugin/amrencApi.h new file mode 100644 index 0000000000..140bb09f13 --- /dev/null +++ b/platform/mcu/xr871/src/cedarx/CdxEncPlugin/amrencApi.h @@ -0,0 +1,18 @@ + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef __AMR_ENC_LIB_API__ +#define __AMR_ENC_LIB_API__ + +#include + +struct __AudioENC_AC320 *AudioAMRENCEncInit(void); +int AudioAMRENCEncExit(struct __AudioENC_AC320 *p); + +#endif // __AMR_ENC_LIB_API__ + +#ifdef __cplusplus +} +#endif diff --git a/platform/mcu/xr871/src/cedarx/CdxEncPlugin/config.mk b/platform/mcu/xr871/src/cedarx/CdxEncPlugin/config.mk new file mode 100644 index 0000000000..5013b0ae1a --- /dev/null +++ b/platform/mcu/xr871/src/cedarx/CdxEncPlugin/config.mk @@ -0,0 +1,140 @@ +######## Config for Your platform ########### +# You can choose follows for osplaform +#OS_LINUX +#OS_ANDROID_4_4 +#OS_ANDROID_5_0 +#OS_ANDROID_6_0 + +# You can choose follows for osplaform log level +#OPTION_LOG_LEVEL_CLOSE +#OPTION_LOG_LEVEL_ERROR +#OPTION_LOG_LEVEL_WARNING +#OPTION_LOG_LEVEL_DEFAULT +#OPTION_LOG_LEVEL_DETAIL + +OS_PLATFORM = OS_LINUX +CONFIG_LOG_LEVEL = OPTION_LOG_LEVEL_DEFAULT +OS_CHECK_MEMORY_CONFIG = false +OS_DEBUG_CONFIG = false +USE_IP_PROTECTION = false + + +######## Config for Your compile tool ######## +# You can choose follows for your compile tools +#OPTION_CC_GNUEABIHF +#OPTION_CC_GNUEABI +#OPTION_CC_ARM_GNUEABI +#OPTION_CC_ARM_OPENWRT_UCLIBCGNUEABI +#OPTION_CC_ARM_OPENWRT_MUSLGNUEABI +#OPTION_CC_UCLIBC_ARM926_STATIC +#OPTION_CC_X86_ANDROID +#OPTION_CC_AARCH64_OPENWRT_MUSL + +CONFIG_CC = OPTION_CC_GNUEABI + +ifeq ($(CONFIG_CC), OPTION_CC_GNUEABI) + +########## set compile tool chains to be arm-linux-gnueabihf- set. +CC = arm-linux-gnueabi-gcc +CPP = arm-linux-gnueabi-g++ +STRIP = arm-linux-gnueabi-strip +AR = arm-linux-gnueabi-ar +AS = arm-linux-gnueabi-as +LD = arm-linux-gnueabi-ld +endif + +ifeq ($(CONFIG_CC), OPTION_CC_AARCH64_OPENWRT_MUSL) + +########## set compile tool chains to be arm-linux-gnueabihf- set. +CC = aarch64-openwrt-linux-musl-gcc +CPP = aarch64-openwrt-linux-musl-g++ +STRIP = aarch64-openwrt-linux-musl-strip +AR = aarch64-openwrt-linux-musl-ar +AS = aarch64-openwrt-linux-musl-as +LD = aarch64-openwrt-linux-musl-ld +endif + +ifeq ($(CONFIG_CC), OPTION_CC_GNUEABIHF) + +########## set compile tool chains to be arm-linux-gnueabihf- set. +CC = arm-linux-gnueabihf-gcc +CPP = arm-linux-gnueabihf-g++ +STRIP = arm-linux-gnueabihf-strip +AR = arm-linux-gnueabihf-ar +AS = arm-linux-gnueabihf-as +LD = arm-linux-gnueabihf-ld +endif + +ifeq ($(CONFIG_CC), OPTION_CC_GNUEABI) + +# set compile tool chains to be arm-none-linux-gnueabi- set. +CC = arm-none-linux-gnueabi-gcc +CPP = arm-none-linux-gnueabi-g++ +STRIP = arm-none-linux-gnueabi-strip +AR = arm-none-linux-gnueabi-ar +AS = arm-none-linux-gnueabi-as +LD = arm-none-linux-gnueabi-ld +endif + +ifeq ($(CONFIG_CC), OPTION_CC_ARM_GNUEABI) + +# set compile tool chains to be arm-linux-gnueabi- set. +CC = arm-linux-gnueabi-gcc +CPP = arm-linux-gnueabi-g++ +STRIP = arm-linux-gnueabi-strip +AR = arm-linux-gnueabi-ar +AS = arm-linux-gnueabi-as +LD = arm-linux-gnueabi-ld +endif + +ifeq ($(CONFIG_CC), OPTION_CC_ARM_OPENWRT_UCLIBCGNUEABI) + +# set compile tool chains to be arm-openwrt-linux-uclibcgnueabi- set. +CC = arm-openwrt-linux-uclibcgnueabi-gcc +CPP = arm-openwrt-linux-uclibcgnueabi-g++ +STRIP = arm-openwrt-linux-uclibcgnueabi-strip +AR = arm-openwrt-linux-uclibcgnueabi-ar +AS = arm-openwrt-linux-uclibcgnueabi-as +LD = arm-openwrt-linux-uclibcgnueabi-ld +endif + +ifeq ($(CONFIG_CC), OPTION_CC_ARM_OPENWRT_MUSLGNUEABI) + +# set compile tool chains to be arm-openwrt-linux-muslgnueabi- set. +CC = arm-openwrt-linux-muslgnueabi-gcc +CPP = arm-openwrt-linux-muslgnueabi-g++ +STRIP = arm-openwrt-linux-muslgnueabi-strip +AR = arm-openwrt-linux-muslgnueabi-ar +AS = arm-openwrt-linux-muslgnueabi-as +LD = arm-openwrt-linux-muslgnueabi-ld +endif + +ifeq ($(CONFIG_CC), OPTION_CC_UCLIBC_ARM926_STATIC) + +# set compile tool chains to be arm-buildroot-linux-uclibcgnueabi- set. +CC = arm-buildroot-linux-uclibcgnueabi-gcc +CPP = arm-buildroot-linux-uclibcgnueabi-g++ +STRIP = arm-buildroot-linux-uclibcgnueabi-strip +AR = arm-buildroot-linux-uclibcgnueabi-ar +AS = arm-buildroot-linux-uclibcgnueabi-as +LD = arm-buildroot-linux-uclibcgnueabi-ld +endif + +ifeq ($(CONFIG_CC), OPTION_CC_X86_ANDROID) + +# set compile tool chains to be i686-linux-android- set. +CC = i686-linux-android-gcc +CPP = i686-linux-android-g++ +STRIP = i686-linux-android-strip +AR = i686-linux-android-ar +AS = i686-linux-android-as +LD = i686-linux-android-ld +endif +########## set out lib path +LIBPATH = /home/${USER}/libs_for_linux + +########## set build objects path +BUILDPATH = ./build + +########## set extra compile flag +EXTRA_FLAG = -g diff --git a/platform/mcu/xr871/src/cedarx/CdxEncPlugin/include/aencoder.h b/platform/mcu/xr871/src/cedarx/CdxEncPlugin/include/aencoder.h new file mode 100644 index 0000000000..8c23262c92 --- /dev/null +++ b/platform/mcu/xr871/src/cedarx/CdxEncPlugin/include/aencoder.h @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2008-2016 Allwinner Technology Co. Ltd. + * All rights reserved. + * + * File : aencoder.h + * Description : audio encoder api + * History : + * + */ + +#ifndef AUDIO_ENCODER_H +#define AUDIO_ENCODER_H + +#ifdef __cplusplus +extern "C" { +#endif + +#define AMR_ENCODER (1) +#define PCM_ENCODER (1) +#define OTHER_ENCODER (0) + +typedef enum __AUDIO_ENC_RESULT +{ + ERR_AUDIO_ENC_ABSEND = -2, + ERR_AUDIO_ENC_UNKNOWN = -1, + ERR_AUDIO_ENC_NONE = 0, //decode successed, no error + ERR_AUDIO_ENC_PCMUNDERFLOW = 1, //pcm data is underflow + ERR_AUDIO_ENC_OUTFRAME_UNDERFLOW = 2, //out frame buffer is underflow. + ERR_AUDIO_ENC_ +} __audio_enc_result_t; + + +enum AUDIO_ENCODER_TYPE +{ +#if OTHER_ENCODER + AUDIO_ENCODER_AAC_TYPE, + AUDIO_ENCODER_LPCM_TYPE, //only used by mpeg2ts + AUDIO_ENCODER_MP3_TYPE, +#endif +#if PCM_ENCODER + AUDIO_ENCODER_PCM_TYPE, +#endif +#if AMR_ENCODER + AUDIO_ENCODER_AMR_TYPE, +#endif +}; + +typedef enum AUDIO_ENCODER_TYPE AUDIO_ENCODER_TYPE; + +typedef struct AudioEncConfig +{ + AUDIO_ENCODER_TYPE nType; + int nInSamplerate; //fs + int nInChan; //pcm chan 1:mon 2:stereo + int nBitrate; //bs + int nSamplerBits; //only for 16bits + int nOutSamplerate; //fs,now OutSamplerate must equal InSamplerate + int nOutChan; // chan + + //for aac: 0:add head,1:raw data; + //for pcm: 2:mpegTs pcm(big endian), + //other: common pcm(little endian) + int nFrameStyle; +}AudioEncConfig; + +typedef void* AudioEncoder; + +AudioEncoder* CreateAudioEncoder(); +void DestroyAudioEncoder(AudioEncoder* pEncoder); +int InitializeAudioEncoder(AudioEncoder *pEncoder, AudioEncConfig *pConfig); +int ResetAudioEncoder(AudioEncoder* pEncoder); +int EncodeAudioStream(AudioEncoder *pEncoder); +int WriteAudioStreamBuffer(AudioEncoder *pEncoder, char* pBuf, int len); +int RequestAudioFrameBuffer(AudioEncoder *pEncoder, char **pOutBuf, + unsigned int *size, long long *pts, int *bufId); +int ReturnAudioFrameBuffer(AudioEncoder *pEncoder, char *pOutBuf, + unsigned int size, long long pts, int bufId); + + +#ifdef __cplusplus +} +#endif + +#endif // AUDIO_ENC_API_H diff --git a/platform/mcu/xr871/src/cedarx/CdxEncPlugin/include/log.h b/platform/mcu/xr871/src/cedarx/CdxEncPlugin/include/log.h new file mode 100644 index 0000000000..8235a22b45 --- /dev/null +++ b/platform/mcu/xr871/src/cedarx/CdxEncPlugin/include/log.h @@ -0,0 +1,108 @@ +#ifndef LOG_H +#define LOG_H +#ifndef LOG_TAG +#define LOG_TAG "AllwinnerAudioCodec" +#endif + +#include "cdx_malloc_dbg.h" + +#define OPTION_LOG_LEVEL_WARNING + +#if 0 +#ifdef OS_ANDROID + #include + + #define LOG_LEVEL_ERROR ANDROID_LOG_ERROR + #define LOG_LEVEL_WARNING ANDROID_LOG_WARN + #define LOG_LEVEL_INFO ANDROID_LOG_INFO + #define LOG_LEVEL_VERBOSE ANDROID_LOG_VERBOSE + #define LOG_LEVEL_DEBUG ANDROID_LOG_DEBUG + + #define AWLOG(level, fmt, arg...) \ + LOG_PRI(level, LOG_TAG, "<%s:%u>: "fmt, __FUNCTION__, __LINE__, ##arg) + +#elif (defined OS_LINUX) + #include + #include + + #define LOG_LEVEL_ERROR "error " + #define LOG_LEVEL_WARNING "warning" + #define LOG_LEVEL_INFO "info " + #define LOG_LEVEL_VERBOSE "verbose" + #define LOG_LEVEL_DEBUG "debug " + + #define AWLOG(level, fmt, arg...) \ + printf("%s: %s <%s:%u>: "fmt"\n", level, LOG_TAG, __FUNCTION__, __LINE__, ##arg) +#else + #error "invalid configuration of os." +#endif +#endif + + + + #ifndef LOG_LEVEL_ERROR + #define LOG_LEVEL_ERROR "error " + #endif + #ifndef LOG_LEVEL_WARNING + #define LOG_LEVEL_WARNING "warning" + #endif + #ifndef LOG_LEVEL_INFO + #define LOG_LEVEL_INFO "info " + #endif + #ifndef LOG_LEVEL_VERBOSE + #define LOG_LEVEL_VERBOSE "verbose" + #endif + #ifndef LOG_LEVEL_DEBUG + #define LOG_LEVEL_DEBUG "debug " + #endif + +int wrap_printf(const char *fmt, ...); +#define AWLOG(level, fmt, arg...) \ + wrap_printf("%s: %s <%s:%u>: "fmt"\n", level, LOG_TAG, __func__, __LINE__, ##arg) + +void __printf_time(const char *f, unsigned int l); +#define printf_time() __printf_time(__func__, __LINE__) + +#define logd(fmt, arg...) //AWLOG(LOG_LEVEL_DEBUG, fmt, ##arg) + +#ifdef OPTION_LOG_LEVEL_CLOSE + +#define loge(fmt, arg...) +#define logw(fmt, arg...) +#define logi(fmt, arg...) +#define logv(fmt, arg...) + +#elif (defined OPTION_LOG_LEVEL_ERROR) + +#define loge(fmt, arg...) AWLOG(LOG_LEVEL_ERROR, "\033[40;31m"fmt"\033[0m", ##arg) +#define logw(fmt, arg...) +#define logi(fmt, arg...) +#define logv(fmt, arg...) + +#elif (defined OPTION_LOG_LEVEL_WARNING) + +#define loge(fmt, arg...) AWLOG(LOG_LEVEL_ERROR, "\033[40;31m"fmt"\033[0m", ##arg) +#define logw(fmt, arg...) AWLOG(LOG_LEVEL_WARNING, fmt, ##arg) +#define logi(fmt, arg...) +#define logv(fmt, arg...) + +#elif (defined OPTION_LOG_LEVEL_DEFAULT) + +#define loge(fmt, arg...) AWLOG(LOG_LEVEL_ERROR, "\033[40;31m"fmt"\033[0m", ##arg) +#define logw(fmt, arg...) AWLOG(LOG_LEVEL_WARNING, fmt, ##arg) +#define logi(fmt, arg...) AWLOG(LOG_LEVEL_INFO, fmt, ##arg) +#define logv(fmt, arg...) + +#elif (defined OPTION_LOG_LEVEL_DETAIL) + +#define loge(fmt, arg...) AWLOG(LOG_LEVEL_ERROR, "\033[40;31m"fmt"\033[0m", ##arg) +#define logw(fmt, arg...) AWLOG(LOG_LEVEL_WARNING, fmt, ##arg) +#define logi(fmt, arg...) AWLOG(LOG_LEVEL_INFO, fmt, ##arg) +#define logv(fmt, arg...) AWLOG(LOG_LEVEL_VERBOSE, fmt, ##arg) + +#else + #error "invalid configuration of debug level." +#endif + +#endif + diff --git a/platform/mcu/xr871/src/cedarx/CdxEncPlugin/mp3encApi.h b/platform/mcu/xr871/src/cedarx/CdxEncPlugin/mp3encApi.h new file mode 100644 index 0000000000..c0be828b1f --- /dev/null +++ b/platform/mcu/xr871/src/cedarx/CdxEncPlugin/mp3encApi.h @@ -0,0 +1,18 @@ + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef __MP3_ENC_LIB_API__ +#define __MP3_ENC_LIB_API__ + +#include + +extern struct __AudioENC_AC320 *AudioMP3ENCEncInit(void); +extern int AudioMP3ENCEncExit(struct __AudioENC_AC320 *p); + +#endif // __MP3_ENC_LIB_API__ + +#ifdef __cplusplus +} +#endif diff --git a/platform/mcu/xr871/src/cedarx/CdxEncPlugin/pcm_enc.h b/platform/mcu/xr871/src/cedarx/CdxEncPlugin/pcm_enc.h new file mode 100644 index 0000000000..557cdc7e2a --- /dev/null +++ b/platform/mcu/xr871/src/cedarx/CdxEncPlugin/pcm_enc.h @@ -0,0 +1,16 @@ +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef PCM_ENC_H +#define PCM_ENC_H +#include "aenc_sw_lib.h" + +extern struct __AudioENC_AC320 *AudioPCMEncInit(); +extern int AudioPCMEncExit(struct __AudioENC_AC320 *p); + +#endif /* PCM_ENC_H */ + +#ifdef __cplusplus +} +#endif diff --git a/platform/mcu/xr871/src/cedarx/Makefile b/platform/mcu/xr871/src/cedarx/Makefile new file mode 100644 index 0000000000..d573828e41 --- /dev/null +++ b/platform/mcu/xr871/src/cedarx/Makefile @@ -0,0 +1,28 @@ +# +# Rules for building library +# + +# ---------------------------------------------------------------------------- +# common rules +# ---------------------------------------------------------------------------- +ROOT_PATH := ../.. + +include $(ROOT_PATH)/gcc.mk + +# ---------------------------------------------------------------------------- +# library and objects +# ---------------------------------------------------------------------------- +LIBS := libcedarx.a + +DIRS := $(shell find . -maxdepth 4 -type d) +DIRS += . + +SRCS := $(basename $(foreach dir,$(DIRS),$(wildcard $(dir)/*.[csS]))) + +OBJS := $(addsuffix .o,$(SRCS)) + +# extra include path +INCLUDE_PATHS += $(foreach dir, $(DIRS), -I$(dir)) + +# library make rules +include $(LIB_MAKE_RULES) diff --git a/platform/mcu/xr871/src/cedarx/awrecorder/AudioEncodeComponent.h b/platform/mcu/xr871/src/cedarx/awrecorder/AudioEncodeComponent.h new file mode 100644 index 0000000000..97756001f3 --- /dev/null +++ b/platform/mcu/xr871/src/cedarx/awrecorder/AudioEncodeComponent.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2008-2016 Allwinner Technology Co. Ltd. + * All rights reserved. + * + * File : AudioEncodeComponent.h + * Description : AudioEncodeComponent + * History : + * + */ + +#ifndef AUDIO_ENCODE_COMPONENT_H +#define AUDIO_ENCODE_COMPONENT_H + +#include "aencoder.h" +#include "awencoder.h" + +typedef void* AudioEncodeComp; + + +enum AUDIOENCODERNOTIFY +{ + AUDIO_ENCODE_NOTIFY_ERROR, + AUDIO_ENCODE_NOTIFY_CRASH, + AUDIO_ENCODE_NOTIFY_EOS, +}; + +typedef int (*AudioEncodeCallback)(void* pUserData, int eMessageId, void* param); +AudioEncodeComp* AudioEncodeCompCreate(); +int AudioEncodeCompInit(AudioEncodeComp* p, AudioEncodeConfig* config); +void AudioEncodeCompDestory(AudioEncodeComp* p); +int AudioEncodeCompStart(AudioEncodeComp *p); +int AudioEncodeCompStop(AudioEncodeComp *p); +int AudioEncodeCompReset(AudioEncodeComp* p); +int AudioEncodeCompInputBuffer(AudioEncodeComp *p, AudioInputBuffer *buf); +int AudioEncodeCompRequestBuffer(AudioEncodeComp *p, AudioEncOutBuffer *bufInfo); +int AudioEncodeCompReturnBuffer(AudioEncodeComp *p, AudioEncOutBuffer *bufInfo); +int AudioEncodeCompSetCallback(AudioEncodeComp *p, AudioEncodeCallback notifier, void* pUserData); +#endif diff --git a/platform/mcu/xr871/src/cedarx/awrecorder/EncDataComponent.h b/platform/mcu/xr871/src/cedarx/awrecorder/EncDataComponent.h new file mode 100644 index 0000000000..a3d3869a36 --- /dev/null +++ b/platform/mcu/xr871/src/cedarx/awrecorder/EncDataComponent.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2008-2016 Allwinner Technology Co. Ltd. + * All rights reserved. + * + * File : EncDataComponent.h + * Description : EncDataComponent + * History : + * + */ + +#ifndef __MUX_COMP_H__ +#define __MUX_COMP_H__ + +#include "CdxMuxer.h" +#include "AudioEncodeComponent.h" +//#include "VideoEncodeComponent.h" + +enum MUXERNOTIFY +{ + ENCDATA_NOTIFY_ERROR, + ENCDATA_NOTIFY_ENCODE_PACKET, +}; + +typedef void* EncDataComp; +typedef int (*MuxerCallback)(void* pUserData, int eMessageId, void* param); + +EncDataComp* EncDataCompCreate(void * app); +int EncDataCompInit(EncDataComp *v,VideoEncodeConfig *videoConfig, + AudioEncodeConfig *audioConfig, EncDataCallBackOps *ops); +void EncDataCompDestory(EncDataComp* p); +int EncDataCompStart(EncDataComp *p); +int EncDataCompStop(EncDataComp *p); +int EncDataCompReset(EncDataComp *p); +int EncDataCompSetAudioEncodeComp(EncDataComp *p, AudioEncodeComp* pAudioEncoder); +#if VIDEO_SUPPORT +int EncDataCompSetVideoEncodeComp(EncDataComp *p, VideoEncodeComp* pVideoEncoder); +#endif +int EncDataCompSetCallback(EncDataComp* v, MuxerCallback callback, void* pUserData); + +#endif /* __VIDEO_RESIZER_MUXER_H__ */ + diff --git a/platform/mcu/xr871/src/cedarx/awrecorder/Makefile.am b/platform/mcu/xr871/src/cedarx/awrecorder/Makefile.am new file mode 100644 index 0000000000..160ebb9ba6 --- /dev/null +++ b/platform/mcu/xr871/src/cedarx/awrecorder/Makefile.am @@ -0,0 +1,31 @@ + +include $(top_srcdir)/Makefile.inc + +lib_LTLIBRARIES = libawrecorder.la + +libawrecorder_la_SOURCES = AudioEncodeComponent.c \ + awencoder.c \ + EncDataComponent.c \ + VideoEncodeComponent.c + +libawrecorder_la_CFLAGS = $(CFLAGS_CDXG) +libawrecorder_la_CPPFLAGS = $(CPPFLAGS_CDXG) +LOCAL_INCLUDE = -I$(top_srcdir) \ + -I$(top_srcdir)/libcore/include \ + -I$(top_srcdir)/libcore/base/include \ + -I$(top_srcdir)/libcore/muxer/include \ + -I$(top_srcdir)/external/include/aencoder \ + -I$(top_srcdir)/../libcedarc/include + +libawrecorder_la_CFLAGS += $(LOCAL_INCLUDE) +libawrecorder_la_CPPFLAGS += $(LOCAL_INCLUDE) + +libawrecorder_la_LDFLAGS = $(LDFLAGS_CDXG) + +libawrecorder_la_LIBADD = \ + $(top_srcdir)/libcore/base/libcdx_base.la \ + $(top_srcdir)/libcore/muxer/base/libcdx_muxer.la + +LOCAL_LIB = -lpthread -dl -lcdc_vencoder -laencoder -lcdc_memory +libawrecorder_la_LDFLAGS += $(LOCAL_LIB) + diff --git a/platform/mcu/xr871/src/cedarx/awrecorder/awencoder.h b/platform/mcu/xr871/src/cedarx/awrecorder/awencoder.h new file mode 100644 index 0000000000..c3c1f36ffc --- /dev/null +++ b/platform/mcu/xr871/src/cedarx/awrecorder/awencoder.h @@ -0,0 +1,171 @@ +/* + * Copyright (c) 2008-2016 Allwinner Technology Co. Ltd. + * All rights reserved. + * + * File : awencoder.h + * Description : encoder + * History : + * + */ + +#ifndef __AW_RECORDER_H__ +#define __AW_RECORDER_H__ + +//#include +//#include +#include +//#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define AMR_ENCODER (1) +#define PCM_ENCODER (1) +#define OTHER_ENCODER (0) + +typedef void* AwEncoder; + +typedef enum AwEncoderIndexType +{ + AwEncoder_SetFrameRate = 0x0, + AwEncoder_SetBitRate, + +}AwEncoderIndexType; + +enum ERECORDERINTERNALNOTIFY //* +{ + AWENCODER_VIDEO_ENCODER_NOTIFY_STREAM_UNDERFLOW = 32, + AWENCODER_VIDEO_ENCODER_NOTIFY_CRASH, + AWENCODER_VIDEO_ENCODER_NOTIFY_EOS, + AWENCODER_VIDEO_ENCODER_NOTIFY_OUT_BUFFER, + AWENCODER_VIDEO_ENCODER_NOTIFY_RETURN_BUFFER, + + AWENCODER_AUDIO_ENCODER_NOTIFY_STREAM_UNDERFLOW = 96, + AWENCODER_AUDIO_ENCODER_NOTIFY_CRASH, + AWENCODER_AUDIO_ENCODER_NOTIFY_EOS, + AWENCODER_AUDIO_ENCODER_NOTIFY_OUT_BUFFER, + + AWENCODER_MUXER_NOTIFY_FIRST_FRAME = 128, + AWENCODER_MUXER_NOTIFY_EOS, + AWENCODER_MUXER_NOTIFY_PTS_AND_CACHETIME, +}; + +typedef enum VIDEO_ENCODE_TYPE +{ + VIDEO_ENCODE_H264, + VIDEO_ENCODE_JPEG, +}VIDEO_ENCODE_TYPE; + +typedef enum AUDIO_ENCODE_TYPE +{ +#if PCM_ENCODER + AUDIO_ENCODE_PCM_TYPE, +#endif +#if OTHER_ENCODER + AUDIO_ENCODE_AAC_TYPE, + AUDIO_ENCODE_LPCM_TYPE, //only used by mpeg2ts + AUDIO_ENCODE_MP3_TYPE, +#endif +#if AMR_ENCODER + AUDIO_ENCODE_AMR_TYPE, +#endif +}AUDIO_ENCODE_TYPE; + +typedef struct AudioEncodeConfig +{ + AUDIO_ENCODE_TYPE nType; + int nInSamplerate; //??fs + int nInChan; //??pcm chan 1:mon 2:stereo + int nBitrate; //bs + int nSamplerBits; //only for 16bits + int nOutSamplerate; //?3?fs,now OutSamplerate must equal InSamplerate + int nOutChan; //???3? chan + //for aac: 0:add head,1:raw data; + //for pcm: 2:mpegTs pcm(big endian), + //other: common pcm(little endian) + int nFrameStyle; +}AudioEncodeConfig; + +typedef struct VideoEncodeConfig +{ + VIDEO_ENCODE_TYPE nType; + int nFrameRate; + int nBitRate; + int nOutWidth; + int nOutHeight; + int nSrcFrameRate; + int nSrcWidth; + int nSrcHeight; + int bUsePhyBuf; +}VideoEncodeConfig; + +typedef struct VideoInputBuffer +{ + unsigned char* pData; //virtual data buff + int nLen; //data len + long long nPts; //pts + + unsigned long nID; + unsigned char* pAddrPhyY; + unsigned char* pAddrPhyC; +}VideoInputBuffer; + +typedef struct AudioInputBuffer +{ + char *pData; //data buff + int nLen; //data len + long long nPts; //pts +}AudioInputBuffer; + +typedef struct VideoOutBuffer +{ + unsigned int nSize0; + unsigned int nSize1; + unsigned char* pData0; + unsigned char* pData1; +}VideoOutBuffer; + +typedef struct AudioEncOutBuffer +{ + char *pBuf; + unsigned int len; + long long pts; + int bufId; +}AudioEncOutBuffer; + +typedef struct EncDataCallBackOps +{ + int (*onVideoDataEnc)(void *app,CdxMuxerPacketT *buff); + int (*onAudioDataEnc)(void *app,CdxMuxerPacketT *buff); +} EncDataCallBackOps; + +typedef void (*EncoderNotifyCallback)(void* pUserData, int msg, void* param); + +AwEncoder* AwEncoderCreate(void * app); +void AwEncoderDestory(AwEncoder* p); +int AwEncoderInit(AwEncoder* p, + VideoEncodeConfig *videoConfig, + AudioEncodeConfig *audioConfig, + EncDataCallBackOps *ops); + +int AwEncoderGetExtradata(AwEncoder* v, unsigned char** buf, unsigned int* length); +int AwEncoderSetParamete(AwEncoder* p, AwEncoderIndexType nIndexType, void* para); +int AwEncoderStart(AwEncoder* p); +int AwEncoderStop(AwEncoder* p); +int AwEncoderReset(AwEncoder* p); + +int AwEncoderWriteYUVdata(AwEncoder* p, VideoInputBuffer* buf); +int AwEncoderWritePCMdata(AwEncoder* p, AudioInputBuffer* buf); + +int AwEncoderSetNotifyCallback(AwEncoder* p, + EncoderNotifyCallback notifier, + void* pUserData); + +#ifdef __cplusplus +} +#endif + +#endif /* __RECORDER_H__ */ + diff --git a/platform/mcu/xr871/src/cedarx/base/include/AwMessageQueue.h b/platform/mcu/xr871/src/cedarx/base/include/AwMessageQueue.h new file mode 100644 index 0000000000..922e6e65f8 --- /dev/null +++ b/platform/mcu/xr871/src/cedarx/base/include/AwMessageQueue.h @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2008-2016 Allwinner Technology Co. Ltd. + * All rights reserved. + * + * File : CdxMessageQueue.h + * Description : message queue + * History : + * + */ + +#ifndef MESSAGE_QUEUE_H +#define MESSAGE_QUEUE_H + +#include +//#include +#include "pthread.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct MessageQueueContext AwMessageQueue; + +#ifndef uintptr_t +typedef size_t uintptr_t; +#endif + +typedef struct AwMessage AwMessage; +typedef void (*msgHandlerT)(AwMessage *msg, void *arg); + +/* Define your own struct AwMessage and put AWMESSAGE_COMMON_MEMBERS at the + * beginning + */ +#define AWMESSAGE_COMMON_MEMBERS \ + int messageId; \ + msgHandlerT execute; \ + +/** + * @param nMaxMessageNum How many messages the message queue can hold + * @param pName The name of the message queue which is used in log output + * @param nMessageSize sizeof(struct AwMessage) + */ +AwMessageQueue* AwMessageQueueCreate__(int nMaxMessageNum, const char* pName, + size_t nMessageSize); +#define AwMessageQueueCreate(nMaxMessageNum, pName) \ + AwMessageQueueCreate__(nMaxMessageNum, pName, sizeof(AwMessage)) + +void AwMessageQueueDestroy(AwMessageQueue* mq); + +int AwMessageQueuePostMessage(AwMessageQueue* mq, AwMessage* m); + +int AwMessageQueueWaitMessage(AwMessageQueue* mq, int64_t timeout); + +int AwMessageQueueGetMessage(AwMessageQueue* mq, AwMessage* m); + +int AwMessageQueueTryGetMessage(AwMessageQueue* mq, AwMessage* m, int64_t timeout); + +int AwMessageQueueFlush(AwMessageQueue* mq); + +int AwMessageQueueGetCount(AwMessageQueue* mq); + +//* define a semaphore timedwait method for common use. +int SemTimedWait(sem_t* sem, int64_t time_ms); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/platform/mcu/xr871/src/cedarx/base/include/AwPool.h b/platform/mcu/xr871/src/cedarx/base/include/AwPool.h new file mode 100644 index 0000000000..225bbd6207 --- /dev/null +++ b/platform/mcu/xr871/src/cedarx/base/include/AwPool.h @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2008-2016 Allwinner Technology Co. Ltd. + * All rights reserved. + * + * File : AwPool.h + * Description : Pool + * History : + * + */ + +#ifndef AW_POOL_H +#define AW_POOL_H + +#include +#include + +typedef struct AwPoolS AwPoolT; + +#define Palloc(pool, size) \ + AwPalloc(pool, size, __FILE__, __LINE__) + +#define Pfree(pool, p) \ + AwPfree(pool, p) + +#define Pstrdup(pool, str) \ + AwStrDup(pool, str, __FILE__, __LINE__) + +#define Prealloc(pool, p, size) \ + AwRealloc(pool, p, size, __FILE__, __LINE__) + +#ifdef __cplusplus +extern "C" +{ +#endif + +AwPoolT *__AwPoolCreate(AwPoolT *father, char *file, int line); + +#define AwPoolCreate(father) __AwPoolCreate(father, __FILE__, __LINE__) + +void AwPoolDestroy(AwPoolT *pool); + +void *AwPalloc(AwPoolT *pool, int size, char *file, int line); + +void AwPfree(AwPoolT *pool, void *p); + +void AwPoolReset(void); + +static inline char *AwStrDup(AwPoolT *pool, const char *str, char *file, int line) +{ + char *ret = NULL; + int strLen = 0; +// CDX_CHECK(str); + + strLen = strlen(str); + + ret = (char *)AwPalloc(pool, strLen + 1, file, line); + strcpy(ret, str); + ret[strLen] = 0; + return ret; +} + +void *AwRealloc(AwPoolT *pool, void *p, int size, char *file, int line); + +#ifdef __cplusplus +} +#endif + +#endif /* AW_POOL_H */ + diff --git a/platform/mcu/xr871/src/cedarx/base/include/CdxAtomic.h b/platform/mcu/xr871/src/cedarx/base/include/CdxAtomic.h new file mode 100644 index 0000000000..5c086f12f5 --- /dev/null +++ b/platform/mcu/xr871/src/cedarx/base/include/CdxAtomic.h @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2008-2016 Allwinner Technology Co. Ltd. + * All rights reserved. + * + * File : CdxAtomic.h + * Description : Atomic + * History : + * + */ + +#ifndef CDX_ATOMIC_H +#define CDX_ATOMIC_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + volatile cdx_ssize counter; +} cdx_atomic_t; + +/*ü+ 1 ؽֵ*/ +static inline cdx_int32 CdxAtomicInc(cdx_atomic_t *ref) +{ + return __sync_add_and_fetch(&ref->counter, 1L); +} + +static inline cdx_int32 CdxAtomicAdd(cdx_atomic_t *ref, cdx_ssize val) +{ + return __sync_add_and_fetch(&ref->counter, val); +} + +/*ü- 1 ؽֵ*/ +static inline cdx_int32 CdxAtomicDec(cdx_atomic_t *ref) +{ + return __sync_sub_and_fetch(&ref->counter, 1L); +} + +static inline cdx_int32 CdxAtomicSub(cdx_atomic_t *ref, cdx_ssize val) +{ + return __sync_sub_and_fetch(&ref->counter, val); +} + +/*üΪvalǰֵ*/ +static inline cdx_int32 CdxAtomicSet(cdx_atomic_t *ref, cdx_ssize val) +{ + return __sync_lock_test_and_set(&ref->counter, val); +} + +/*ȡüֵ*/ +static inline cdx_int32 CdxAtomicRead(cdx_atomic_t *ref) +{ + return __sync_or_and_fetch(&ref->counter, 0L); +} + +static inline cdx_bool CdxAtomicCAS(cdx_atomic_t *ref, cdx_ssize oldVal, cdx_ssize newVal) +{ + return __sync_bool_compare_and_swap(&ref->counter, oldVal, newVal); +} + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/platform/mcu/xr871/src/cedarx/base/include/CdxBase64.h b/platform/mcu/xr871/src/cedarx/base/include/CdxBase64.h new file mode 100644 index 0000000000..cb96dedcc1 --- /dev/null +++ b/platform/mcu/xr871/src/cedarx/base/include/CdxBase64.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2008-2016 Allwinner Technology Co. Ltd. + * All rights reserved. + * + * File : CdxBase64.h + * Description : Base64 + * History : + * + */ + +#ifndef CDX_BASE_64_H +#define CDX_BASE_64_H +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +CdxBufferT *CdxDecodeBase64(AwPoolT *pool, const cdx_char *s); + +cdx_char *CdxEncodeBase64(AwPoolT *pool, const void *_data, cdx_size size); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/platform/mcu/xr871/src/cedarx/base/include/CdxBinary.h b/platform/mcu/xr871/src/cedarx/base/include/CdxBinary.h new file mode 100644 index 0000000000..620a3c83b5 --- /dev/null +++ b/platform/mcu/xr871/src/cedarx/base/include/CdxBinary.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2008-2016 Allwinner Technology Co. Ltd. + * All rights reserved. + * + * File : CdxBinary.h + * Description : Binary + * History : + * + */ + +#ifndef CDX_BINARY_H +#define CDX_BINARY_H +#include + +static inline cdx_uint32 GetU8(const cdx_uint8 *data) +{ + return (cdx_uint32)(*data); +} + +static inline cdx_uint32 GetLE16(const cdx_uint8 *data) +{ + return GetU8(data) | (GetU8(data + 1) << 8); +} + +static inline cdx_uint32 GetLE32(const cdx_uint8 *data) +{ + return GetLE16(data) | (GetLE16(data + 2) << 16); +} + +static inline cdx_uint64 GetLE64(const cdx_uint8 *data) +{ + return ((cdx_uint64)GetLE32(data)) | (((cdx_uint64)GetLE32(data + 4)) << 32); +} + +static inline cdx_uint32 GetBE16(const cdx_uint8 *data) +{ + return (GetU8(data) << 8) | GetU8(data + 1); +} + +static inline cdx_uint32 GetBE32(const cdx_uint8 *data) +{ + return (GetBE16(data) << 16) | GetBE16(data + 2); +} + +static inline cdx_uint64 GetBE64(const cdx_uint8 *data) +{ + return (((cdx_uint64)GetBE32(data)) << 32) | ((cdx_uint64)GetBE32(data + 4)); +} + +#endif diff --git a/platform/mcu/xr871/src/cedarx/base/include/CdxBitReader.h b/platform/mcu/xr871/src/cedarx/base/include/CdxBitReader.h new file mode 100644 index 0000000000..8f9def153d --- /dev/null +++ b/platform/mcu/xr871/src/cedarx/base/include/CdxBitReader.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2008-2016 Allwinner Technology Co. Ltd. + * All rights reserved. + * + * File : CdxBitReader.h + * Description : BitReader + * History : + * + */ + +#ifndef CDX_BIT_READER_H +#define CDX_BIT_READER_H + +#include + +typedef struct CdxBitReaderS CdxBitReaderT; + +struct CdxBitReaderOpsS +{ + void (*destroy)(CdxBitReaderT *); + + cdx_uint32 (*getBits)(CdxBitReaderT *, cdx_uint32); + + void (*skipBits)(CdxBitReaderT *, cdx_uint32); + + void (*putBits)(CdxBitReaderT *, cdx_uint32, cdx_uint32); + + cdx_uint32 (*numBitsLeft)(CdxBitReaderT *) ; + + const cdx_uint8 *(*data)(CdxBitReaderT *); +}; + +struct CdxBitReaderS +{ + struct CdxBitReaderOpsS *ops; +}; + +#ifdef __cplusplus +extern "C" +{ +#endif + +CdxBitReaderT *CdxBitReaderCreate(const cdx_uint8 *data, cdx_uint32 size); +void CdxBitReaderDestroy(struct CdxBitReaderS *br); +cdx_uint32 CdxBitReaderGetBits(struct CdxBitReaderS *br, cdx_uint32 n); +void CdxBitReaderSkipBits(struct CdxBitReaderS *br, cdx_uint32 n); +void CdxBitReaderPutBits(struct CdxBitReaderS *br, cdx_uint32 x, cdx_uint32 n); +cdx_uint32 CdxBitReaderNumBitsLeft(struct CdxBitReaderS *br) ; +const cdx_uint8 *CdxBitReaderData(struct CdxBitReaderS *br); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/platform/mcu/xr871/src/cedarx/base/include/CdxBuffer.h b/platform/mcu/xr871/src/cedarx/base/include/CdxBuffer.h new file mode 100644 index 0000000000..e8c0b2185c --- /dev/null +++ b/platform/mcu/xr871/src/cedarx/base/include/CdxBuffer.h @@ -0,0 +1,185 @@ +/* + * Copyright (c) 2008-2016 Allwinner Technology Co. Ltd. + * All rights reserved. + * + * File : CdxBuffer.h + * Description : Buffer + * History : + * + */ + +#ifndef CDX_BUFFER_H +#define CDX_BUFFER_H +#include +#include +#include +#include +#include + +/* + *溯ʧܵʱ򣬶Ӧbuf ǺϷ + *ԼʹãȻҲҪͷš + */ + +struct CdxBufferItemS +{ + CdxBufferT *val; + CdxListNodeT node; +}; + +struct CdxBufferOpsS +{ + CdxBufferT *(*incRef)(CdxBufferT *); + void (*decRef)(CdxBufferT *); + cdx_uint8 *(*getData)(CdxBufferT *); + cdx_uint8 *(*getBase)(CdxBufferT *); + cdx_uint32 (*getSize)(CdxBufferT *); + void (*append)(CdxBufferT *, const cdx_uint8 * /*data*/, cdx_uint32 /*len*/ ); + void (*insert)(CdxBufferT *, const cdx_uint8 * /*data*/, cdx_uint32 /*len*/ ); + void (*setRange)(CdxBufferT *, cdx_uint32 /*offset*/, cdx_uint32/*len*/); + void (*seekRange)(CdxBufferT *, cdx_int32 /*left*/, cdx_int32/*right*/); + CdxMetaT *(*getMeta)(CdxBufferT *); +}; + +struct CdxBufferS +{ + struct CdxBufferOpsS *ops; +}; + +static inline CdxBufferT *CdxBufferIncRef(CdxBufferT *buf) +{ + CDX_CHECK(buf); + CDX_CHECK(buf->ops); + CDX_CHECK(buf->ops->incRef); + return buf->ops->incRef(buf); +} + +static inline void CdxBufferDecRef(CdxBufferT *buf) +{ + CDX_CHECK(buf); + CDX_CHECK(buf->ops); + CDX_CHECK(buf->ops->decRef); + return buf->ops->decRef(buf); +} + +static inline cdx_uint8 *CdxBufferGetData(CdxBufferT *buf) +{ + CDX_CHECK(buf); + CDX_CHECK(buf->ops); + CDX_CHECK(buf->ops->getData); + return buf->ops->getData(buf); +} + +static inline cdx_uint8 *CdxBufferGetBase(CdxBufferT *buf) +{ + CDX_CHECK(buf); + CDX_CHECK(buf->ops); + CDX_CHECK(buf->ops->getBase); + return buf->ops->getBase(buf); +} + +static inline cdx_uint32 CdxBufferGetSize(CdxBufferT *buf) +{ + CDX_CHECK(buf); + CDX_CHECK(buf->ops); + CDX_CHECK(buf->ops->getSize); + return buf->ops->getSize(buf); +} + +static inline void CdxBufferAppend(CdxBufferT *buf, const cdx_uint8 *data, + cdx_uint32 len) +{ + CDX_CHECK(buf); + CDX_CHECK(buf->ops); + CDX_CHECK(buf->ops->append); + buf->ops->append(buf, data, len); +} + +static inline void CdxBufferInsert(CdxBufferT *buf, + cdx_uint8 *data, cdx_uint32 len) +{ + CDX_CHECK(buf); + CDX_CHECK(buf->ops); + CDX_CHECK(buf->ops->insert); + buf->ops->insert(buf, data, len); +} + +static inline CdxMetaT *CdxBufferGetMeta(CdxBufferT *buf) +{ + CDX_CHECK(buf); + CDX_CHECK(buf->ops); + CDX_CHECK(buf->ops->getMeta); + return buf->ops->getMeta(buf); +} + +static inline void CdxBufferSetRange(CdxBufferT *buf, + cdx_uint32 offset, cdx_uint32 len) +{ + CDX_CHECK(buf); + CDX_CHECK(buf->ops); + CDX_CHECK(buf->ops->setRange); + buf->ops->setRange(buf, offset, len); +} + +static inline void CdxBufferSeekRange(CdxBufferT *buf, + cdx_int32 left, cdx_int32 right) +{ + CDX_CHECK(buf); + CDX_CHECK(buf->ops); + CDX_CHECK(buf->ops->seekRange); + buf->ops->seekRange(buf, left, right); +} + +#define CdxBufferDump(buf) \ + CDX_BUF_DUMP(CdxBufferGetData(buf), CdxBufferGetSize(buf)) + +#define CdxBufferSetInt32(buf, name, val) \ + CdxMetaSetInt32(CdxBufferGetMeta(buf), name, val) + +#define CdxBufferFindInt32(buf, name, pVal) \ + CdxMetaFindInt32(CdxBufferGetMeta(buf), name, pVal) + +#define CdxBufferRemoveInt32(buf, name) \ + CdxMetaRemoveInt32(CdxBufferGetMeta(buf), name) + +#define CdxBufferSetInt64(buf, name, val) \ + CdxMetaSetInt64(CdxBufferGetMeta(buf), name, val) + +#define CdxBufferFindInt64(buf, name, pVal) \ + CdxMetaFindInt64(CdxBufferGetMeta(buf), name, pVal) + +typedef CdxBufferT CdxStringT; + +#define CdxStringCreate(pool) \ + CdxBufferCreate(pool, 1024, NULL, 0) + +#define CdxStringDestroy(str) \ + CdxBufferDestroy(str) + +static inline void CdxStringAppend(CdxStringT *str, cdx_char *val) +{ + CDX_CHECK(val); + CdxBufferAppend(str, (cdx_uint8 *)(val), strlen(val) + 1); + CdxBufferSetRange(str, 0, CdxBufferGetSize(str) - 1); +} + +#define CdxStringGet(str) \ + (cdx_char *)CdxBufferGetData(str) + +#define CdxBufferCreate(pool, capacity, initData, len) \ + __CdxBufferCreate(pool, capacity, initData, len, __FILE__, __LINE__) + +#ifdef __cplusplus +extern "C" { +#endif + +CdxBufferT *__CdxBufferCreate(AwPoolT *pool, cdx_uint32 capacity, cdx_uint8 *initData, + cdx_uint32 len, char *file, int line); + +void CdxBufferDestroy(CdxBufferT *buf); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif // CDX_BUFFER_H diff --git a/platform/mcu/xr871/src/cedarx/base/include/CdxDebug.h b/platform/mcu/xr871/src/cedarx/base/include/CdxDebug.h new file mode 100644 index 0000000000..9471cd7509 --- /dev/null +++ b/platform/mcu/xr871/src/cedarx/base/include/CdxDebug.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2008-2016 Allwinner Technology Co. Ltd. + * All rights reserved. + * + * File : CdxDebug.h + * Description : Debug + * History : + * + */ + +#ifndef CDX_DEBUG_H +#define CDX_DEBUG_H +#include +#ifdef LINUX_PLATFORM +#include +#endif + +#ifdef __cplusplus +extern "C" +{ +#endif + +#ifdef LINUX_PLATFORM +#define gettid() syscall(__NR_gettid) + +void CdxDumpThreadStack(pthread_t tid); + +void CdxCallStack(void); +#else + +static inline void CdxDumpThreadStack(pthread_t tid) { + +} + +static inline void CdxCallStack(pthread_t tid) { + +} +#endif +#ifdef __cplusplus +} +#endif + +#endif diff --git a/platform/mcu/xr871/src/cedarx/base/include/CdxEnumCommon.h b/platform/mcu/xr871/src/cedarx/base/include/CdxEnumCommon.h new file mode 100644 index 0000000000..2735ee5664 --- /dev/null +++ b/platform/mcu/xr871/src/cedarx/base/include/CdxEnumCommon.h @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2008-2016 Allwinner Technology Co. Ltd. + * All rights reserved. + * + * File : CdxEnumCommon.h + * Description : helper for some public enum type + * History : + * + */ + +#ifndef CDX_ENUM_COMMON_H +#define CDX_ENUM_COMMON_H + +enum SPLIT_PUBLIC_ENUM_RANGE { + MEDIA_EVENT_VALID_RANGE_MIN = 0, + MEDIA_EVENT_VALID_RANGE_MAX = 255, + + PARSER_NOTIFY_VALID_RANGE_MIN = 256, + PARSER_NOTIFY_VALID_RANGE_MAX = 500, + + DEMUX_NOTIFY_VALID_RANGE_MIN = 512, + DEMUX_NOTIFY_VALID_RANGE_MAX = 1000, + + PLAYBACK_NOTIFY_VALID_RANGE_MIN = 1024, + PLAYBACK_NOTIFY_VALID_RANGE_MAX = 1500, + + SUBTITLE_CALLBACK_VALID_RANGE_MIN = 1600, + SUBTITLE_CALLBACK_VALID_RANGE_MAX = 2000, + + STREAM_EVENT_VALID_RANGE_MIN = 4096, + STREAM_EVENT_VALID_RANGE_MAX = 4500, +}; + +#define CHECK_MEDIA_EVENT_MAX_VALID(x) \ + typedef char MEDIA_EVENT_MAX_VALID[(int)MEDIA_EVENT_VALID_RANGE_MAX - (int)x]; + +#define CHECK_PARSER_NOTIFY_MAX_VALID(x) \ + typedef char PARSER_NOTIFY_MAX_VALID[(int)PARSER_NOTIFY_VALID_RANGE_MAX - (int)x]; + +#define CHECK_DEMUX_NOTIFY_MAX_VALID(x) \ + typedef char DEMUX_NOTIFY_MAX_VALID[(int)DEMUX_NOTIFY_VALID_RANGE_MAX - (int)x]; + +#define CHECK_PLAYBACK_NOTIFY_MAX_VALID(x) \ + typedef char PLAYBACK_NOTIFY_MAX_VALID[(int)PLAYBACK_NOTIFY_VALID_RANGE_MAX - (int)x]; + +#define CHECK_SUBTITLE_CALLBACK_MAX_VALID(x) \ + typedef char SUBTITLE_CALLBACK_MAX_VALID[(int)SUBTITLE_CALLBACK_VALID_RANGE_MAX - (int)x]; + +#define CHECK_STREAM_EVENT_MAX_VALID(x) \ + typedef char STREAM_EVENT_MAX_VALID[(int)STREAM_EVENT_VALID_RANGE_MAX - (int)x]; + +#endif diff --git a/platform/mcu/xr871/src/cedarx/base/include/CdxISOLang.h b/platform/mcu/xr871/src/cedarx/base/include/CdxISOLang.h new file mode 100644 index 0000000000..6fd21724a8 --- /dev/null +++ b/platform/mcu/xr871/src/cedarx/base/include/CdxISOLang.h @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2008-2016 Allwinner Technology Co. Ltd. + * All rights reserved. + * + * File : CdxISOLang.h + * Description : iso639 standard for language names + * History : + * + */ + +#ifndef CDX_ISO_LANG_H +#define CDX_ISO_LANG_H + +typedef struct Cdx_ISO_639_Lang_t { + const char *eng_name; /* Description in English */ + const char iso639_1[3]; /* ISO-639-1 (2 characters) code */ + const char iso639_2T[4]; /* ISO-639-2/T (3 characters) English code */ + const char iso639_2B[4]; /* ISO-639-2/B (3 characters) native code */ +} Cdx_ISO_639_Lang_t; + +extern const Cdx_ISO_639_Lang_t CDX_ISO_639_LANGS[]; + +#endif diff --git a/platform/mcu/xr871/src/cedarx/base/include/CdxKeyedVector.h b/platform/mcu/xr871/src/cedarx/base/include/CdxKeyedVector.h new file mode 100644 index 0000000000..ae8b4499c6 --- /dev/null +++ b/platform/mcu/xr871/src/cedarx/base/include/CdxKeyedVector.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2008-2016 Allwinner Technology Co. Ltd. + * All rights reserved. + * + * File : CdxKeyedVector.h + * Description : KeyVector + * History : + * + */ + +#ifndef __CDX_KEY_VECTOR_H +#define __CDX_KEY_VECTOR_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +typedef struct CdxKeyedVectorS CdxKeyedVectorT; +typedef struct KeyValuePairS KeyValuePairT; + +struct KeyValuePairS { + char *key; + char *val; +}; + +struct CdxKeyedVectorS { + int size; + int index; + KeyValuePairT item[0]; +}; + + +/* return NULL on error */ +CdxKeyedVectorT *CdxKeyedVectorCreate(int num); + +void CdxKeyedVectorDestroy(CdxKeyedVectorT *keyedVector); + +/* For the following four functions, keyedVector must not be NULL. */ + +/* return 0 on success, -1 on error */ +int CdxKeyedVectorAdd(CdxKeyedVectorT *keyedVector, + const char *key, const char *val); + +int CdxKeyedVectorGetSize(CdxKeyedVectorT *keyedVector); + +char *CdxKeyedVectorGetKey(CdxKeyedVectorT *keyedVector, int index); + +char *CdxKeyedVectorGetValue(CdxKeyedVectorT *keyedVector, int index); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/platform/mcu/xr871/src/cedarx/base/include/CdxList.h b/platform/mcu/xr871/src/cedarx/base/include/CdxList.h new file mode 100644 index 0000000000..7146c23330 --- /dev/null +++ b/platform/mcu/xr871/src/cedarx/base/include/CdxList.h @@ -0,0 +1,273 @@ +/* + * Copyright (c) 2008-2016 Allwinner Technology Co. Ltd. + * All rights reserved. + * + * File : CdxList.h + * Description : List + * History : + * + */ + +#ifndef CDX_LIST_H +#define CDX_LIST_H +#include + +#define CDX_LIST_POISON1 ((void *) 0x00100100) +#define CDX_LIST_POISON2 ((void *) 0x00200200) + +struct CdxListNodeS +{ + struct CdxListNodeS *next; + struct CdxListNodeS *prev; +}; + +struct CdxListS +{ + struct CdxListNodeS *head; + struct CdxListNodeS *tail; +}; + +/* + * Simple doubly linked list implementation. + * + * Some of the internal functions ("__xxx") are useful when + * manipulating whole lists rather than single entries, as + * sometimes we already know the next/prev entries and we can + * generate better code by using them directly rather than + * using the generic single-entry routines. + */ +#define CdxListInit(list) do { \ + (list)->head = (list)->tail = (struct CdxListNodeS *)(list);\ + }while (0) + +#define CdxListNodeInit(node) do { \ + (node)->next = (node)->prev = (node);\ + }while (0) + +#ifdef __cplusplus +extern "C" +{ +#endif + +#ifdef AWP_DEBUG +/* + * Insert a new entry between two known consecutive entries. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + */ +void __CdxListAdd(struct CdxListNodeS *new , struct CdxListNodeS *prev, struct CdxListNodeS *next); + +/** + * list_add - add a new entry + * @new: new entry to be added + * @head: list head to add it after + * + * Insert a new entry after the specified head. + * This is good for implementing stacks. + */ +void CdxListAdd(struct CdxListNodeS *new , struct CdxListS *list); + +void CdxListAddBefore(struct CdxListNodeS *new , struct CdxListNodeS *pos); + +void CdxListAddAfter(struct CdxListNodeS *new , struct CdxListNodeS *pos); + +void CdxListAddTail(struct CdxListNodeS *new , struct CdxListS *list); + +/* + * Delete a list entry by making the prev/next entries + * point to each other. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + */ +void __CdxListDel(struct CdxListNodeS *prev, struct CdxListNodeS *next); + +/** + * list_del - deletes entry from list. + * @entry: the element to delete from the list. + * Note: list_empty() on entry does not return true after this, the entry is + * in an undefined state. + */ +void __CdxListDelEntry(struct CdxListNodeS *node); + +void CdxListDel(struct CdxListNodeS *node); + +/** + * list_replace - replace old entry by new one + * @old : the element to be replaced + * @new : the new element to insert + * + * If @old was empty, it will be overwritten. + */ + void CdxListReplace(struct CdxListNodeS *old, struct CdxListNodeS *new ); + +void CdxListReplaceInit(struct CdxListNodeS *old, struct CdxListNodeS *new ); + +/** + * list_del_init - deletes entry from list and reinitialize it. + * @entry: the element to delete from the list. + */ +void CdxListDelInit(struct CdxListNodeS *node); + +/** + * list_move - delete from one list and add as another's head + * @list: the entry to move + * @head: the head that will precede our entry + */ +void CdxListMove(struct CdxListNodeS *node, struct CdxListS *list); + +/** + * list_move_tail - delete from one list and add as another's tail + * @list: the entry to move + * @head: the head that will follow our entry + */ +void CdxListMoveTail(struct CdxListNodeS *node, struct CdxListS *list); + +/** + * list_is_last - tests whether @list is the last entry in list @head + * @list: the entry to test + * @head: the head of the list + */ +int CdxListIsLast(const struct CdxListNodeS *node, const struct CdxListS *list); + +/** + * list_empty - tests whether a list is empty + * @head: the list to test. + */ +int CdxListEmpty(const struct CdxListS *list); + +/** + * list_rotate_left - rotate the list to the left + * @head: the head of the list + */ +void CdxListRotateLeft(struct CdxListS *list); + +/** + * list_is_singular - tests whether a list has just one entry. + * @head: the list to test. + */ +int CdxListIsSingular(const struct CdxListS *list); + +#else +#include +#endif + +#ifdef __cplusplus +} +#endif + +/** + * list_entry - get the struct for this entry + * @ptr: the &struct list_head pointer. + * @type: the type of the struct this is embedded in. + * @member: the name of the list_struct within the struct. + */ +#define CdxListEntry(ptr, type, member) \ + CdxContainerOf(ptr, type, member) + +/** + * list_first_entry - get the first element from a list + * @ptr: the list head to take the element from. + * @type: the type of the struct this is embedded in. + * @member: the name of the list_struct within the struct. + * + * Note, that list is expected to be not empty. + */ +#define CdxListFirstEntry(ptr, type, member) \ + CdxListEntry((ptr)->head, type, member) + +/** + * list_for_each - iterate over a list + * @pos: the &struct list_head to use as a loop cursor. + * @head: the head for your list. + */ +#define CdxListForEach(pos, list) \ + for (pos = (list)->head; \ + pos != (struct CdxListNodeS *)(list);\ + pos = pos->next) + +/** + * list_for_each_prev - iterate over a list backwards + * @pos: the &struct list_head to use as a loop cursor. + * @head: the head for your list. + */ +#define CdxListForEachPrev(pos, list) \ + for (pos = (list)->tail; \ + pos != (struct CdxListNodeS *)(list); \ + pos = pos->prev) + +/** + * list_for_each_safe - iterate over a list safe against removal of list entry + * @pos: the &struct list_head to use as a loop cursor. + * @n: another &struct list_head to use as temporary storage + * @head: the head for your list. + */ +#define CdxListForEachSafe(pos, n, list) \ + for (pos = (list)->head, n = pos->next; \ + pos != (struct CdxListNodeS *)(list); \ + pos = n, n = pos->next) + +/** + * list_for_each_prev_safe - iterate over a list backwards safe against removal of list entry + * @pos: the &struct list_head to use as a loop cursor. + * @n: another &struct list_head to use as temporary storage + * @head: the head for your list. + */ +#define CdxListForEachPrevSafe(pos, n, list) \ + for (pos = (list)->tail, n = pos->prev; \ + pos != (struct CdxListNodeS *)(list); \ + pos = n, n = pos->prev) + +/** + * list_for_each_entry - iterate over list of given type + * @pos: the type * to use as a loop cursor. + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + */ +#define CdxListForEachEntry(pos, list, member) \ + for (pos = CdxListEntry((list)->head, typeof(*pos), member); \ + &pos->member != (struct CdxListNodeS *)(list); \ + pos = CdxListEntry(pos->member.next, typeof(*pos), member)) + +/** + * list_for_each_entry_reverse - iterate backwards over list of given type. + * @pos: the type * to use as a loop cursor. + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + */ +#define CdxListForEachEntryReverse(pos, list, member) \ + for (pos = CdxListEntry((list)->tail, typeof(*pos), member); \ + &pos->member != (struct CdxListNodeS *)(list); \ + pos = CdxListEntry(pos->member.prev, typeof(*pos), member)) + +/** + * list_for_each_entry_safe - iterate over list of given type safe against removal of list entry + * @pos: the type * to use as a loop cursor. + * @n: another type * to use as temporary storage + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + */ +#define CdxListForEachEntrySafe(pos, n, list, member) \ + for (pos = CdxListEntry((list)->head, typeof(*pos), member), \ + n = CdxListEntry(pos->member.next, typeof(*pos), member); \ + &pos->member != (struct CdxListNodeS *)(list); \ + pos = n, n = CdxListEntry(n->member.next, typeof(*n), member)) + +/** + * list_for_each_entry_safe_reverse - iterate backwards over list safe against removal + * @pos: the type * to use as a loop cursor. + * @n: another type * to use as temporary storage + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + * + * Iterate backwards over list of given type, safe against removal + * of list entry. + */ +#define CdxListForEachEntrySafeReverse(pos, n, list, member) \ + for (pos = CdxListEntry((list)->prev, typeof(*pos), member), \ + n = CdxListEntry(pos->member.prev, typeof(*pos), member); \ + &pos->member != (struct CdxListNodeS *)(list); \ + pos = n, n = CdxListEntry(n->member.prev, typeof(*n), member)) + +#endif diff --git a/platform/mcu/xr871/src/cedarx/base/include/CdxList.i b/platform/mcu/xr871/src/cedarx/base/include/CdxList.i new file mode 100644 index 0000000000..6b91616fe8 --- /dev/null +++ b/platform/mcu/xr871/src/cedarx/base/include/CdxList.i @@ -0,0 +1,177 @@ +/* + * Insert a new entry between two known consecutive entries. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + */ +CDX_INTERFACE void __CdxListAdd(struct CdxListNodeS *new, + struct CdxListNodeS *prev, struct CdxListNodeS *next) +{ + next->prev = new; + new->next = next; + new->prev = prev; + prev->next = new; +} + +/** + * list_add - add a new entry + * @new: new entry to be added + * @head: list head to add it after + * + * Insert a new entry after the specified head. + * This is good for implementing stacks. + */ +CDX_INTERFACE void CdxListAdd(struct CdxListNodeS *new, struct CdxListS *list) +{ + __CdxListAdd(new, (struct CdxListNodeS *)list, list->head); +} + +CDX_INTERFACE void CdxListAddBefore(struct CdxListNodeS *new, + struct CdxListNodeS *pos) +{ + __CdxListAdd(new, pos->prev, pos); +} + +CDX_INTERFACE void CdxListAddAfter(struct CdxListNodeS *new, + struct CdxListNodeS *pos) +{ + __CdxListAdd(new, pos, pos->next); +} + +CDX_INTERFACE void CdxListAddTail(struct CdxListNodeS *new, struct CdxListS *list) +{ + __CdxListAdd(new, list->tail, (struct CdxListNodeS *)list); +} + +/* + * Delete a list entry by making the prev/next entries + * point to each other. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + */ +CDX_INTERFACE void __CdxListDel(struct CdxListNodeS *prev, struct CdxListNodeS *next) +{ + next->prev = prev; + prev->next = next; +} + +/** + * list_del - deletes entry from list. + * @entry: the element to delete from the list. + * Note: list_empty() on entry does not return true after this, the entry is + * in an undefined state. + */ +CDX_INTERFACE void __CdxListDelEntry(struct CdxListNodeS *node) +{ + __CdxListDel(node->prev, node->next); +} + +CDX_INTERFACE void CdxListDel(struct CdxListNodeS *node) +{ + __CdxListDel(node->prev, node->next); + node->next = CDX_LIST_POISON1; + node->prev = CDX_LIST_POISON2; +} + +/** + * list_replace - replace old entry by new one + * @old : the element to be replaced + * @new : the new element to insert + * + * If @old was empty, it will be overwritten. + */ +CDX_INTERFACE void CdxListReplace(struct CdxListNodeS *old, + struct CdxListNodeS *new) +{ + new->next = old->next; + new->next->prev = new; + new->prev = old->prev; + new->prev->next = new; +} + +CDX_INTERFACE void CdxListReplaceInit(struct CdxListNodeS *old, + struct CdxListNodeS *new) +{ + CdxListReplace(old, new); + CdxListNodeInit(old); +} + +/** + * list_del_init - deletes entry from list and reinitialize it. + * @entry: the element to delete from the list. + */ +CDX_INTERFACE void CdxListDelInit(struct CdxListNodeS *node) +{ + __CdxListDelEntry(node); + CdxListNodeInit(node); +} + +/** + * list_move - delete from one list and add as another's head + * @list: the entry to move + * @head: the head that will precede our entry + */ +CDX_INTERFACE void CdxListMove(struct CdxListNodeS *node, struct CdxListS *list) +{ + __CdxListDelEntry(node); + CdxListAdd(node, list); +} + +/** + * list_move_tail - delete from one list and add as another's tail + * @list: the entry to move + * @head: the head that will follow our entry + */ +CDX_INTERFACE void CdxListMoveTail(struct CdxListNodeS *node, + struct CdxListS *list) +{ + __CdxListDelEntry(node); + CdxListAddTail(node, list); +} + +/** + * list_is_last - tests whether @list is the last entry in list @head + * @list: the entry to test + * @head: the head of the list + */ +CDX_INTERFACE int CdxListIsLast(const struct CdxListNodeS *node, + const struct CdxListS *list) +{ + return node->next == (struct CdxListNodeS *)list; +} + +/** + * list_empty - tests whether a list is empty + * @head: the list to test. + */ +CDX_INTERFACE int CdxListEmpty(const struct CdxListS *list) +{ + return (list->head == (struct CdxListNodeS *)list) + && (list->tail == (struct CdxListNodeS *)list); +} + +/** + * list_rotate_left - rotate the list to the left + * @head: the head of the list + */ +CDX_INTERFACE void CdxListRotateLeft(struct CdxListS *list) +{ + struct CdxListNodeS *first; + + if (!CdxListEmpty(list)) { + first = list->head; + CdxListMoveTail(first, list); + } +} + +/** + * list_is_singular - tests whether a list has just one entry. + * @head: the list to test. + */ +CDX_INTERFACE int CdxListIsSingular(const struct CdxListS *list) +{ + return !CdxListEmpty(list) && (list->head == list->tail); +} + + diff --git a/platform/mcu/xr871/src/cedarx/base/include/CdxLock.h b/platform/mcu/xr871/src/cedarx/base/include/CdxLock.h new file mode 100644 index 0000000000..4da45d7e74 --- /dev/null +++ b/platform/mcu/xr871/src/cedarx/base/include/CdxLock.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2008-2016 Allwinner Technology Co. Ltd. + * All rights reserved. + * + * File : CdxLock.h + * Description : Lock + * History : + * + */ + +#ifndef CDX_LOCK_H +#define CDX_LOCK_H +#include + +typedef pthread_mutex_t CdxMutexT; +typedef pthread_cond_t CdxCondT; + +#define CdxMutexInit(mutex) pthread_mutex_init(mutex, NULL) +#define CdxMutexDestroy(mutex) pthread_mutex_destroy(mutex) +#define CdxMutexLock(mutex) pthread_mutex_lock(mutex) +#define CdxMutexTrylock(mutex)pthread_mutex_trylock(mutex) +#define CdxMutexUnlock(mutex) pthread_mutex_unlock(mutex) + +#define CdxCondInit(cond) pthread_cond_init(cond, NULL) +#define CdxCondDestroy(cond) pthread_cond_destroy(cond) +#define CdxCondWait(cond, mutex) pthread_cond_wait(cond, mutex) +#define CdxCondTimedwait(cond, mutex, time) \ + pthread_cond_timedwait(cond, mutex, time) +#define CdxCondSignal(cond) pthread_cond_signal(cond) +#define CdxCondBroadcast(cond) pthread_cond_broadcast(cond) + +#endif diff --git a/platform/mcu/xr871/src/cedarx/base/include/CdxMemory.h b/platform/mcu/xr871/src/cedarx/base/include/CdxMemory.h new file mode 100644 index 0000000000..2ba3709edd --- /dev/null +++ b/platform/mcu/xr871/src/cedarx/base/include/CdxMemory.h @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2008-2016 Allwinner Technology Co. Ltd. + * All rights reserved. + * + * File : CdxMemory.h + * Description : Memory + * History : + * + */ + +#ifndef CDX_MEMORY_H +#define CDX_MEMORY_H +#include +#include + +#define CdxMalloc(size) malloc(size) +#define CdxFree(ptr) free(ptr) +#define CdxRealloc(ptr, size) realloc(ptr, size) + +//#define CdxMemcpy(dest, src, n) memcpy(dest, src, n) +//#define CdxMemset(s, c, n) memset(s, c, n) +#define CdxStrdup(s) strdup(s) +//#define CdxStrlen(s) strlen(s) +#endif diff --git a/platform/mcu/xr871/src/cedarx/base/include/CdxMessage.h b/platform/mcu/xr871/src/cedarx/base/include/CdxMessage.h new file mode 100644 index 0000000000..69f33bd610 --- /dev/null +++ b/platform/mcu/xr871/src/cedarx/base/include/CdxMessage.h @@ -0,0 +1,232 @@ +/* + * Copyright (c) 2008-2016 Allwinner Technology Co. Ltd. + * All rights reserved. + * + * File : CdxMessage.h + * Description : Message + * History : + * + */ + +#ifndef CDX_MESSAGE_H +#define CDX_MESSAGE_H +#include +#include +#include + +typedef struct CdxMessageS CdxMessageT; +typedef struct CdxHandlerS CdxHandlerT; +typedef struct CdxDeliverS CdxDeliverT; + +struct CdxDeliverOpsS +{ + cdx_int32 (*postUS)(CdxDeliverT *, CdxMessageT *, cdx_uint64/*timeUs*/); +}; + +struct CdxDeliverS +{ + struct CdxDeliverOpsS *ops; +}; + +CdxDeliverT *CdxDeliverCreate(); + +void CdxDeliverDestroy(CdxDeliverT *deliver); + +void CdxDeliverReset(void); + +static inline cdx_int32 CdxDeliverPostUs(CdxDeliverT *deliver, CdxMessageT *msg, + cdx_uint64 timeUs) +{ + CDX_CHECK(deliver); + CDX_CHECK(deliver->ops); + CDX_CHECK(deliver->ops->postUS); + return deliver->ops->postUS(deliver, msg, timeUs); +} + +/** + *ĿǰmsgijǴеģдеҪ + *Ǽ + */ + +/* sizeof(name) < 32 */ +struct CdxMessageOpsS +{ + CdxMessageT *(*incRef)(CdxMessageT *); + + void (*decRef)(CdxMessageT *); + + CdxMetaT *(*getMeta)(CdxMessageT *); + + cdx_int32 (*what)(CdxMessageT *); + + cdx_err (*postUs)(CdxMessageT *, cdx_uint64); /* asynchronous */ + + CdxMessageT *(*dup)(CdxMessageT *); + + void (*deliver)(CdxMessageT *); +}; + +struct CdxMessageS +{ + struct CdxMessageOpsS *ops; +}; + +static inline CdxMessageT *CdxMessageIncRef(CdxMessageT *msg) +{ + CDX_CHECK(msg); + CDX_CHECK(msg->ops); + CDX_CHECK(msg->ops->incRef); + return msg->ops->incRef(msg); +} + +static inline void CdxMessageDecRef(CdxMessageT *msg) +{ + CDX_CHECK(msg); + CDX_CHECK(msg->ops); + CDX_CHECK(msg->ops->decRef); + return msg->ops->decRef(msg); +} + +static inline CdxMetaT *CdxMessageGetMeta(CdxMessageT *msg) +{ + CDX_CHECK(msg); + CDX_CHECK(msg->ops); + CDX_CHECK(msg->ops->getMeta); + return msg->ops->getMeta(msg); +} + +static inline void CdxMessageDeliver(CdxMessageT *msg) +{ + CDX_CHECK(msg); + CDX_CHECK(msg->ops); + CDX_CHECK(msg->ops->deliver); + return msg->ops->deliver(msg); +} + +#define CdxMessageFastNotify(pool, what, hdr) \ + do \ + { \ + CdxMessageT *_notifyMsg = CdxMessageCreate(pool, what, hdr); \ + CDX_FORCE_CHECK(_notifyMsg); \ + CdxMessagePost(_notifyMsg); \ + }while (0) + +#define CdxMessageSetInt32(msg, name, val) \ + CdxMetaSetInt32(CdxMessageGetMeta(msg), name, val) + +#define CdxMessageFindInt32(msg, name, pVal) \ + CdxMetaFindInt32(CdxMessageGetMeta(msg), name, pVal) + +#define CdxMessageSetInt64(msg, name, val) \ + CdxMetaSetInt64(CdxMessageGetMeta(msg), name, val) + +#define CdxMessageFindInt64(msg, name, pVal) \ + CdxMetaFindInt64(CdxMessageGetMeta(msg), name, pVal) + +#define CdxMessageSetString(msg, name, val) \ + CdxMetaSetString(CdxMessageGetMeta(msg), name, val) + +#define CdxMessageFindString(msg, name, pVal) \ + CdxMetaFindString(CdxMessageGetMeta(msg), name, pVal) + +#define CdxMessageSetObject(msg, name, val) \ + CdxMetaSetObject(CdxMessageGetMeta(msg), name, val) + +#define CdxMessageFindObject(msg, name, pVal) \ + CdxMetaFindObject(CdxMessageGetMeta(msg), name, (void **)(pVal)) + +#define CdxMessageRemoveObject(msg, name) \ + CdxMetaRemoveObject(CdxMessageGetMeta(msg), name) + +#define CdxMessageClearMeta(msg) \ + CdxMetaClear(CdxMessageGetMeta(msg)) + +static inline cdx_int32 CdxMessageWhat(CdxMessageT *msg) +{ + CDX_CHECK(msg); + CDX_CHECK(msg->ops); + CDX_CHECK(msg->ops->what); + return msg->ops->what(msg); +} + +static inline cdx_err CdxMessagePostUs(CdxMessageT *msg, cdx_uint64 timeOut) +{ + CDX_CHECK(msg); + CDX_CHECK(msg->ops); + CDX_CHECK(msg->ops->postUs); + return msg->ops->postUs(msg, timeOut); +} + +static inline CdxMessageT *CdxMessageDup(CdxMessageT *msg) +{ + CDX_CHECK(msg); + CDX_CHECK(msg->ops); + CDX_CHECK(msg->ops->dup); + return msg->ops->dup(msg); +} + +#define CdxMessagePost(msg) \ + CdxMessagePostUs(msg, 0) + +#ifdef __cplusplus +extern "C" { +#endif +#define CdxMessageCreate(pool, what, handler) \ + __CdxMessageCreate(pool, what, handler, __FILE__, __LINE__) + +CdxMessageT *__CdxMessageCreate(AwPoolT *pool, cdx_int32 what, CdxHandlerT *handler, + char *file, int line); + +void CdxMessageDestroy(CdxMessageT *msg); + +/*-----------------------------msg handler-----------------------------*/ +typedef struct CdxHandlerItfS CdxHandlerItfT; + +struct CdxHandlerItfOpsS +{ + void (*msgRecv)(CdxHandlerItfT *, CdxMessageT *); +}; + +struct CdxHandlerItfS +{ + struct CdxHandlerItfOpsS *ops; +}; + +struct CdxHandlerOpsS +{ + cdx_int32 (*postUs)(CdxHandlerT *, CdxMessageT *, cdx_uint64); + + void (*msgRecv)(CdxHandlerT *, CdxMessageT *); +}; + +struct CdxHandlerS +{ + struct CdxHandlerOpsS *ops; +}; + +static inline cdx_int32 CdxHandlerPostUs(CdxHandlerT *hdr, CdxMessageT *msg, + cdx_uint64 timeUs) +{ + CDX_CHECK(hdr); + CDX_CHECK(hdr->ops); + return hdr->ops->postUs(hdr, msg, timeUs); +} + +static inline void CdxHandlerMsgRecv(CdxHandlerT *hdr, CdxMessageT *msg) +{ + CDX_CHECK(hdr); + CDX_CHECK(hdr->ops); + return hdr->ops->msgRecv(hdr, msg); +} + +CdxHandlerT *CdxHandlerCreate(AwPoolT *pool, CdxHandlerItfT *itf, CdxDeliverT *deliver); + +void CdxHandlerDestroy(CdxHandlerT *hdr); + +/*--------------------------msg handler end----------------------------*/ + +#ifdef __cplusplus +} +#endif + +#endif // A_MESSAGE_H_ diff --git a/platform/mcu/xr871/src/cedarx/base/include/CdxMeta.h b/platform/mcu/xr871/src/cedarx/base/include/CdxMeta.h new file mode 100644 index 0000000000..da0d54b35b --- /dev/null +++ b/platform/mcu/xr871/src/cedarx/base/include/CdxMeta.h @@ -0,0 +1,235 @@ +/* + * Copyright (c) 2008-2016 Allwinner Technology Co. Ltd. + * All rights reserved. + * + * File : CdxMeta.h + * Description : Meta + * History : + * + */ + +#ifndef CDX_META_H +#define CDX_META_H +#include +#include +#include + +#define CDX_META_ITEM_NAMESIZE 32 + +//typedef void (*pfObjDtor)(void *); +typedef struct CdxMetaS CdxMetaT; + +/* sizeof(name) < 32 */ +struct CdxMetaOpsS +{ + CdxMetaT *(*incRef)(CdxMetaT *); + void (*decRef)(CdxMetaT *); + + cdx_err (*setInt32)(CdxMetaT *, cdx_char * /*name*/, cdx_int32); + cdx_err (*findInt32)(CdxMetaT *, cdx_char * /*name*/, cdx_int32 *); + cdx_err (*removeInt32)(CdxMetaT *, cdx_char * /*name*/); + + cdx_err (*setInt64)(CdxMetaT *, cdx_char * /*name*/, cdx_int64); + cdx_err (*findInt64)(CdxMetaT *, cdx_char * /*name*/, cdx_int64 *); + cdx_err (*removeInt64)(CdxMetaT *, cdx_char * /*name*/); + + cdx_err (*setString)(CdxMetaT *, cdx_char * /*name*/, cdx_char *); + cdx_err (*findString)(CdxMetaT *, cdx_char * /*name*/, cdx_char **); + cdx_err (*appendString)(CdxMetaT *, cdx_char * /*name*/, cdx_char *); + cdx_err (*removeString)(CdxMetaT *, cdx_char * /*name*/); + + cdx_err (*setData)(CdxMetaT *, cdx_char * /*name*/, cdx_uint8 *, cdx_uint32); + cdx_err (*findData)(CdxMetaT *, cdx_char * /*name*/, cdx_uint8 **, cdx_uint32*); + cdx_err (*removeData)(CdxMetaT *, cdx_char * /*name*/); + + cdx_err (*setObject)(CdxMetaT *, cdx_char * /*name*/, void *); + cdx_err (*findObject)(CdxMetaT *, cdx_char * /*name*/, void **); + cdx_err (*removeObject)(CdxMetaT *, cdx_char * /*name*/); + + CdxMetaT *(*dup)(CdxMetaT *); + void (*clear)(CdxMetaT *); +}; + +struct CdxMetaS +{ + struct CdxMetaOpsS *ops; +}; + +static inline CdxMetaT *CdxMetaIncRef(CdxMetaT *meta) +{ + CDX_CHECK(meta); + CDX_CHECK(meta->ops); + CDX_CHECK(meta->ops->incRef); + return meta->ops->incRef(meta); +} + +static inline void CdxMetaDecRef(CdxMetaT *meta) +{ + CDX_CHECK(meta); + CDX_CHECK(meta->ops); + CDX_CHECK(meta->ops->decRef); + return meta->ops->decRef(meta); +} + +static inline cdx_err CdxMetaSetInt32(CdxMetaT *meta, + cdx_char *name, cdx_int32 val) +{ + CDX_CHECK(meta); + CDX_CHECK(meta->ops); + CDX_CHECK(meta->ops->setInt32); + return meta->ops->setInt32(meta, name, val); +} + +static inline cdx_err CdxMetaFindInt32(CdxMetaT *meta, + cdx_char *name, cdx_int32 *pVal) +{ + CDX_CHECK(meta); + CDX_CHECK(meta->ops); + CDX_CHECK(meta->ops->findInt32); + return meta->ops->findInt32(meta, name, pVal); +} + +static inline cdx_err CdxMetaRemoveInt32(CdxMetaT *meta, cdx_char *name) +{ + CDX_CHECK(meta); + CDX_CHECK(meta->ops); + CDX_CHECK(meta->ops->removeInt32); + return meta->ops->removeInt32(meta, name); +} + +static inline cdx_err CdxMetaSetInt64(CdxMetaT *meta, + cdx_char *name, cdx_int64 val) +{ + CDX_CHECK(meta); + CDX_CHECK(meta->ops); + CDX_CHECK(meta->ops->setInt64); + return meta->ops->setInt64(meta, name, val); +} + +static inline cdx_err CdxMetaFindInt64(CdxMetaT *meta, + cdx_char *name, cdx_int64 *pVal) +{ + CDX_CHECK(meta); + CDX_CHECK(meta->ops); + CDX_CHECK(meta->ops->findInt64); + return meta->ops->findInt64(meta, name, pVal); +} + +static inline cdx_err CdxMetaSetString(CdxMetaT *meta, + cdx_char *name, cdx_char *val) +{ + CDX_CHECK(meta); + CDX_CHECK(meta->ops); + CDX_CHECK(meta->ops->setString); + return meta->ops->setString(meta, name, val); +} + +/** + *ͨλҪstring + *ûҪеCdxFree()ͷſռ + */ +static inline cdx_err CdxMetaFindString(CdxMetaT *meta, + cdx_char *name, cdx_char **pVal) +{ + CDX_CHECK(meta); + CDX_CHECK(meta->ops); + CDX_CHECK(meta->ops->findString); + return meta->ops->findString(meta, name, pVal); +} + +static inline cdx_err CdxMetaAppendString(CdxMetaT *meta, + cdx_char *name, cdx_char *val) +{ + CDX_CHECK(meta); + CDX_CHECK(meta->ops); + CDX_CHECK(meta->ops->appendString); + return meta->ops->appendString(meta, name, val); +} + +static inline cdx_err CdxMetaSetData(CdxMetaT *meta, cdx_char *name, + cdx_uint8 *val, cdx_uint32 size) +{ + CDX_CHECK(meta); + CDX_CHECK(meta->ops); + CDX_CHECK(meta->ops->setData); + return meta->ops->setData(meta, name, val, size); +} + +/** + *ͨλҪstring + *ûҪеCdxFree()ͷſռ + */ +static inline cdx_err CdxMetaFindData(CdxMetaT *meta, cdx_char *name, + cdx_uint8 **pVal, cdx_uint32 *pSize) +{ + CDX_CHECK(meta); + CDX_CHECK(meta->ops); + CDX_CHECK(meta->ops->findData); + return meta->ops->findData(meta, name, pVal, pSize); +} + +static inline cdx_err CdxMetaSetObject(CdxMetaT *meta, cdx_char *name, void *val) +{ + CDX_CHECK(meta); + CDX_CHECK(meta->ops); + CDX_CHECK(meta->ops->setObject); + return meta->ops->setObject(meta, name, val); +} + +/** + *ͨλҪĶľ + *meta,Ч + *ҪڱʹõĻü+1 + *߱渱 + */ +static inline cdx_err CdxMetaFindObject(CdxMetaT *meta, + cdx_char *name, void **pVal) +{ + CDX_CHECK(meta); + CDX_CHECK(meta->ops); + CDX_CHECK(meta->ops->findObject); + return meta->ops->findObject(meta, name, pVal); +} + +static inline cdx_err CdxMetaRemoveObject(CdxMetaT *meta, cdx_char *name) +{ + CDX_CHECK(meta); + CDX_CHECK(meta->ops); + CDX_CHECK(meta->ops->removeObject); + return meta->ops->removeObject(meta, name); +} + +static inline CdxMetaT *CdxMetaDup(CdxMetaT *meta) +{ + CDX_CHECK(meta); + CDX_CHECK(meta->ops); + CDX_CHECK(meta->ops->dup); + return meta->ops->dup(meta); +} + +static inline void CdxMetaClear(CdxMetaT *meta) +{ + CDX_CHECK(meta); + CDX_CHECK(meta->ops); + CDX_CHECK(meta->ops->clear); + meta->ops->clear(meta); + return ; +} + +#ifdef __cplusplus +extern "C" +{ +#endif + +#define CdxMetaCreate(pool) \ + __CdxMetaCreate(pool, __FILE__, __LINE__) + +CdxMetaT *__CdxMetaCreate(AwPoolT *pool, char *file, int line); + +void CdxMetaDestroy(CdxMetaT *meta); + +#ifdef __cplusplus +} +#endif + +#endif // A_MESSAGE_H_ diff --git a/platform/mcu/xr871/src/cedarx/base/include/CdxQueue.h b/platform/mcu/xr871/src/cedarx/base/include/CdxQueue.h new file mode 100644 index 0000000000..39820d301a --- /dev/null +++ b/platform/mcu/xr871/src/cedarx/base/include/CdxQueue.h @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2008-2016 Allwinner Technology Co. Ltd. + * All rights reserved. + * + * File : CdxQueue.h + * Description : Queue + * History : + * + */ + +#ifndef CDX_QUEUE_H +#define CDX_QUEUE_H +#include +#include +#include + +typedef struct CdxQueueS CdxQueueT; +typedef void * CdxQueueDataT; + +struct CdxQueueOpsS +{ + CdxQueueDataT (*pop)(CdxQueueT *); + cdx_err (*push)(CdxQueueT *, CdxQueueDataT); + cdx_bool (*empty)(CdxQueueT *); + +}; + +struct CdxQueueS +{ + struct CdxQueueOpsS *ops; +}; + +static inline CdxQueueDataT CdxQueuePop(CdxQueueT *queue) +{ + CDX_CHECK(queue); + CDX_CHECK(queue->ops); + CDX_CHECK(queue->ops->pop); + return queue->ops->pop(queue); +} + +static inline cdx_err CdxQueuePush(CdxQueueT *queue, CdxQueueDataT data) +{ + CDX_CHECK(queue); + CDX_CHECK(queue->ops); + CDX_CHECK(queue->ops->push); + return queue->ops->push(queue, data); +} + +static inline cdx_bool CdxQueueEmpty(CdxQueueT *queue) +{ + CDX_CHECK(queue); + CDX_CHECK(queue->ops); + CDX_CHECK(queue->ops->empty); + return queue->ops->empty(queue); +} + +#ifdef __cplusplus +extern "C" +{ +#endif +/*this is a look free queue*/ +CdxQueueT *CdxQueueCreate(AwPoolT *pool); + +void CdxQueueDestroy(CdxQueueT *queue); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/platform/mcu/xr871/src/cedarx/base/include/CdxSocketUtil.h b/platform/mcu/xr871/src/cedarx/base/include/CdxSocketUtil.h new file mode 100644 index 0000000000..fd08dc827d --- /dev/null +++ b/platform/mcu/xr871/src/cedarx/base/include/CdxSocketUtil.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2008-2016 Allwinner Technology Co. Ltd. + * All rights reserved. + * + * File : CdxSocketUtil.h + * Description : SocketUtil + * History : + * + */ + +#ifndef CDX_SOCKET_UTIL_H +#define CDX_SOCKET_UTIL_H +#include + +#include /* See NOTES */ +#include "lwip/sockets.h" +//#include +#include "lwip/inet.h" + +#define CDX_SELECT_TIMEOUT 100000L /*100 ms*/ + +#ifdef __cplusplus +extern "C" +{ +#endif +cdx_int32 CdxSockSetBlocking(cdx_int32 sockfd, cdx_int32 blocking); + +cdx_err CdxMakeSocketBlocking(cdx_int32 s, cdx_bool blocking); + +void CdxSocketMakePortPair(cdx_int32 *rtpSocket, cdx_int32 *rtcpSocket, + cdx_uint32 *rtpPort); + +cdx_int32 CdxSockAddrConstruct(struct sockaddr_in *dest, char *ip, cdx_int32 port); + +cdx_ssize CdxSockNoblockRecv(cdx_int32 sockfd, void *buf, cdx_size len); + +cdx_ssize CdxSockAsynRecv(cdx_int32 sockfd, void *buf, cdx_size len, + cdx_long timeoutUs, cdx_int32 *pForceStop); + +cdx_ssize CdxSockAsynSend(cdx_int32 sockfd, const void *buf, cdx_size len, + cdx_long timeoutUs, cdx_int32 *pForceStop); + +cdx_int32 CdxAsynSocket(int domain, cdx_int32 *nRecvBufLen); + +cdx_int32 CdxSockAsynConnect(cdx_int32 sockfd, const struct sockaddr *addr, + socklen_t addrlen, cdx_long timeoutUs, cdx_int32 *pForceStop); + +cdx_int32 CdxSockIsBlocking(cdx_int32 sockfd); + +void CdxSockDisableTcpKeepalive(cdx_int32 sockfd); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/platform/mcu/xr871/src/cedarx/base/include/CdxStrUtil.h b/platform/mcu/xr871/src/cedarx/base/include/CdxStrUtil.h new file mode 100644 index 0000000000..0697dfbab1 --- /dev/null +++ b/platform/mcu/xr871/src/cedarx/base/include/CdxStrUtil.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2008-2016 Allwinner Technology Co. Ltd. + * All rights reserved. + * + * File : CdxStrUtil.h + * Description : StrUtil + * History : + * + */ + +#ifndef CDX_STR_UTIL_H +#define CDX_STR_UTIL_H + +#include +#include +#include + +struct CdxStrItemS +{ + CdxListNodeT node; + cdx_char *val; +}; + +#ifdef __cplusplus +extern "C" +{ +#endif + +void CdxStrTrimTail(cdx_char *str); + +void CdxStrTrimHead(cdx_char *str); + +void CdxStrTrim(cdx_char *str); + +void CdxStrTolower(cdx_char *str); + +cdx_char *CdxStrAttributeOfKey(AwPoolT *pool, const cdx_char *str, cdx_char *key, cdx_char sepa); + +cdx_uint32 CdxStrSplit(AwPoolT *pool, cdx_char *str, char sepa, CdxListT *itemList); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/platform/mcu/xr871/src/cedarx/base/include/CdxSysinfo.h b/platform/mcu/xr871/src/cedarx/base/include/CdxSysinfo.h new file mode 100644 index 0000000000..8d33c1fff7 --- /dev/null +++ b/platform/mcu/xr871/src/cedarx/base/include/CdxSysinfo.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2008-2016 Allwinner Technology Co. Ltd. + * All rights reserved. + * + * File : CdxSysinfo.h + * Description : Sysinfo + * History : + * + */ + +#ifndef CDX_SYS_INFO_H +#define CDX_SYS_INFO_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +#if 0 +//* get current ddr frequency, if it is too slow, we will cut some spec off. +int MemAdapterGetDramFreq(); + +enum CHIPID +{ + SI_CHIP_UNKNOWN = 0, + SI_CHIP_H3s = 1, + SI_CHIP_H3 = 2, + SI_CHIP_H2 = 3, + SI_CHIP_H2PLUS = 4, +}; + +int SysinfoChipId(void); +#endif + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/platform/mcu/xr871/src/cedarx/base/include/CdxTime.h b/platform/mcu/xr871/src/cedarx/base/include/CdxTime.h new file mode 100644 index 0000000000..6ee1aaa67c --- /dev/null +++ b/platform/mcu/xr871/src/cedarx/base/include/CdxTime.h @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2008-2016 Allwinner Technology Co. Ltd. + * All rights reserved. + * + * File : CdxTime.h + * Description : Time + * History : + * + */ + +#ifndef CDX_TIME_H +#define CDX_TIME_H + +#include + +/* Wall Clock: it's not monotonic and may have discontinuities */ +CDX_API cdx_int64 CdxGetNowUs(void); + +/* Monotonic clock */ +CDX_API cdx_int64 CdxMonoTimeUs(void); + +#endif diff --git a/platform/mcu/xr871/src/cedarx/base/include/CdxTypes.h b/platform/mcu/xr871/src/cedarx/base/include/CdxTypes.h new file mode 100644 index 0000000000..2c8f606bfa --- /dev/null +++ b/platform/mcu/xr871/src/cedarx/base/include/CdxTypes.h @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2008-2016 Allwinner Technology Co. Ltd. + * All rights reserved. + * + * File : CdxTypes.h + * Description : Types + * History : + * + */ + +#ifndef CDX_TYPES_H +#define CDX_TYPES_H + +/* This is not safe as ((void*)0) for C, and you cannot define NULL as + * ((void*)0) for C++, so don't bother to define your own NULL. +#ifdef NULL +#undef NULL +#endif +#define NULL 0 +*/ + +#define CDX_SUCCESS 0 +#define CDX_FAILURE (-1) + +typedef long long cdx_int64; +typedef unsigned long long cdx_uint64; + +typedef int cdx_int32; +typedef unsigned int cdx_uint32; + +typedef short cdx_int16; +typedef unsigned short cdx_uint16; + +typedef unsigned char cdx_uint8; + +/* For gcc, char is default to unsigned on ARM and signed on x86 platform. So + * typedef char cdx_int8 is wrong. + */ +typedef signed char cdx_int8; + +typedef unsigned long cdx_ulong; +typedef long cdx_long; + +typedef char cdx_char; +typedef int cdx_bool; +typedef unsigned long cdx_size; +typedef signed long cdx_ssize; + +typedef float cdx_float; + +typedef struct CdxBufferS CdxBufferT; +typedef struct CdxListNodeS CdxListNodeT; +typedef struct CdxListS CdxListT; + +#ifdef AWP_DEBUG +#define CDX_INTERFACE +#else +#define CDX_INTERFACE static inline +#endif + +#ifdef __cplusplus +#define CDX_EXTERN extern "C" +#else +#define CDX_EXTERN +#endif + +#define CDX_API CDX_EXTERN + +typedef cdx_int32 cdx_err; + +#define CDX_TRUE 1 +#define CDX_FALSE 0 + +#define CedarXMin(a, b) ((a) < (b) ? (a) : (b)) + +#define CdxOffsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) + +#define CdxContainerOf(ptr, type, member) ({ \ + const typeof(((type *)0)->member) *__mptr = (ptr); \ + (type *)((char *)__mptr - CdxOffsetof(type,member) ); }) + +enum CdxMediaTypeE +{ + CDX_MEDIA_UNKNOWN = -1, + CDX_MEDIA_VIDEO = 0, + CDX_MEDIA_AUDIO, + CDX_MEDIA_SUBTITLE, + CDX_MEDIA_METADATA, + CDX_MEDIA_DATA, +}; + +#ifndef ARRAY_SIZE +#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) +#endif + +#endif diff --git a/platform/mcu/xr871/src/cedarx/base/include/CdxUrl.h b/platform/mcu/xr871/src/cedarx/base/include/CdxUrl.h new file mode 100644 index 0000000000..c3d83585cb --- /dev/null +++ b/platform/mcu/xr871/src/cedarx/base/include/CdxUrl.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2008-2016 Allwinner Technology Co. Ltd. + * All rights reserved. + * + * File : CdxUrl.h + * Description : Url + * History : + * + */ + +#ifndef CDX_URL +#define CDX_URL + +typedef struct AW_URL +{ + char* url; + char* protocol; + char* hostname; + char* file; + unsigned int port; + char* username; + char* password; + char* noauth_url; +}CdxUrlT; + +CdxUrlT* CdxUrlNew(char* url); +void CdxUrlPrintf(CdxUrlT* url); +void CdxUrlFree(CdxUrlT* curUrl); + +void CdxUrlEscapeString(char *outbuf, const char *inbuf); + +void CdxUrlUnescapeString(char *outbuf, const char *inbuf); + +void CdxUrlEscapeStringPart(char *outbuf, const char *inbuf); + +#endif diff --git a/platform/mcu/xr871/src/cedarx/base/include/SmartDnsService.h b/platform/mcu/xr871/src/cedarx/base/include/SmartDnsService.h new file mode 100644 index 0000000000..2fa4e6ada7 --- /dev/null +++ b/platform/mcu/xr871/src/cedarx/base/include/SmartDnsService.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2008-2016 Allwinner Technology Co. Ltd. + * All rights reserved. + * + * File : SmartDnsService.h + * Description : SmartDnsService + * History : + * + */ + +#ifndef SMART_DNS_SERVICE_H +#define SMART_DNS_SERVICE_H + +typedef void (*ResponeHook)(void * /* userhdr */, int /* ret */ , struct addrinfo * /* ret */); + +enum SDSReturnValueE +{ + SDS_OK, + SDS_PENDING, + SDS_ERROR, +}; + +#ifdef __cplusplus +extern "C" +{ +#endif + +int SDSRequest(const char *hostname, int port, struct addrinfo **pAddr, + void *userHdr, ResponeHook hook); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/platform/mcu/xr871/src/cedarx/base/include/cdx_log.h b/platform/mcu/xr871/src/cedarx/base/include/cdx_log.h new file mode 100644 index 0000000000..46b12c808a --- /dev/null +++ b/platform/mcu/xr871/src/cedarx/base/include/cdx_log.h @@ -0,0 +1,225 @@ +/* + * Copyright (c) 2008-2016 Allwinner Technology Co. Ltd. + * All rights reserved. + * + * File : cdx_log.h + * Description : Log + * History : + * + */ + +#ifndef CDX_LOG_H +#define CDX_LOG_H + +#ifndef LOG_TAG +#define LOG_TAG "awplayer" +#endif + +#include "cdx_malloc_dbg.h" + +#define CDX_DEBUG 1 + +/* temp define to develop */ +#define VIDEO_SUPPORT 0 //might be needed in the camera application. but it's not realized. +#define SUBTITLE_SUPPORT 0 //almost impossible +#define ONLINE_SUPPORT 0 //should be change to cache support +#define FD_SUPPORT 0 //might not support in future +#define ID3_IOT_IMPLEMENT 1 //revise for IOT. +#define LIVEMODE_VIDEO 0 //almost impossible +#define SOUNDCTRL_NULL 0 //only for test +#define CAPTURECTRL_NULL 0 //only for test +#define CDX_IOT_AWPOOL 0 //revise for IOT, but can be used for revise memory allocating. +#define CDX_IOT_OLD_SOCKET 0 //revise for IOT, 0: connect every time. +#define CDX_IOT_DNS_CACHE 0 //revise for IOT, 0: using Lwip Dns. 1: using Cedarx Dns. +#define CDX_IOT_CMCC_LOG 0 //log string callback to user. +#define CDX_LWIP_SELECT_ERR 0 //Lwip not support error teller function. +#define SET_SPEED_SUPPORT 0 +#define SECURE_BUFFER_SUPPORT 0 + +#ifndef DEF_CDX_LOG_LEVEL_TYPE +#define DEF_CDX_LOG_LEVEL_TYPE +enum CDX_LOG_LEVEL_TYPE { + LOG_LEVEL_VERBOSE = 2, + LOG_LEVEL_DEBUG = 3, + LOG_LEVEL_INFO = 4, + LOG_LEVEL_WARNING = 5, + LOG_LEVEL_ERROR = 6, +}; +#endif + +#include +int printf_lock_init(); +int wrap_printf(const char *fmt, ...); +int printf_lock_deinit(); + +int log_file_reset(const char *path); +int log_file(const char *path, unsigned char *buf, unsigned int len); + +void __printf_time(const char *f, unsigned int l); +#define printf_time() __printf_time(__func__, __LINE__) + +#define printf wrap_printf + +extern enum CDX_LOG_LEVEL_TYPE GLOBAL_LOG_LEVEL; + +#ifndef CONFIG_LOG_LEVEL +#define CONFIG_LOG_LEVEL (0xFFFF) +#endif + +#ifdef __ANDROID__ +#include + +#define CDX_LOG_ORDER \ + ((unsigned)LOG_LEVEL_ERROR == (unsigned)ANDROID_LOG_ERROR) && \ + ((unsigned)LOG_LEVEL_WARNING == (unsigned)ANDROID_LOG_WARN) && \ + ((unsigned)LOG_LEVEL_INFO == (unsigned)ANDROID_LOG_INFO) && \ + ((unsigned)LOG_LEVEL_DEBUG == (unsigned)ANDROID_LOG_DEBUG) && \ + ((unsigned)LOG_LEVEL_VERBOSE == (unsigned)ANDROID_LOG_VERBOSE) + +typedef char CHECK_LOG_LEVEL_EQUAL_TO_ANDROID[CDX_LOG_ORDER > 0 ? 1 : -1]; + +#define AWLOG(level, fmt, arg...) \ + do { \ + if (level >= GLOBAL_LOG_LEVEL || level >= CONFIG_LOG_LEVEL) \ + LOG_PRI(level, LOG_TAG, "<%s:%u>: " fmt, __FUNCTION__, __LINE__, ##arg); \ + } while (0) + +#define CDX_TRACE() \ + CDX_LOGI("<%s:%u> tid(%d)", __FUNCTION__, __LINE__, gettid()) + +/*check when realease version*/ +#define CDX_FORCE_CHECK(e) \ + LOG_ALWAYS_FATAL_IF( \ + !(e), \ + "<%s:%d>CDX_CHECK(%s) failed.", \ + __FUNCTION__, __LINE__, #e) + +#define CDX_TRESPASS() \ + LOG_ALWAYS_FATAL("Should not be here.") + +#define CDX_LOG_FATAL(fmt, arg...) \ + LOG_ALWAYS_FATAL("<%s:%d>" fmt, \ + __FUNCTION__, __LINE__, ##arg) + +#define CDX_LOG_CHECK(e, fmt, arg...) \ + LOG_ALWAYS_FATAL_IF( \ + !(e), \ + "<%s:%d>check (%s) failed:" fmt, \ + __FUNCTION__, __LINE__, #e, ##arg) + +#ifdef AWP_DEBUG +#define CDX_CHECK(e) \ + LOG_ALWAYS_FATAL_IF( \ + !(e), \ + "<%s:%d>CDX_CHECK(%s) failed.", \ + __FUNCTION__, __LINE__, #e) + +#else +#define CDX_CHECK(e) +#endif + +#else + +#include +#include +//#include +#include "unistd.h" + +extern const char *CDX_LOG_LEVEL_NAME[]; + +#if CDX_DEBUG +#define AWLOG(level, fmt, arg...) \ + do { \ + if ((level >= GLOBAL_LOG_LEVEL || level >= CONFIG_LOG_LEVEL) && \ + level <= LOG_LEVEL_ERROR) \ + printf("%s: %s <%s:%u>: " fmt "\n", \ + CDX_LOG_LEVEL_NAME[level], LOG_TAG, __FUNCTION__, __LINE__, ##arg); \ + } while (0) +#else +#define AWLOG(level, fmt, arg...) +#endif + +#define CDX_TRESPASS() + +#define CDX_FORCE_CHECK(e) CDX_CHECK(e) + +#define CDX_LOG_CHECK(e, fmt, arg...) \ + do { \ + if (!(e)) \ + { \ + CDX_LOGE("check (%s) failed:"fmt, #e, ##arg); \ + } \ + } while (0) + +#ifdef AWP_DEBUG +#define CDX_CHECK(e) \ + do { \ + if (!(e)) \ + { \ + CDX_LOGE("check (%s) failed.", #e); \ + assert(0); \ + } \ + } while (0) +#else +#define CDX_CHECK(e) (void)(e) +#endif + +#endif + +#define CDX_LOGV(fmt, arg...) logv(fmt, ##arg) +#define CDX_LOGD(fmt, arg...) logd(fmt, ##arg) +#define CDX_LOGI(fmt, arg...) logi(fmt, ##arg) +#define CDX_LOGW(fmt, arg...) logw(fmt, ##arg) +#define CDX_LOGE(fmt, arg...) loge(fmt, ##arg) + +#if CDX_DEBUG +#define CDX_BUF_DUMP(buf, len) \ + do { \ + char *_buf = (char *)buf;\ + char str[1024] = {0};\ + unsigned int index = 0, _len;\ + _len = (unsigned int)len;\ + snprintf(str, 1024, ":%d:[", _len);\ + for (index = 0; index < _len; index++)\ + {\ + snprintf(str + strlen(str), 1024 - strlen(str), "%02hhx ", _buf[index]);\ + }\ + str[strlen(str) - 1] = ']';\ + CDX_LOGD("%s", str);\ + }while (0) +#else +#define CDX_BUF_DUMP(buf, len) +#endif + +#define CDX_ITF_CHECK(base, itf) \ + CDX_CHECK(base); \ + CDX_CHECK(base->ops); \ + CDX_CHECK(base->ops->itf) + +#define CDX_UNUSE(param) (void)param +#define CEDARX_UNUSE(param) (void)param + +#define logd(fmt, arg...) //AWLOG(LOG_LEVEL_DEBUG, fmt, ##arg) +#define loge(fmt, arg...) AWLOG(LOG_LEVEL_ERROR, "\033[40;31m" fmt "\033[0m", ##arg) +#define logw(fmt, arg...) //AWLOG(LOG_LEVEL_WARNING, fmt, ##arg) +#define logi(fmt, arg...) //AWLOG(LOG_LEVEL_INFO, fmt, ##arg) +#define logv(fmt, arg...) //AWLOG(LOG_LEVEL_VERBOSE, fmt, ##arg) + +#define CDX_ENTRY() //PRINTF("[%s entry] line %d\n", __func__, __LINE__) +#define CDX_EXIT(ret) //PRINTF("[%s exit] line %d, return %d\n", __func__, __LINE__, (int)ret) + +#define CDX_FAILED() //PRINTF("[%s failed] line %d\n", __func__, __LINE__) + + +#ifdef __cplusplus +extern "C" +{ +#endif + +void log_set_level(unsigned level); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/platform/mcu/xr871/src/cedarx/base/include/cdx_malloc_dbg.h b/platform/mcu/xr871/src/cedarx/base/include/cdx_malloc_dbg.h new file mode 100644 index 0000000000..1a51b3057d --- /dev/null +++ b/platform/mcu/xr871/src/cedarx/base/include/cdx_malloc_dbg.h @@ -0,0 +1,42 @@ + +#ifndef CDX_MALLOC_DBG_H +#define CDX_MALLOC_DBG_H + +#define DBG_MALLOC 0 +#define DBG_MALLOC_TOTAL 1 +#define DBG_MALLOC_LIST 0 +#define DBG_MALLOC_OVERFLOW 1 +#define DBG_MALLOC_LOG_ALL 0 +#define FREE_NULL_WARNING 0 + +#if DBG_MALLOC_LOG_ALL +extern int wrap_printf(const char *fmt, ...); +#define DBG_MALLOC_PRINTF(fmt, arg...) wrap_printf(fmt, ##arg) +#else +#define DBG_MALLOC_PRINTF(fmt, arg...) +#endif + +#include +#include +void *dbg_malloc(unsigned int size, const char *func, int line); +void dbg_free(void *p, const char *func, int line); +void *dbg_calloc(unsigned int n, unsigned int size, const char *func, int line); +void *dbg_strdup(const char *s, const char *func, int line); +#if DBG_MALLOC_TOTAL +void *dbg_realloc(void *ptr, unsigned int newsize, const char *func, int line); +#endif + +#if DBG_MALLOC +#define malloc(s) dbg_malloc(s, __func__, __LINE__) +#define calloc(n,s) dbg_calloc(n, s, __func__, __LINE__) +#define free(p) dbg_free(p, __func__, __LINE__) +#define strdup(s) dbg_strdup(s, __func__, __LINE__) +#if DBG_MALLOC_TOTAL +#define realloc(p,s) dbg_realloc(p, s, __func__, __LINE__) +#endif +#endif + + + +#endif + diff --git a/platform/mcu/xr871/src/cedarx/cedarx.a b/platform/mcu/xr871/src/cedarx/cedarx.a new file mode 100644 index 0000000000000000000000000000000000000000..f69656fdd029c1fc7858797add9e9362e4b4a891 GIT binary patch literal 1989960 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Ťt>({OX|@U_CbqEZHITZXdkrnm8%YB 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�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

Z#e$Nm(y$5>$7WofxU;j3ZT)x zS>QY=0G;#fKlTLPY|$pq>N~6D%{G1VsJ=rT8J&lk>zmo@w)l269|&IMXSpnqqY3}h zp;_j(%&oz+yu?LXChM5o|H_dOnOjEIeqvxipL0wz?%!9O*_5eek?Zk&1x5D7+mC;F z^gu9GAC4Z**jalEiV`*UYJ0%lc_8?p_Eg<(usyKz{-QvC6DE|SlP}t?#^%Ks=i9Wb zCl)BH90<1UOnY~`j8F$^c+a3ZOm7}dSDA@=Z}As>ft?VE_OdbW1-`7NDCwRyW5X|o!R+`PA1h8@KxwfnT3f}_9O@?`&; zu|=<9348RhEy)?N_fXSyEmsHA{G*bu4vzN!4B;^TO{9$Xy_xdXh^vDW{p&if4rZyGll-4R%48Lu;JZ4Q?N81)H97WP zU*PKCl&-6TQ&nH=>c1&=#H;VEO+KpIl3!}to!x4?;b$%S4fbQjhIYg307i`ASGLk~ ztKZf#>l;@0pA?(&YRTf%mRq*PY_AMD5+4Yr`c}4#IxqG<oHE%W4yA=1QnjBER&@yGf6BnS=AE~?sfFP<%oTC#%?Xw{thKseS(>rM0TDRRHXk#S4Q zSuJO+yEwSoucNnWd07FypYu}OgPfVPeb&_q*G-Hi%)t6QN6qH`hxd1+%KUw( z3FBWIn9X?w+oG56z65!wBdJ@Vry6s4mhO>f&F9(6vORlIQr@jCmjqXPy5@TxE&J*~ z7yFVuP_P@*9qy+K-ER$5X%9S?K(Fes8%pAfY&*0y&?W&~+5LWMyYEm~z9D1A193-Mop z|2q8hz9V#u!OerUhO+CYW8D{bBT^sWY9->ZIb$2KATr7kXM2OoC7V6=Pq^*1+hh2h zF`F~yzj0G-bJ{OM{cXCV1R@Q-4sVQZB)9H-3NbcY%$ay(YO}@CC%MfY^C3B2Lr$ac zDof~E*wJ_g>qTQ_i4}wOnHFPr*!dsaTlg1L|oGwNyZI{8AM z)8Sl#<(0Vs(i|p(nt3h5E{5$;DGO)}qmFI{T{<<5b)Ji``!#5$x$gwOt{GL(BG%2j zXW@x|fIKB&(cEXj*t$Cp{5JO@EG7;2E}ZR980Lu`cJV;2;m4V)6b3_H)JAvjg?yX) zCR*10A)Cm3y$6+WZ$+Cr+zmL#x+fsUsTmiecClk0hXg0LtEI$_F9Vprz->(Igi1tf zrVH&AJCV^&^DX3x&0oSLxp_NT;RWws9UFej~ zOl7p)yb+c&M^z&nXI{;0X?-ZnV;+TUb2L>+G5H#z=^Kr3n)w^@WIhRgpE;X(mCeSP zxbkY|B=S!&pNEd-!Yfe1Av8I_h)Th8~r~JGr2p5|x zP!BVowqIa=K$$aWf>N^*H8y9myDTx;ZssZbp+mX3i}7<4(K1zL4)v*|{wvI_)S-&u z)#f^+nkyKtF+W4wnk!jugZX>PS@kr+E#`fAzGJRoxYb+>E1IX8a!o^}QgT zKZXn`=BY?@Cor69;>%!~+e4vgN~l{29S3pGK$6i#Q?~Q|EG{)J{Ssu1ufuoL;xZW2 z;v4Y|z_?6??dC0L$+&S0JI%dFj2q8zoEd{k#Z6#KdOQUbG?8JuJ~qX(2*!)cQsh|t zfaS11jTD1Vk>V!%nAsB_{|BJ?GFl=&fkDl@+k=N&L z8WM{&p~v)W+;&4J`M@V?r#wu=H}~`Defe+?Qr)g;2h!3K)lzT0XeDeUm|>xn~%Xpy6a$= zQSM5ZB+XrmYR06zfO+9UZTM6ckfb-F8Y3RZ@J&sep#KJhw*nPS%+l3UJ}=*+N}Qw! zyLSVmBu-X@(|ZOAPs~)>KZZ@g&6KDakjhjagK4@AkX3E#|&cK++yx@f6N)l{UU1Sau>o*ZudjbCeHma zgvGm`hbl7Em_|#S zA@zu14QDDM#d{WJk;GYwNcCPx0kid);7;@2NfVu-QheSP5~nI+oVOJAPMo8($?^_| zZ4>9Jlos!`Eax=+Za|y2glwm)lrHZY)FiP$5xrg=!y~a!XII;(B{YJY`{LBxb-FhE zAFNZ6GU|xu5Uxoq)@zX6`vR*JP%<=cEV@MEd_~y3k5KvoML4~C*iH)-5$FAYZM#Sj z9`F4ucd;T;yu6$zmMS9E>tOjy6p`lTrlrKCitu?~McXEpDPo*=F4{G*ToGB`^O$Fu zBBprL&>s^k6yf)B7Y(LPcC~!(G#aQ%5wpCT$aaPz=6GFH=S)Quct4?qS16*``xcvM zr6Lx1v)L+VDWcT7g>0)8QSLoP4bE0XmA8<6b&dWO3SFTMf0k5@J`o(wo6!8Jo|s2r zBy$ED#S{Aom^8Bt@g65z+a8aPcH^4M%+7dz#MI+ryvLl68hhLfr)b716z+*@gB)ir z1ZQ~?(qSs|5%eLChe6H!iX%Ho$u#{8Co}9c|9~2KhS4H%CN=e>uq==H160m4oZ(b+ z4BFQ-g8V*AUR2lMd%+&>ewO$w`hAUO+hIdN%3glDu>BDXPm##kl(8Ht{; z49A%dvZlTs#+dg)m?xd#Wb*^oE`!!cG50Y(lNzR)e?T*N#xp+6+(6AIFy3eO;p~~n z_;F?*HP2#vmKlrj>zOZ@5#Fys+e)qKc7L( z%p(8E4BO3IT4si&e+?2nGcEmx(+sm1_Lz^L*LY?#oMQf!t$7N=spdz_dn&_e=6K4V z!?4es!Me|7c$}HUmO72$Eb|7IdpgUVVg_0F0*3wO5r&IspL}x^d5Rkmo?(`;DkTih zG$+v}0fuLpJIS+v;o0WvEO!y*&oOIgpHhYk%tW@)656WRJddqhM*an64D~4|PpSDQ z%Bf(u+#FB)S2A2>ato-Zis2O|2bO0!^R6}n0_`$;bIeBUszs20g za2>-JnKN0(W|pXqMONZm^G!tos%V>o-b1ubP?q{kwhOM}5Il?LCjkLlQj8BCI z9MBFsCwPaQN}R;ZlaZLr6C&?Hq{BW6gi0z4B_;DRlS~rH%;d1o0Ab9a3Rf~$+?1p# z=OfDG>jX)DE`g9f0Vzp&d{cj99Bh{y=e&VkJ2~O^WaX__vgb*1nyVlwIq5}2+szV& zlc}`R?16-2YckVDQA>xNc@4@*PPv*q%%7I*Erj{hom%oqmPjRjB*S(~{3wQ zdm19}sZ%m0&n!iha~P_Sl02L5>zgalPRXY-E3Oa7n>>dzoZZ}uUYtCadN|Dh<4>!vLVwkVm9la*W)gXT4V3(n+Xir35|lwU&r6!T`*AwZT?>&ibILDlaP zH6x9cozHIvq`iyaHa&UaZ4{QrOpDo)n#nIXB$ray6!S?8*W@LX;5Yw?nB=AGOe@S5 zNJuVYXL@|}NQReD%tn3mpJ;%JyTH0xAALWoRCy;-w&-E|5n~Uy^69N_Ooy{ z_=#@IK3sQcE6cGWV@L7Pv&THKm?i$yB5Z0bj^c|<7-&xxKKxuy!V#m&eI^bm5 zeW24v;&c#VlMw;YdV!YiM--KttfDOC5T99&_`iq-&uU3>(8I@5yrp1j_;wR#cA0~~x0v9QL ztFaa7%Sb=O^h*s+JbayV>;ssj^~+TH)uegXrC+Yn`Ltjxzp0{cQ}iD|`&7$bZLnoo zp6_<_L;V_6-e*id4HL9}t)gc`l-07^4Yn+Qo}ai4nF{c~5(@ctpf&U-6*Hf=Epq`91~($VL*lSMg7^u;Flp%@DG4AozS*kp_yfA9WtfkYVR#`Lt2P@PR-Ak! zGWKkC(IfEJiT;c_WT}dNtkP}QNj*PR^<;Ld9-pat@HI-So}VlBGeJ+f01c@B)ly{Q zH4w^yavBuzUCg$gpyv4N!KtM~W!1SWkSdi-Uy8VoQSmm3TaT0q+eUD6JTT?56H>a7 z!hU~?N?FDpKmL0Xm;MX_?2vn`L~FEwBXb-~6lu;0^|&zV7byLZ2Tu$^?r=g$%Y+x$ z*|+XvomJakXlL6$&zekrpPgeXbEt0*>N~cEvu#z`J_Xy@zjB(`YG;?r1aJ0+x(y+k z578{KmnpI5gANaV-iL~Prc6c=qP6j*j!`u=2rcxhRoNV*`i@~J#!~reRrW(j8(RYz z;9@KPneYntLFBXA{6}{7-3laXIaN4O6W>CPs(X~! z7N%&q=i-F#$8&`0yP=6+8B4R#SbiILkeGv%RiT?_kWYU~B!T5}-}Da{t}NYZV6zZ&B5~ zDl@AqR+5(R0Fo#`(J>bNQHXZ_KkR)8cvaQa_Br?5+}wMU+zbQ=5a8Y)7H{6j}w*8pW3XefL@C z4k1+Auiy87|MUFkdCq*EZ%$ep` zm8l2q%+!xas|-J6XNLK4i5QPGrq6<3 z7XqjI;kcZRG&pX9Bgd}gz-LA^96T;=!a+#cjQazW)yyXN+Ac5i^}~aW;TP~^ zjk>%HtxGS^Qaez_9M{ZhmZC2W1Zfj)M;ITrfxl*EZcvhnW}atC)F!-zP(;**A@qc+ z;EOkSpgcRPI!55eDwF2Bd`!9sp24IsG9(~?t=IBgim{Asegh<^?tmlAdNY>c#)LzS zbM(AOL^-!>zBX`ke7RBlHX-t9@MDsXC_fn}$^3oj{5b-6js=rtf=ktKS!MLTprzQ( z)QsL|A)z0Pd_(MrFtRsVYV^K_pp&6CODqv3 z;K(<}N{-~(_%qyi%-2R(!x{P)RIDch738&B0pSL(f})YB`{Z$=1sRA;nw zwko5Sl&a`C$6tn;ssC27BEHN&a`E`g!hBURehjsJcuH!X^R=mu;ohceJ~Sa!yOI&Z zR2+3f4KFG@)r;_9$V5mOx*l%V7a)e}YA`VJ6RE_ZOm)r{l)})M>4-MB0sjl?*;Jd` ze3&TA<^c@JyJpM(0hmXnUsX)Ak56gcOgFElxGD+0w8*ItO0pQHE)T=Y*&eqe_vUUHl;~A{H#D zMg#^$j@JmzA-ds-iedyEB)*!nV$T4#+pd$_3XMufD3>D9L*Q@u;tM;&X{N|H9$haT zLRqmsq>v4txS-IesF;X-zoC+&2EnKEW2!5owh@v7Q`vmGo|FlPuVf;mQ%PF<%&q4UoRm2DNQNS}&GAxcdfjgp|& zE2CDa=ZUYla#WGe$P>p*gs=#ihLk+s94a*5I{2zszf!39rE*D45Gl(QPYfy)eC8Vp?#2!g{5tt4#3t!v8& z6l2#gxa@>OUFfD1uU+ffrth?b*|lC!!lD2@2X!H=QO@PcTYivZgf1l%C}%Z+j7)9m z@g$|0y!Z`V*>E8p!hYqfCVXuA5LKaEJpLmTZLynd8^FLFssZc(&3#?r|syf_;0VmHdmioq^{*Ci&XT1;o6(AG%E;^Jk= zGbwZCKsVb9FDv}NBfV0r>1^5qP~D~<@x*BO?1Q<%t;0y`w>AC^qu^S0dq;0}bM}hP z?seJzoGjR5JOxI1v-4ME!{&T_U432lroN8Op3K(H-pnoutnX`WYs|*ywq31lP2Jge zku|fUv!f}KCiF6=*5qa8`g5k8nybJ+EjKs2thJ+W~DrC3UHMH>K;{VaI8C;c~P525rJ1aZzRO`AMT3CL(*H3R(#o3+* zo^?&#Fanz04b408Y`UAQ?1aY;nuM1Ew<31-WUuP$D6cvm`|&KuThA>_%U~)Y(K>r- z*0%M9>Sd`=hs5zBF>+AX_P?OivGfq5>NR+~8dk5f8rZaYdK(*?nzLHi)6~FK*VhJ% z@~|S?13zqXz#?rUasdMEa$O(Y$!$kymiWM*r*is&tWyPT^CgZdE&JHui zI+`(7kI783trK7N!zME*bkN8@}9=WBl+mwOL5!F>2e>QaQ z-0UB>`cjP3viup@zBIpoOv)Jl?Afbk&zWPhp;xn5uL7%2v)Oe=mctn8H8yw+SC)V6 z`20n_)Z~=0zLabg=yi0dB%oW z5GN8Uvggd6Guxk$vZid#9G@>c*_WDaxM!uNjLP<<__E9V{;}EfvS$~hjLFVU$)0Vv zvyA)Co@KnGWrbQW zZ;U^6QTE!|C$CyNHhW`=%@HE+A(w7M&l;PZYMeUDYs6+5x9h#ekItU8$ZzaEZK1I^ z%V*?z%M9177Q^G6J9p7+!__<%&u7k*$vI<6{CRU$&z{{!Tm8JZ8TE2f#X7;K%^K$0Q{a%YdWzlMhF*e&j*FVRITe%)(F(=2D zneChHujn&=Z=YrKWEq|;n=?e?9Z|Y>oRP3_PS_KDI7&}3ZaAIXIsSY<9_?D=CHt(| zv*#G6W*Ko=VazW^>4{;b@Y^WewG4bwt5+rQSObgAlUYG`e4ZQ!{mgBeR-wq((g@)b*99=f$nEA44(tOk>Th9cwFaEJjBYOx|}kH=~<5#$bFEt=cz~U=&wbA7E%3hRE{^wT3>JI)~O> z#T$FO>$JkgjTJBhU%#%fdF^0&tyr92Q&wJ8HCWirZR*lGP(E!+a2OZ0y$5L+&VP zt6SScBl2Z9yF?i%@9Km(?@eGe%Qei%`{J?+t*357eGQJ(>8?{}>~wXvc4Bmp{vFXw zliEv{6)!6en$+&H^n@{YT40|6#=f@Fj>e{qIa(LqOt0ze>P1CU8qj*V1Zr)a9c#Ua z0FMG#h+kKQqf(kW8k&k>%YN-9ts{T^S{OW3R^JEFw}MvJQ7RZ!VE-Fg)w&jRwslrn zh2S4R=fL`W9R`LbB@iP{VBo+g+1=OBtD&e_q`j@MJ>CL~^4>ocY!i+xxB8ewXvb4uVY;y^4i(gThkNNV?YH*5;X;- z)iugnThh6KHM<&y*v$qNIPqrC2Usx$hV0APe$;ncJ*{gyni{?lcHLC z7kBaduIj<%x9>nJqM*7s-&+Iza1P$}9q)K@ia(yF>h#VP9vucs*VrfTI z^+1+h?5P*kR1_~Ms;OF5MJz7TI`A0rP#5#T_O(@_+6Ih-vNQ|I7S$9Lm!Wfm>Rdsn zD%sbTzaH&iZ4-F4`crH0!B34!D)c#(LjFqC*O*;mV_98Kud2)y=*cL!bjkAkveF{c z*r}X=rLquE!F)1oSt?=^RSLKCisI|VkkMmKNVT3mlsvi&)u#mpB=z}V$1$UjsxH0g zI@-1R*4{3RXR2Xo^Or317Utx5)iTb@rJsLh&P;DQ4(w{G>uH+kJqgDzWjD3gH(~nR zm^n4G9%nfZ*HDHZ|1b`cr)7@y832g>4I~{GTZY&_qO!4*8`j3-`ur6 zFSDmBvoU8HecC$fdNZ-!oZZve+}qsc&0M>-5v=7s=8dW7?6n!y{V@MrJvcR z`jOdwcy)AUHf?NZQavSNGy^&7 z#`+%Gay6?+&jQrdrtTVc;VBRdr=?{^FXF z(votmgL4g5wcZvr=BS$8rBXFJP&KtM$FjwXYgm)Z3l`Q?uc|0E)9h+!?~$>tczN-X z>R>P@CNh&L@9M#f1_k?XtjW<*&6dH@8g)VIDD7FWLDg@qSY=WT&1M!gE_sTI>7(X% zYJ${VhcN+CWPCIyYjs>BHa0cX^tPZ;G=>$c6cNE@oS;y*;Y$UfS2x zy=iG5W=Xx9Fuqll6&F{83~-#`qB*xiGKW$%zf&_J>)EN++}CE-9Zj_av(&qr+Ur_7 zG>lptB5Imjn>*FiRE?F#HJU9W&#~*QB8+ zwx$l9^LkVzuB5_Rn%M&tEGwxQFr&8mNURnr>o!1B3~;LDg|Di@mEqI~6Q&Jy-HmKh zRjApRORJK?7ffpSHOE=DWt4;2Wpv>2VKvsPsVc>$u4X)_2x?Bn7HmFy)rq2`Evd|3 zT#S()HJvS38dev_9dk*i1ewA)bsMZvYMk6Pa~h{Q&-?d@1#nH4~#Z>|y*4?B}q)U(r?LK+p@6a`5a z)Fpznj`=cDS*mFH=!9#EN~`h<%8J#Zu_C{4QR$NTYLHQBsfSe4jdK-P9vqh%u%fmq zDm1QUe?a8Jurd=Mz8u6t-USFTN|4? zHFLZ(Gs7(Qp=&jdyzF55Xh!W_T34y;orNuR78F&g4FuH%HFoyZW4UUkRb9HcxH8zC zs5((tP$}IJn&hjs*6L3sa*wG*Y<=gL$&-yl^OGNQ>=Zt2x#iXGC=xt494vx zXtbIvK()SZ7A332V6}>~6^<=@%K7cx$a=L_URARg2W)O+*VWnE+Pq24Tk{LbE2~4g z*|1hQ$RJWxT2aYy(VT{w6-6!na@k)MV;0T{^r?d>mCseqfHAPBysTgqD$@MY>in{r ziZZN1`ns#T`_xFpCNPK!oDW%<87PmN7}k<4Fi6oe*DQ5ZFHEW%)Ow~)O{!EyK$$eL zSrm70%&0c^h4Ks7&N_;k+WR&tuK~K_ToB~BvS^iNwxK{TUe(#x-O!|&g9o~NEXb_R zTP3=8MitAyngz!&6kGYTtifn3HSGB35fkn@ut*KEenp)PW zYJi0x4{F`eTiUo$2Gu}~YeM&=p?|0k?KCJSFeB)x4y*S|IO{Fbnu@!-vGBw{05#l9 zZfjcK#3?{gX?ac9hDl+0`68{Sp#|Nixvzq9u3FdHRe_0xbS5=5)-u){_0W}ebB2Wx zDeNR%Hqh?IKpxCORO6Z&tT`%kKf#;@Y3+3z)pi2cgM;Xmk7?|Hr-q@cW@qiVg^${CAUHPm1~AOqYPJDxtvp>&|mU=G4r26s-Xt5i>9_FPsMS+j(@ zIBHE?T#bgy$qiRO9o##@Q?uHU`J~OY2FRgF1zYU?;+qAJ%& z4`Ye)t6`3N7<_)g)oBeko~GrY`9W{YOal8}O5H(niCxb^vuQ|I$(}WkV>PmI4a3g4 zd|9>D+}hpKt6B<#H@2Za;qcPHk&%tAp$l7URcKl&IkU)n238oZs=~)E)m5uL-5h*W zkHITB5b{uEb!(-A}u!Q*eUUW6qhe0oAmwvI$FfOhL^!I1L+hM=hnWUD6%0-P?gxh}Pa-qx#eu zRfD>?x2ARRY;0?~f^|SWHc>*KrdoAl6PC+GYvH_jS#|Ns;8a1IUs!m8H=R2()t#Mf zJzh-zyx4{D7RxS-7yt7&U@6?$-81pOym7;q9r#kjQCxWW_6!eBSK+Hf*0lwWd{5GO zjx7;e?dKa8*e=88h*ltwFHGcn;}jFFoYqOBDqk?-u9Q z$nzZ+L~OHPXk0Xqhc9@5=RD_@$gPg^BQCISGcF{L^#TV);h)+txqJyEM3>6nJpbIY ze0?fZU%ns$p7ZQmjIETH4%)hQ3&hJ!by^^10W8;6^A(w&wsKfdBQc>_SP&2~ALrb3@E_()qvt|S@ zLyH{{9vcjV%fLxl;qv3P6XCxtz@@(M3Qz{P!}*6O{zn7F9v*7oz0Gi+p#fQR7%2Vl za6XOTWuTUahlkdB#vd^toNqG+FGIt}B_WrAUV!-vUZK4L<&6mj!DVP;2k?&_5bhfg z?i&!EIv_lCKsakf@G`XVK>{vA)wZCQG2j$GLz^57RQ#C(=xmifU)40P9Lu55P_;AY zWoZ5&0hggo2|N3nazbCuCe8Qmtc!GwMF2_n4xmpTKyL=k_QC!$0qx;tq1jg^p#DDz z+PWz3b=>5)<4!>RXFSsEClgTLoh0NUe-dbVU4lCS^*vkY-Ket(s67h?@YfBX*9$-U zxdhbb3xsB$n1K3w!vOvVB%CcZ0rmIiLi0%{puY0kKI@`>p9syKAs+SZE1_94<56Gv z%``8Q_G$dIj~DYFkAshg1=^lj-s1<*(+1Eb1L(j+E4Wji)_cr0ePGSh5VGx)hJQcJ zt!DEzAFP)e)>#WXnQDeJ$c~vT+tlPuZN&YvKaUi{ac92A-$YRH)D+BIPjPNn#eehz zQ|GXq$C?^7Sk<&Owywt>>+#m2$K68`MUI{S4x9+8e>^ZD$D&(lXYR5Oz9_6$mfX0@Oeb&Q9@i~+KHk+XBgCn{SW0Q6PXEIwh3lG zKx96N?01Odw^f_;qc!f z^c{i^2rB#F2qIipOAUXX#6UT_ z1fLQ7h2TqquL`~?sO(cC-aeuKDyZyJ!~aX6j|xUan(0Id#tRM?94(kCI8iWLaH`;m zg872;1s4le39c4Y_64C&kIEi|e-iva@PObUL1mX3>HS@3 z0|N-lSJ|Zo#tJ=DaFn1=aDrf#;E93@1eINC$f*~)Q}7JI3k9zhyjAdC!CivC6#RqW z`+~}@H01w7=w!SxPW{sbrwS@N(eN)9`eeaY!S#aY32qm>LGUiYM+Bc0+#{&$IYa&d zq5B0RFu+rv1i`U_8GAC7f^Q0bD0o=# zs9+Q(8k93!FjX*Hkf(1je7<0nV2$89!3~1v34T{_hu|H84+uUf_$$Fb2)-xyiQo~z zNX*%&#}L7>f|CWO3l<8N3!WsnR#4dwhMcp7zF6=Y!CM7?BKWwVvI~rOzZLpVf(Hb@ z5aihq)H7Cal;8xxDS~qa7YVKqY!K`eJYDcc!5<1fB*??ancj2e3xr-QbfwVW z5xSlTIqL<#EBHgf=ZQ#n4-sR)UZHtfFw;9I^bw)CQAoZaf|CUch>*kMKxlt=rO*vT z$Y~M(Zt>??Y&O){ON73L2)^ruzC-9A3C+V#8UBROKNtGfLjOVNw}jp&^k0PjLg=rB zj!ZP=MH8X-aG{fho*;CF(9?yUMTFj^;$JQPtHr-V@L~zSniz-qn9%nUk&lOj-YxWV zLcc8Zt3tmo_=)&`CiGFEc_aY!k0nBGqR>7f(#;Zjri9NG|0=zfEjPK&^1KF?-AsA z#|*!OhWF)|V1{6^V7=f5!HWfV3O*|Mj^NjV zaU)H+BLpW1`UUfeD917)HgQf8x|xV_S|{{+p-&h3e4#HE`f8zX7rdJYdAlY2MZq@& zKOm05K9$f%iAc{r3J85-iJ+5+;L8v?N9Yp-iv(8+wh5j|Wc?s|HSG$aZx;NC;Likq zK}>;7OrhT+BK;4=|L=mMktgyM5+Q$yU=y(t^+M?N#HE^cy3iL8E6^VceJ8OTCWM5( zPw-K}Cj_4od`|F}g1-@bRq%Dey@Kxx9uWLQ@Na@&3F@Pv2l5di7%dnlI9za)AhxB{ z_=n}bLJUm`F^y4(N2L%&q;R5Orr<2Wd4lr=7YU*WRlMbbErM-=>jh5}yo8AHbGzU* zf;$B761+$7A;F&t?iPGj@b^Sy>J7nn1V0e`jEM27Uyy(Nl7`JWg>FHQ;84L4g6V=8 zf>Q*i3(gTN5L_y_Oz>pETER}i9>LQD&l2PTx76=)!D|Ki{}BCo;4Se!!Ji8566686 z3|ISpKpx0UTJ8G*c>pfyLxMa;k+jU;9dGp7t9wd7F;2Ck|2#1kiS{5 zUGP-Fje=(g@_1hIUoFT(Z#f^iNAMxRp9-pdNrb;7^zQ}V5aj8%oL?Li{6bLeSHj=M zeL|o^Fh($5Fja7ZV3uI6;7matRZlrQO_x|DxLS~hCeojVcZjWmJZ+cs*@EW_s{K#+ zUoP}jg4YY)BzQj&a~YnvPuwN=l;8`3zZT>PyySaV@MFPG1s&YCM7UcJ#||o5?VrMb zywH;ba|EXf&Jvs_SSh$%kVo}Wew!eVuH`&qi{QnA-xa)C@H#;rZA<=p1s@T7T<~eZ z=LLC6C;9gXz9#sVAdkjn_@{zj3jSTt&izh=I|X@kF6l9X;{_)P<_Jy|RQtN%Um$e3 zV3pu%!8L+BZI|+U1y2_|TabtFGW;sR+XU|vRQtaOe_UuD$V>j;2=Z`D&V$r`FYrU5 z{~~xqQ0?;~+`)ZMApfHy^2Ar7S8#$LPmiZRPkbe+{am0r@CK;%bAdd#mGh@s!L@?x z1iJ;-3!W)>jvx+Wd$qt5Lb@>g)M#a!4pY z2MHnZ?OexNyp$Z`b*xVtKa3ZN&{50c=Z$<7J0zC+Zbr+(E6Z7Ry}jM7_4ur)2etKE zUs%?%si=OHk=RZ{FPjiPAo&cT2l6qD*96>X-rvaQ2hYHCx4_T3taRB=;;$=ho&s{9tFJyw{=+@Jiad&DsLEwH27JU zB@gO^$;*R0#-}`@C65m!R342behNS9vgCEbBUHaxkcaM1$-`e|0eLsV9V#ya0{CvC zby@PRgGZ>mGRR{eO#Py9Tlzf>cc{GOc+d>9E=%4A@CfBw4ZcYh6VT#&2ky{xw}j+_ zdj(dy5fBzCZwutH@3$^X2OUhI@~#XiXKeWgZfG-G@Y;6|V#G_`IwO!RmTS_^keb z?*$}VOhBugxsMQ<-;E0%XE4FS@^7U<4{;EFYB_> zeHVP8>0W|#t#&jP%vQQIAzjdZ(EL0NujxUGC}-y~Gt@^Luj-%CQS;(>8;T=}y3Hr8|Fsyd!wHn?)AEl2Y5IX2}@#BIr=X=^JEYa1Z%0OWCPWL=iLmI3mdT4oPU@s7Ht;^yghIngv)ew&PJf_MTvaZsN15&q8 zdAF_7G(IEivgBpwF-g?|aQKeeS(*6|xt)G9@yQRQ*%>dq15c#^E z8#C$CJq!OB(} zZJa)C{>Dble$Znt+W&|6*ojGdp51uP#_j#~y^UJY{(SEl_IdmN7-#?NEPHCXw@~w~ z==aULslfKKo)a^%f6s3YKW&fQ`*Zw8?{!>1YJvSA-r)09^!qAy1$gvvSnf|qs=ZKq zrr*$}*XHlv8h^vaiG}(5uhg&YdVS;5ho2fpYD?mXO>cGO?|)Yxaq6l~`TGkJj*c%l zIMz0%YxSvXHeR=>`Jl&9f6%b4>ALRJgOaz%iE$6b?s;ls;ikx3-$BC;Sw3H(ad52h zRM(!kLdeihEjU&4#_h`d=wSK9_eM1|`%n90NUIL2wIk@xufZHr3!Q|H<5 z!LQ~Sbx(n}$o5A0!K40n_UcoN$>WOtvd6#IHu3Yfjg7X+pYGB9jvqOHVx*QI)I3G~ z_D?P@aFmQ|alGl@XcR>hU0X1&Hu{Y}?Yp{P+x^6$qp{pdKD6iP)N!@O=bL*z+;>NT z13vHW`Nhb{9ro=>Ma~@&_;p?G*#35Z@@6fy=Bx9*`sD$)FTdCTpZ zai84(^8=B-(&7l^A5rqReXeV@%b`V7%~u!yO}qa20}kKK1yL&WypqrMxeqwCY5o~S z3ySoi|N3;m28^<(UZM(CQ?*00B2RB&WZ zc}hyx+LZdPTe_-J-tDrb)Sr6m`cZkQC7RcL=xFXyUq!L&|OI$3|SYe$9WH~QnWXZ!8<-dvkf_ujd;^(Ss-N^kT>`=9MkzxU2V4v%pp^3cx?>9OSp zowm!5eEO;Fd$0D-*t-ikf3@F#?@e{zf6|%Su%-N9lDl?ML%nvycF29ibNHP)gU{1F zq6ELLlZsNnp+!IQk?p$jgBk8;`t7?H_3QJV>5seVnf}Dx2iECR4m3<|_Z-qa`iVCd z+Ye+0`5C9^=uh<5 zZ@K-z1-dQemMxJ7^7WhQEig(C$a?mD)0V0|CF}+yK6OXgsKhA?Y-X0(vp%D`OAyB9)^2m(T^}^S&ny>FyL-} z9P@0K zehxauoe*uzXri+MKlazQ;@9T14}$>2TmdS=?y&!czPdw4CQM7d#%@H6h@-#3PqHH7 z`3I))Ig*b^P|gHoA!4|4(&TBxNaf_~RuQ8v0dM4GU~oE|{KL@sF}xivI`P3-0^Dx8 z?MQ4i(BRA6jwm`^%4U3|liOTwR6qQtfnV3yhiZ{gRY9JZTgX!k7R~)5WJh-wg5Ty| zicA>pyYOq*j4dd^$RT*PmgJ!a8S7y#9o*L;H@dqB@@?)DS@`b#C`r3J1=1qi+!1!T ze}dmg_gBcFQ!{#zjmWY5YcldQ@Fzx&rYbh-HdHiZlzT@bONyf^}?)(Ab zIz5?immx0yWi(D?@|j#PIP6ryEJK&`cL;QU0DjH=3?%683|1cZrO1Tg=KnHwH{X7Y za4$fv9c~&Pj?@eG{Q~D}lsSz>Q06RV zTGLrw7CZUeTqisP9V(og=|9^J3DwS@(_KaVS30LqPBq;pJD&olYbD*a&Iu?X*D9vl z=;U`wuGN>o-Qvu~Gk2|_yVdzS^3>6Nij)5pyISd9=iG#PI`=UV)@-4h%vS{A1}iY!Uaw~(wmI#LyL4&AXV zPOnPJoxr5hA)0?eI_#7bc?O*6QBlmiN+2rQOyGW$c~s1w5h2leBY2`bbSF7i(?5PXx~OijlqExT67AXS`5yp)S^@AwmXlIDUEKY^CkpF zkE1)`yLjMI{`i(H@9A5-NWG% z=YA1cig!;&IV89W&5HF!x2^8Sb8q)RWww!ac%02C9v8^OgQl z?jJ&avil$4j7U5UHS=t3s11ZCUguwe!+r{{B+q#L0uaObv5hBPKZ{u#z7NTHCMv={ z{7p#lOj3k%_^U|VGg%SQ!wu->$xuYxaK5GM$yCA;wIpTdcsgWxhUkYO-@O_MYwlby z>FzTT&*ol+L=5-6__e!F!Ec0n7Rt%tJ{_Eq?hnB3bbFCAmzy6Iy4^3KRHEGcQ#{)J z3PNMtKS5b~v|*LV>~@b|-ve^^Wyq8#SFgr{)P_eQif4-CV;pmsC;7O7AyXwE=rc9X zG|2~7#h&SkNF4qyGc`jINy9rRV5Ytm+{wd#0i8T2s1Wb)D@dHEh_vDStj#k^X_G$u zO~#q6LRyA*Go3m53xKxa?~-k<3h5f&%$&?qMDOtTn1y_uRc#|w0rxuyR-MsYZD=nw zFHnUVS-~e58!-(9;N-4YY~-up)|>|XW1Upg9)qLvv{=_I^p4?CIBp*C5&SSkEn#nTL{zjiXa@HX+^EcqP-R)15%K)0u!r6g!0VCfa#7 z9$;)D(~5Jl2Zv190VI-kdH>{z;^oomSNErvVJxt*otqdUPFi3b;(!qP}|-b(*eYMA8I znd~_FCp+K4la3uvf3Ncg_>G-F|1{^r5pbu|Kizp68ddD1JE4i+$@ec}Gno2H&DhT3 z%X$C-4m&k>*cl;`-$G{Sl5j=JAaLI^~rQQojahLo=JDK zvkX;R&!Ridc^gFP*~M@tI=4`cpYCDKSCF8dL!Kn(AE1JsOLwyKT*jS39eK0lUCIR%I`eEK6Pbq}zdrl3fX;KtBRbEcTXZgTXZg_ zTXZfdhFf%=Pq*m2fIOmeDcz#;Lh^{ti|9^q-iR8dFQz*U6Fj(=&^_MFOT|{W)6Kjr zr8`42(wX5Ze*QD$IhH4zjJ|da9-W)-Zt3n(s>3-yf*npAYKGnYW0Yfr`zqDpL?dD( z$^dzDSE8P|+%y*(?HECjI)|h%&OC%)yXRI9%of)$n~}O#T^zA7NeM^%idZ`U_BbM*oZw zA7KrhhkNv`tWb}V54(@r=;xrf{!@|<;vSQN4Cs%MS+5#>DT&AV6&5{4moerOWc{4v zsqoi#ksOM;JN7x&izi7=CCPV)OmYoLe$t`uX6RQSy?!`&k8NcV@6g|mLc=E><;bdT z_K2h>;nuI`)OMs&?L?ibaHDr54*Rh<8%e92m@&74PT|+Qy4iz5?lshBxhgNp)fjkv zAyOY%PSutxxerjzLdspCIQc@{$S!KMQYSB8a2q*-m1dRBV!0f2Y6V4|q?aPxcKN(i zzPzWOtgljJ8K2U3lq`N+o7Tm+YZT3fm-aDhU5)D4A0d5+TGpzHa)fje%eYR_X$VRi zPr6>wOGr16Zcua`=y7~A^c>`8KOV+-y<$i&zTr>VfE^qyWs{b2nwF9}O&d1`OqYPk zfQn$U_2L(nhP83M2)GIXqeB8JZ5tu?V+rAObUch{V`F^+-@+s}XyZ)&7v`|PfP(e# z0WtS#+_7mMK3qO#p?!_4aS=9p^YW@E5e-2j`JEC%=;kN8Bb7Pp2wM*_`BTv8Yz!$# z^+WKb^|R>WYzbig2Lqi5SOSJWGLT=|=qqenLC14IpID0@HayL{9DMh{k@hn@5dFKh zBnVgvo{8%bpjeO*j++iUJ7b5P-J6G!vcRb_5v`@pW$Lf%^WbILLyj5)r-zgIWfmK1 z?@LfUZfG%snBOidC|aA$&29ZBweT?oWPF86&O5#(^DuySW(R_K|ECNe2P_CHo3+jm zo4?4UE8OC-8=bwfcpPP#Oa6by@Nv-ktz}|YY!{CV8tLUXBBR9Wt(XB$4jAW+I>zep zXo(2}xr{j`gv1{+V}sUml}%*JPOvW^@mM>`DG~>X(^zq8khDQ!S_p~sXc{?SR(bqI z=i+2H)2#9YD*!DZXcu`Re8rOTq@Zf1so%+=9!x4DXvmkwplJX(VCok&k5>k6*!m-| z4IXPwl5LDe1&M0N(kkH!Tyx+;d_p~LGd^8yW&nXB*0F*U6~j_Oi!dzRNmsWSK(Jzl zTU>lhF;o&PhH#6EzeflSbrQ)DYN!*W@<+HmBph9G@RIn>kZ^Pg=2Z(|{$O*l89;^M z>;l|nI`OM6t4LwsmF30~ipL*oSL)8ABXiQ`jNG72ALrXi?}fM%XsC5S82!s|j|>X~ zU#hM+8K2?o8~KQcranul4-1gq#EWo45&Q-&FI+Y)XD88&wTy7CcoVP24NJiIRTtAT zvtA)S3exe5DnOWo``9P#R18ZA7DM<0jaCemgcRI{*1c&b9Ts=^(;cI@D+nfc*IA0& zZ~9LItTCMgmV~Y?C!AsW63#b$3ENCx!eyo};rpg9;TGJeE%N|@%S>x2f$1^NOO*}O z?}0)=6A(!PNLW`$Uddo+mp^#66-JE14MWBRGm*;BM5p@7H&i9IeRA^^KiqT4|q9l)*4PF(s9#W0;~`>kvole;ye^) zvv}VEyi44~U*d*}gqOveXuf|@i8Ry|RBsv8n}-|qZ6Ux^W6DnAmCD=vcNWe#$l)e5 zG$2q5v_JyCCP6khrQrca4m^C!jds%gg$k-bbafSgq{_OsnqX?Xg1~Y_eX$sCf#Xix zEC%9_a3cwV$+;RXbv=t;(`$wJXnO-*0eK7>i5toju2#;~1kPT}wxe9A8&J*;@ZvtU zQ6;OGO9>vO@-l)YWa-YJkY%Alf(?(I5}8nTaH)&?ze%`l+Ps}aK6hBztpxC3ph$En z?xROX?j*se4lNpIk)9o>ZS>>T5=0W1uA%h??N76+!4FrkyQLbu-ZDY8dF3=2RqiE7 z!N0=49D|lkdkwR}|EJ8i4sN3Kf0?z^f5Sjb_J6BE+c3kZ|2gxmA%<09lP@>RU(wdL zwzVVMvd8*AS`LHOjN=#qZ9p?T)_y8lRnUGa8b-j_Dq7_K1zV~(ew;@J(qt*tj>lS0 zh37z9r~&JzPWA^8s+%z0GxW4EDQod(sck-NX0EbDXnMf5=Pq6MPVpm5cX%B+i;W=* z*ZOlNUubOdj!R*T1%>kpQ%7f;?h}kCuWzg|#A~#BvyB9=5!JhPtZ%MSKg$^7HR`kY z+v+uvyhaOtvy7H3xYKN*_D&zS=|e}SP8gHY==b?DeQCB(6RWS=bR%k((FehnDb$l! z&&l-psEKjfEW%Xf-sH8crDi$ioR|tfebEw_9UY?-g{@Hd7=8SqRaV;!BWfW`wED*S zMyHM=vtyQHr^1Df^At8auI!$>%6}3F!?9BRX5{wyec36haFYGwXQUY$v!Gtes*_c) zSD4Pr&i1D;Sv|lsY?g7FcT`S#vgw9g$^UV zd2*_;X`wF{zgdtx*6+`oJlUUNxK$rmFdegXo}XG?Vs$=&d#1Q zHal-N(wjCLIuvBj17Pe30Yrl|9q^x&~T1AWwCDn>uX#IJQj1cBz<^_&3d! z;;+3B1Enxt0;OR~1U6NRO}k(n|J-KjfCJ}oVm}NPSawSPQwB=e{2OQpZGEV*zJSFK zu|yg?r+wg|{xo3JCsq~*DR*Ulw5EpdozyHShOwK}YcG9wCTN~`QnAUM&i!H?7 z-9U4qA?88{5jfC7Xdgeu3L0Xu40HBq0W+bdkU*QH9Z;qDzhL4MtX9`Yi!jGn{5-~_ zXQ%;Bng$-sTxVDtsH&>edb(h5t!86uSBn&RkLqMClb)#5L5rQLOHl@{!p(CIZjrOC z4%T|s!rWS0+yA%38MMs#pR>dn_AD1HUs-s}Y2E6G>3_*)rZSJh!?6QK#lp;D_L;W1 z*zQb*|0cVb#~Q!vZfX{L$*`-2-gb~y`pq^i1IAy_qlB2OWOu@T8kPMU>{XgG0$64i zJENG;Xkm{*sa3L4D_^^Iff}G_)E7PHaXQV){QrXKNtITJ#mR=w4)l4JS!uJFlo9J= z%uXtcZUM`Z=93IF5E+)Is)9;Qb<*r|P5Up(995rbhw6CqH`uaSIlMxvB?-T$B1y)-BI@TP!&ULF!ue6mKVb;D5?@s z{W=Q@jhi-DndrAMW@ybb(Ix}d3d8aj>`E--TOmdWznyJCv6}WT84V0fD~>TAI7ruF z*$n)z8VCFjnE@O`LmK|;lgGoUglQkX1&F{fwR*rL_**8hqXJk{@^mo4dTLb-J`YziNts;jH8#Ilxu zp^NgWLRhJUP1AqfYTiG!gNHK9ukXPSdtCc>VckX9pcej=?YmIxcELrkW|_A$JA8B8 z6RRWN+N|BNX6!<$6;(Q(RlB}|O*jC6Yk{VQfC)ej`mke$sV|m=Fv&NVwY%_4S6BRZ zTdyT+ievxr8EW71vjUldmCu$V!Ge82q0#%WD z-&5123`y6tGxn0WpXZo$lLoT(^DHLzSvI`= z!P-I`Du^JBXEXVTqw)5F_-6{9Aeb+>K(Jh}TCh&AS+Ga&G{J3xmkNGg@K(Wl1o26u z((h@(J%X%D)O(-cr-FYM#S(FLd8+4iC+tE%Xb5zY*m3DwNA_)rg-7qQNUVS};j4P0%kmOK^c; zwP3Aan;_4kq5SQF*9+b$_@Ll!!Cwoi_Z1LNeMSUSXJG=rmT)JA4a!Xv^a^GO&Ja9F zaIIjEpgOw}@h=wo8o^rypAb}MQG)MxLhlv)i(tQC6yDLLKGOsX1o;{>{Z|XN3Z5+Hs7 zg#Lx#OMg z5czHpyi4#A!Dj{E737=!=ryr@FKyh1%D{`px|!7 zUkkn__`cvl!LJ3KZd0#BL9d{))dPKJ2wfytA$YQ2tKeqAO9a0!$nTMu?|TKG5PU)K zcY=Eb|038gXvg%D@#6&ft~BY1f_Z}T1j__h3N{LM37#RiP4G&=n*@I(_^9Bsf-eic zCHSG>=YpJYC_M$$NBPh*S?CnObipjaJi!wL=LwbwmI?B`SIS>0sBHa!?i9LD@N~g* z1h)xZK}278i_kwN;^o4}1)masW%~zp;|-zTm++5-J}h*<&^AoxDJPN$eG&!7h<~ck znL_6ZJxl0(p%)2VA@pjYYlLnUx`PP)Hi-W@;=fI3eo<+IZnp^iV+p@s=v_iTE%b{* zzasQrL1jY-Q)_;a!F)x)&3w9vpoa=QQs{9)PZD~n&?g98Dp)7}%|f3l^m?Js5&C?g zFBkf1p>G!YcA@VR`eC7;68d=}?gN5$ETc$|5zG}lg^2w05wY0VBJ_8O$nTXx?-cqr zBKYnT|0l%%De?b}_`fdxe-eLwG0k|NO8DO;oPNBsiO^#f5$k~ELZ2$AKK-49H_wGW zUr^Z^LcGg`zFP1)K}?s!&elb9RWy=U=o1_#m?@YeIFE?>RwTGcuv`#FP^oYnEuyed zuto4R!LtOn3SKC9h2S-UHwxY&_@LmUf;?G(`aC1ZudPVGF8Ge%2ZEmqekq6--V{I2 zL}WaEyGO(+LkfL@;{^FJAH!z~&J$E;+rod5(B*<_1nUJ`1=V^3{A#@c+$R1v3En37 ztl$fRd>@?Y|4#5t!9NM^6FeZum#fLoSBr@b&f9@*!FWM_qe=hKf<8fhhDiTB!9pVX z6P`gwTq0O0*eIyhAqejjx<`l^ST3$1LEf}S9>vKavSM4<}= zO9U$f)%pi~%7y??*)9e0lS=B*CwPY7Wp$?&TL)%pnZO+qW%rl9W@TG z0Q7A_-zmt?P#K>mfe@b-d|vQH!99Yn3BD!xp`cp-AwEB2rJSRJ{1%=xkI^8;3-ag> z()`Som@eoS%o99OaITey&O!DyY_}pnXE8 z3uXu^TeAqCEi^x6Wqh@s1)eN)tsns34x0OnoDagO`a3TX{ZKj)<>x0NJ}>g&@hQY| zM)8AHAz2tNQBcW4d~C5PIbI@|(gZ(XJ7S($AJETs6A}NrM8vb6kCKPTN*?$#7$3CK z3-QW?t{@_QqtI&Hg`Dj|Ur9vz|K8c^gKeLxld;vw>&N|v%sfo|_RRyZ}kW3|35o?0;&S*Fsh-t@&qlwv_W00XLPf@@FHI*FRH7$ zST9i>wRws{KQr5u?*&aB-b`-^?ofHGr`+SUE=%4*c!bK^93qdpSn}H84wc9G7^cU8 z3&Xp*F2`>uUk3>*FvNK&+@a}iL%Qkk^NPQf?meJG<#nMTxmRmlRzB{6N2t7eA&Q*A3$B)|J%ug|J@m$bYh| zbSHq%ibEOajt$l^(pFe19HH{|R+xRH7d(l$EqNylkoPp?v5s4pC6E6bgvyIss%a3Z zd2u7_0eNKuQ{CWcsONkb z6Q_H-=4(&-X4Q4~KXg^Qqa?N@u_X1B#|wOK-rD+3Yg6m!qF;@;p}_e{`^ZTpesvz~ zX`|{|yGH1HPY}A{B5m@5J+_o*dDettdZPioWXt=d?Y|)|-J0bh*k(R9ekbMxct+X00I`C6l%2i5oQkdkc z`jc}C@7p%n@v`QN@bYw2-Z$^Ns$a{|Cx5o5f7gtX{$1&~Ge7Dtj4p{N(bCtvd|A7h z2QBlC)`k|_o4!*nEg1cF{VDI(jV}5yZNh~5H``Lr-JbG6?aUJII!bn= zrZ(Xm<6PWR);8SHu&?3Sj}lUI_f9T)x3!_Rq4lOyCKa7nR66Rqf_LlEi+uIzPrfn= zWvt%>*SmE%oz`;oRSv@>%;!@M*dVwH{M*-vU?=-dXi@=cg=rhoi^G3 z(6F}))*0)PeEJdFl+`V#-FI&LEB%{&l`X&h$X5KyN5k|V7C4{0sX%}E>5qmLYbmR1 z{iD+=DLOMCL9k?+-|o^sNt)cRXbnOt;h zy{Sz@%jBYlTJv;b(JzJhyt3r3g3)j6UH2OD>SJExpGIEyAg{5KR~PacF?NQA5M~`fFQ|AB&p`-i-Ie}S<_OKmt;_j>mFN_16Y zQc7$-{Z3QH+k5ocd-uGt55wZUUAz8-PT1^g>^lE5i~a{%^aUXuC7TwGYrPX~-=IMT z!#Y=1t>kSHmvaNs7TJJ73Fpe*PmY5aFAVEkSsL{+xSMEL=gQK&0M3Yz_GF136HSj;MghfHBX9l*dH*p;lGLw`)&}C^f4ZUb`j=*vPi>2s z(L{y^;B>?(Uj8HPh<};BJoDC(pqx)Zf@8RH?uROlk;=J%(MCN7US}ebbVfQCBesht zU`D#=)Lfs@?WUVY%%%ek?nOjK(dkx4A32>o6gJ8RPA$q6$4=JBf{!zbyHUau^JDU; zmp7v3plI|c9*<^=;vb?$R11D_#B4lD);Z+dV634$$1&5g`8s_g#;(h4@h*5;~B56XWZ}@&(m*l%qAo| zC(zsJ;_-dXbY>;mbs0-zA`_2ssq@t)Ukn}|F}n;H`Des+ehK|6Tu}^gl~GtUHxD(} zdB~kBhDQlE(QtmrMSDfC}McdLshcDn26K1J1ccPriNTwMOU*U{bP z`ZLpNqZ>!eQXGpg_9i%Z#O&8V=K~Ov5#?lADV3vKJY5%u;NmCBoeN=!syL&f$dcrm z4*sa<1Spm4+5>f>Vp*JCl~hy$lj0Gxv}@_GQ<9V0gz3>y%)CkPjxEG_2n2kxH(>!7}CY5fx zi$+&t(&%=&)-Z4!-O;Y2NG)bOpJZIzM5Gflfo^rg>}2FOCS8%{5wp)B#Hb|GBrhY! zd177!x=upYJsvtW*A1wto>&#h#m#t69NkXW9)x=0>E;o$EPmrdl%6NyN(4AxMXUv$ z5o~O#Ts$MqaxtR8GU`IGB)XdEPUePChKop$45NG18cvx|ziil49 z6N)`9XX4u zP(*s-LMRxwR1q18<53=Qm5T5u`j8t`CaRN{_!AUKT(u%*B(5Qwd92^8#3-s`p4>Mt zaW66*w^FecCH66Ms}!*y@gXXDk|N3y?;_jDil|8BOBZqGaedW^AG37U=6c+WP<91#caI65`gT4)g;uVlJTWLgPyJ6$~8Fy1^p)kRI? z6RBvNs}iLXKa}nyS2z8Kk>86tLUoM}P8SwVTD&^NG}`qB^_ju^#<}ieS~KZR zbUn@zK7sBeR~M5yk?v#{-)e}TMYq@W1dDJs-D$3!_>G@Kce?8uraPDEX1LZc@AK&P zyUOVX25e|NE}9&b{Z}MC{l9`~ScH^L*#Y$-CFyYp*@;v(LGEuXO`?me4)ddY$Rk zQ2u<2zZfJfrMtx9?wh1#91j)NH(ACNWOcUIC$@+`HUqntXrS6cZjZ#~_st=HJ5 ztLa{EabP8_VcZj}735z__lefal)pY3?vpG&P?L@)f1~v=JM0GXv{={Cy^-!#>*wS@ zf$q)LM@;KPE?U~G3&?X4{ZF%YQGP=u+^1Vhm~IpO&$N!GdlTJfS=6nWw1sKyvPxL4 zHkNU>HHCH6PM(XcILbVk?yp!KbZ;g9rPkM&hYq^0ux??VyU4TG`UmTwebNcDCc~7Ij}H?V$gi)X{$ zxu?PHxBiU(N$0T^;;sGUIsfbMNwi*I&UQZlcZ$W|6q7EX|5)UJ13KEr2|n7#OpIaV z%itKrfAAa$i1wZB1l{cfjrtyfN0CG@BSrhZ1;Wd%9Z454R($TL+}{DMBDB+}0zPRF zek&3jRYv`Ht-m8`vOS9#$1r=* zKF0M@PI3y%KK2>JmsNgpA@_j9m7uUjTmVipt__vxi9;m${%=5RO7dLp z^{}|nGx<11HLO3Q`IF~=2Tq^0nn^5RG5yv*>A#Tv@zx~zFQR{<;iaJDl77lO2%ene zGQJ8}%?M4dpm@XjE9F;`KgF7eN>8pL%UE0gwgq9*2}WYw&A`P^B5>TtaPBsft1m~e z#T|UfwX9ad>OspVFQu?ti#v0Ymr+82brF1$m$PNpThv^Qg*HO(9Y2BYRTQ(s9M3Jn z$#qxb|7qs<^~_fNUWA-!j=zveuV+)>=l5veEaV~D_b?=Q$D>mxAD;rY3Fo1J9*b#v zsmnL@bkwWIVt+O!Hln>vS$>gB#cl43MbtEsrbQ>ONqvxEljf>4E>-ME=?skaorM1r z{TMptcqYT^Mcn4TXYjxa;IJ$Lu7*b$?f?(u*?jv!rqLvdDY|%H1DRfdf}5#~_fL=+ z*O8nkLNn9g!4HSrsgnLfRH&KFfMbz0IX!X%-pI3|_I4Zo*LemIl(`W9_;Hh)QqsAk zH47PqdmhqD;AhS_h?MxrGoAZ_&6$kAFNf*B4U*@Qe2DpwP`*AGnWy3(^HPB5c3x^I z_@@ZW+66~=Ube$0b1(ieFBdS1zJ1qWW&yjkO7jC7a^B` zG>*L)jmj^e0K?pRFN93~5!~EJo%0j;eGm1L^C%qm^1E?PG5#@oIajCy4R|&9VekMR z6aR>R=9A1BuLHQveWxZl?Ir=ekZiZxlbqwYqrq;sr`c}Id^&f*na@xP-v!dY!EW|5 zk`|hK3iI&@{?Vq6-D1X+D){{Ze(mD7%ClWs(SNCt^)y0$jS%HDrk;*&ZU$s&hamYH zZtnX9;!TsjWryS-p>hqDOZy3!?3y~4c^Hpsuxn~JMev=~ zuBi*i%pKcyO@fgfw&TJ`Hk+~pegt`Oh!{TkPp zScb+|0q8Lkgr4Noq}?ZQ0B3>^Sz7v@10<~?Rt#-}I;=Nse{&e(~lsV^b6SsHB87I2yS)}xSi6O>95Ob1i$>ukbO zb0Ij^f-dZB?KjMFiMbtg{?9luR0w?|=ovX2^_3=Nc`ob7e}x0NN}~UP>__lVX}+50 zPiM+@_)^kbA}D=3=t8Ldw%?_G0cr|M!}4ZAI!km}2TEklU}4z;H3(*5=i9+%A_?f@gDV;S-(Mx@}5n*I=n}{_ur+P+SW4d4_8rb zg{!EJ4OdYe=cuUuKcSmCn2{(G-4y#;y$R5d40X-LXzS5qf+VKv(Iyo}lkJ$%2Ax;{ z_)Kp?9MnZqr!qsviM%L>9PcU^`ql*1SuL8U0}>%M%1#0@Fy96>Ne|UcofcxG_UTD3 zax6wBNIz4xHCLyH)IXCsBSdm%b!JG1bXG{Sbhb<3^sy7o!sH}#RP_3|<0YGuql`k) ziJcQtI!*o4L3PzrgIOP{1bdohPBn1nYSxvUZcF@H*|{!6hN@)6 zkOD&;!C?s3T#Si{5(pLITuWGu+ZIL_r(NiuCEOMapzDAgKzK1207vk$rLQ8i;WmwR z1V)8UUBdIWFM$$Vnw>!zcJw-e9m?Fug=t>aEr>urTewKH^R&ihGmHQn1-M<3odQB< zuERyImBO`_K#^W!+v$AamTL+AGav>Q4zBPNX9WXl;JFlclyQntoGq%aU3}R=K1YBD z0-WnKI4;JGT5-IbDt2X!UbhI>S^_IAENjipKfPin~)~wM<0m>K3nj^rSwR^$% zIBuU2yE{2p2zcN%g&AH+$Pwon!b!O8{zBKIb^zf(FaVC=h4BR;s|aLpsrw}hy}iaM zZ1K=_A)HirOtj^%3I*EA#2L&&+rhy)C0i_R?d5o~(3t?hLy%5F3htq!$L8f%Oj*a0 zVbwK^N%jccsYvuFrgUt(1aP{HF{ozrAw?9a7R(!dggk#f|si$ z_QBKD!O1dOyjBtH(A9+fxb0HF5xmGWq{7$2lQl}b95-eW!j6WvO&GqyZH85DhLCn>reZVD{EfCjaG+zEAX{l5A8Q{CT@6?8a&-c7AnSw{ zafUHc6k3$7XzGEtlr40Ey*5yeiy-ap0spxzx$SMNzf2)lQTQBsmMV5D9{ zVgqi=h&M6o(2Hav?jR4`a01L(aum zb9oGYTK?Zv`W>cGdRT>&-F*#PItE(*gOow%|36U^-qF4(e@k<7ez<<>S$Rcyh5t!) z)3>wF2y4{;J~iR04m^KrYj0O;M}Bu-{_z7{E9%3O8@3OHDK&MpiW}}tU!uu-psO94 zuc0R!W3Qp5b#oIG!#4Eq>}li?IY?My) zTvb3RFm5OZh9@|}w1DG@gi^J4GK^0rM+>+G)!OFe*n@5cb$^dH(FMd^X&LFM>6vL66DMV4c^Bt- zXXYI{L-l)m2Lc64r%zuvBQrH?3Uya!!^fMLSH37rllFd(nVd1BFh9)mHjkN-E1J2X z+kD$YGs6PUHO->Q-oAi$M2)Dio}RgI!Ge;73%#2~nK{*L2Usi7dC_(T(kB+=rl-$7 zF@3tnt+Sj8)HCxZrOycS{i(;C?A;ldRa>h}0Gk^fq3jSkJK433fsTs4p0%yLeeK;{%Lh7* zp=z=Fo7y{!>aL!F{<6MhO6IwmD_fuCcqLo&t=zmR{v{ZTb zKtEJY4nQGvF;x!tb+jVofsX2~mew7G!>W|lz>p&8fsQf^%dPdTUHwqK)>_%s+Ph_^ zUEB5j2A1l8XIy}*$P+`-F zp3#pfv#A4fR_{PFG?_*!o_6;eY6D+hX{(^_DW_^2XR}%-n#@yJvwNDgGiVkOENoQYAE9$p1q|>>+ zhcxO54W(^Z-L+z%zp5P%nl@47D&u51bj0Jdic@{k%FOO|j+15eqP>y-dZ{LEaYy&2 zrVgh~M8oRl-d20?3{m+!&{2mupYx? zsjpw!dnJ^=qaAnPlDPjhMbA|n*i07FOzWnBEmGc1?fpHNa75ke|7dz1PSdk=#xC?F zC>>tf-e1|((%uB6;jL)0?v9pCeFi7-0(;uDwa#kav=!vZJfv!zq4rgE4_8%Jt&k2Y z6YcQ&nq?w}2K7EeCO8-8{~^WAnEzxDaxQDdRMA!4w`6-c=7eU>8q$5xchEg$lCjk@ zHVw42lcT|2(%6$}P~kfVvs-U-#BAZ#<6JLYP$VN4K~>I3Po@X-TPn6))7RQtc}hDU zfc8MPqluzmc`ODe>eYt*P_nfhckn@oXJ9Yp_8vYpNpn16T3ON4*Dmw3J!CNfcD72K;lX)9OB6o8iA3?=j(YkT@IJJ@sN5Y5caM1cC}E$_xezoQ@7!7RVDeUn`=LAA}C zX*rQWWN>aNwI{z}G&)O93Ti#Ii?{|4w%)3a?(JcEng{w?o0-pw*1rDsE_v*ro(hNf z%;2&?%O9rE`6wEi2V}mMr=2VsoGO%gWT3-t8bi8;T|u9tr@2Fx8AyLK9x?-*?(M!~ zFQOX;x^y~JeXp#^-fp(V+U|~l&Q{j{zoWoe`d;|bDLi4U8r+Mcdh`&i0;H%x<lIQEc2Y!hS1d<8~Qv9R*0-#!+ISIY+wz*2VPxk|3Gh- zVJ~6q2_-n?1@%gATG!s+wzj>cwcF@%jU#bc( z$3S13vm9Dc->_@|vjf(tU2GXtyYa6ouMSgEJC=P9ptE5n4L%k@>|GDguk>%R@}OlH@Elp z^-J^M4F)f1_VTZ}yt}iLP1@YEQ%l%qchZ&peWm>?F!34T!#?!-VGrOTN|(2C?cIld z^|?xuWlTAfkUSn`aOE5Q>I>5yEvr4eGRm9v ze}p1rd_>|;5N=(_q@9Z&rk#(|^y7!_(uA?+M7y*g2N<9iKY*7d1kUmAia9s>yr}bi zyS*1c)3K!C@bDw}Scd=><1SB#-{n1*oY?5%3fm9&F!DT0fAQ3lPdxtEqmST+GPf+s zE=@>0$EVWVdFMxW{EsV4!ym6Xng=YOC+W&UUsx_t60VyK*A z;qmz+R_LNW zIbNv0`Ff=}79D-e4Bszwyzvk)#Ghb1qcq3VD74=zKwivy5_}@Q<3nw8R!i`Uh5L(; zK6!@uiZk@(+4W41cvX-mwXRFTpW*rv;fm@k2V^A{ddP(>%-o+IhraDQU(it2EZni# z_V13wGnHojl74`QA3lFVoNU)mEE>Cv^i+HQL+muTL8oDp1A8{Z*$(utAWpYUrPF^O z`QY!PKN9Cii^b2Vf}pFEUa8^u>El0Acl1@v7k-Xqza!qQ__*Rh#g`S|R6L@{Ez*qV zN2eqvE2b&(_#FL3*Bg*~K1r`u+^Tqr;ts`26u+kUO~q>!zoU4Y;$w=>C~`|Z)A_aH zD~fL@zODFI#Sau6ozdL&Eag$0q&Q7+j-u#rLpmFk-l8Zv+TeeN(w8fKU-2%*`xSYH zndv>D_;bbQ6n~?5NbzrqpDD)S7dXa0Mp1OBfzDT2bf|$|taQC1_armkR>kd#mneQy z@jHq?ROBfY#(PHb*NUR^4E}#r`eVfy3>5MwD^60JrZ`8jTye9a=x{>3)0MtZ@k+(( z6mL^}Lh(7p*A(AT{7BIoW#?y%VyWUX#p4x4*B8?7S6Xy>f&P}#H!I$y_*2E_6<{FS@sY73#lL(a{yXOKC?p^gg9;R=ivB5yb< z6#t_555;I~F=Tq96h-$j=-Em;I*z$*k$meEPgHDHJXvu-QFI%F|0_y=L-9t%I~0Gc z_>|(W6kk{Tvtle>C78bGE(T^Oou}yNDy~wxPLXGy8E>oN4#jg7xv7%jw9DZNhV6O`Vp^j4(@ls;AIT}pqI2>I74{cWYculOUyhZSEWB7eUlVoZNb zL_Vp+ivF=g&?A)|r*xXqIZ97gdXCcbiIBHa@npp>5h3?HBF4^sBIMtw{rb&7W@{)CA1pC;mdgNS?`QT&LAa2^kl{1QPYDm|JAo;ixkHT-xYUPjxL z?ju6(4yDggdXM5>^}k-}o0Pss@nQ9ULg{Cf{N;x$fL5v3ly(V+^fj5vkbpO@u!ON{s@1bg=P4QiaZQU zT6BN|k0|}2qP!=RJAz|l&N zQ{tX2tX5p6DDR_)->me>id~936i-v+8Cs?%@2kKomA*=m8ZQ_w zHjjY!kjDIezv9D+k1LApBZR-KwCEHDE!(+)JWk8`nIC?L@?Hy!Q<~>%>7S;Utth&> z;a{LMwH%PIO0h;!Y$m~hG8WHJSq4ZuN(tA|tCy7Y+kkY>= zGV#2=ot^znn}GejHnuq~ZgVn3XSb2J3A@Jf=#l4aQ*`M7cWnp_`MSQ)8^iiK-|cO& z5nu0JG29z0T;8tD#3A3I-}aev=2^DL3S$emNZOlD{zupNWOM;^GyLXdUqy-zV0j`J z#{=CQx4g*5G2~o6bu0wB`Bs8{(P0AO@h{gr{KqFH8PD`+Lqv|XEZi6dGM<^Q)wtdA zs8RC({G7{?$8_BC_CnrR_)#7{0S4ur47Xd}AP5cv=W^t2hKF0;?U2X(P#*nRUef2_ zcFT*0fMWPLmm_Z0j16Ne(|GwhRzbn|k;CPd1sx94`sV}Etz&4`fq zUC3KPL3%mzsOQC=Gad8&vbd=b4;+0l3tG=;`#39T#l3IaXf*IkQYD#*>Gp# zX1Xjs^XBSq25lq!0e;Tq$ZNt5RPNanMd*Pq)`dQ^Xx7{r;0ycRHH6{~Gm4s=XQ*pWvoY@g}vp%Ca5di(I=infby_kFOZHK%HK z)n!$$yq&V;mBIDbXnEbut`j4R9`}VT51MwN2kGMh`6&J~Pa9GbYm-ZiX+M6*``%|Wn-6=v47<3< z&b1MUzx~0t4xcYuC!FS#Ih6B2JMIT?qwUYM>x?ya-t9S6d#f&}x?Jjqsd=Z3d-?gn z-9Dt(a(mghSDqidwCL(G^X0u|lV9@fiMrH#<)fKXwGOA$Os$<-Jxy|I=YjQ7dMNfK zJ66Igd&}ZA*0Y&!ygBoZs7o2*+vC0R<;>r{2{+Ps{XqQWn2)1N4h*g@dTP)pIDoo& z8c5EVkG+d?s(y{SdveeS7#U9v`T_}8B{wo`L+XfOYU9!(wx|(^{+znVMh9md9KNff z?OgVn%|>QHm3NQRTg+*oUMA%UEI4HQ?k-{uz2(mhu1{I@qf0W4J>zS}*G{OO5bO&% zhXQThTguz_F7|#is>=IATLU}lvB-NB%TQ4D1bRnC#}g8hxI*yuSUrWr;5pi`()Xve)yU_B znhCY3)v3XJPC0aaTVwm?wzP`!aaWfmJv)26k>`D7MpdcAyJdp^ooKVMP2{z0YJX>Z zazzxh|J$L>&_Unabbb5A3D-Ae!V*DtlbxCo@UgDX3eW2=kk{#N|E)B-YWdj&DS8eH!idk0f~Y;gUq_*oB@eQ>a`&Gvz&_{Kon z$~(?M{XRc*UueY%lYf+2V zv)jhcs4CGKY-&Rt8nl^U`@GS1ZQCa3<`?b#jiSB3F%xx|iJFA=eyzits>nL*Mn1RW zPQEJ;kkVBx`O%8^llzQ4F*Px@{%XJE$a^S|VTj)S0Lq&f(O;fKe2n$-%La`z?|si4 zdC~1LRdK&5IkE+<@$*67nKTJtNYnUb3F+LG$>B~uR}yv)vnFY~lU&Q;Q)(JS6Jq&?7{zGCG4 z=g9lanpw3ot7k76c_@C?U1fhhxZ?eil`LIj<9cu$1jp2x>6laUB~P49s$fT;r2hKG z>zll{8JUeuhZ-}RM*hP0UY512Dr?io$F6)U#$egb1$qurmU)Q(1L!eK^bnx)5GvD%ieD9Tb&uR3cM~$idxN%JF{kV^78dLiv-1p=DxXDO&dKO&T zw~Rm>d+s^mecjXtYB_Kx?gw$3&VoSuo*VpkF^5LL{~++8N7}|NJ_mQ=oxb)Ps!kmA z)iy3JxU;;y;DND=>sqn@zptOCY+K841eE7yRyNC_3hc$M>}s~paKo{(w;Q1yt=Ru7 z8{px^2#Z_$-K67|bD0iN3UepTMr&_xcW)qIWcSSrWcLN;1G5Jf&I?S~SX6M_%)-u` z&GWMRaswUnvRkGEHe)ApM?j*a2OK%b!n{CNcVJsnM|(?PbNlA**a;g8I%EG0kzHDp z>Xwb!EhnZg;0AKr{cnoQ#c@-dn+tGFla6x+8?$?+FU;;akw@E*1l%nr;;7BW?2Zl+ z_ogr)}u z(2=oC-_FZ`9$g_7`}y6g87cz~miwWL`IaEE^m zhQQG!;Dj-Dh=$d#qDv zFY>Jk7FbH~Ht(2f@kiC*71-nVm4FR~VJtid#dd*rVk@~#|7Q4jJW*m1-4k;UzFc^G zQL?^TgqYsai*2FuRofTCB%*xLzUw4%H1{IHvIB@1`gli!_Kn>@XDKB43eKXF8iJz| zh2hVjqmsA|@qP>9qeh97TPmZD5hr!KM~xTfK1hg~a3^^EH-Nz(?JvYq%4&kNXp2t6 zqAum=Sh{J>gKhDx$r}MO!DSXtkJJA)~WnKIBq>kW5&Gz`JT7}czffPu^Hl) zLRwVZ4N=V08TcO)Hx529=khEv6f|0R%d~$) z9E#$z?qp69Xl;V#To!`W`v{Yt{wIir4Ilis>(KH)g21?!!EeOTUV#~x%x)F;4pQ;P z@s~i96#h|h&%i%Aj(e_P&Sf8@SxM(1ZhViGJQLm7dX$++vGXzw@~pA+_F0#r=Bx?a z3mR|zoY7L*2cS<4%Q({$8eqk=$71o!@U&Oj!Vns2Zvsnd8En2#_ z=CHdgv)FFdJlecnX}wDS1%HEkwMA?6Rz35--WpFitLZ+$dI%D%^>jCij(F>MrrTom zQqBf;^fv20)S`6~-R;(1)S}fy_sL=z!fK~`tJRHuWNoFp!`jTWI_QQu7m8yQj@$zW zb#osA`agu2+*m*BO0pbl4Im?iRfm7Ev18EuDN>!Waby{5-2(pD_zw}9YL(MHlGPcI zq+&-gDVlSs25+>FlKki6Urt;c3oi-8#oGydhO)Rmq7Pe>lOMZQfR6O zjT50X=W+`~c%NofcK!kp;>X6PZ$V+joJ)KLowhlb_)NNe7Wa0?XVL8!>kskSbjMph zv}F9TY{|sLzvF-WWV*$i%QiCP2x*&hVS&6^6g1Vxu*4A~_!FV^Dq3Ph0-b0C^nei~ zC6eecACXA6--1?EV?+|&H0Q$V_ZG&(Yt#+|_-Q3;$%t_rY*H^H#@qFBEu~H91WSr# zF_qMN;Lf#JJ0sFqFY~2dMg*8Qm~*)c6*eOMVK7De{?00*#f;cwHi7pZw2JquD(`*-NL;58R8Nq(?$s5-;B&8UeD28H^BG=(+8bFw zR)lZHfEroY3nIB6@=SaMH4?`>0Xl`hB%YpfG5((s_Xflmar~{`jQbPP^2D8w|2QAS z?Wi&lC#L@%oN){B&xm^hftZLHm{>qz{!FA3NStv3@@D;#X%vwiV;uFFI1Asd(cE+C zo=vykN@UbIbjMpAkd|0XccR5lJc)DZPO-T4BXJ(xW3782KXDP=sjy)~qU5hg!21w+ z%Gh-Ntw=jH=~!A&v7+%WDTnNabp!q-O`+RoeS|C|O{Lp!jYYCa)98+8>0wud?nLV! zXsD#=bf;MLbm!4M*6=ccq=H`j_uqoF14%P$kr8Vld1l=V9>e+`Wl9l!)@5X$Lw}g_ z0)LVeG2Wu#t)#j1Pqa28O$iZuhZpLCq0W&M$H=d)NS?nFjd_$s_cFGqJ7>q{!V zj|uaqh@^7)*lPWmnGj#rz38N;C`5NO^(?8KShT8s< zYMwzriqA-Bg(uy|a2mqe#Wn}$eV0X8x)uTcrx7_dX~hC$-de?6uc8y?6_`L>Iov+$ z+w`xmhTCs_O!=!>vUrR8jg!{4z(3K-W+~TQ0C$RYCH>dapLQ!)vC%#@WwejxqsB7w zNeCRpfAD-+7`QoGg3fV*M)CKbQ6#~{NYTENLHPTT`*<_uCveMjWR79w+Eash3>kga zepIeGmUV&${7&S@9Cr!heiPX*F(;(J$NCD2Zce1ru$~})8sipN`%vfRB)W^Nn~`lZ zKzFe<3j)k^y5}1SOq~rP6FjU`)KFN)Gw}5ui>foT<{@x2o$ve1>|2@M1IV{ISqg0} zh7>c0RpXPYHm9(HvaF{WcPgu?WXv}FH>WX?^}aDbW=3RE*y|gULP^s(Lp9Jx znJCJ5uGGvaCNs@zLN@DZn#r(q(xTNr*W?^m20C>-8)CXhGS^Jx7rQgZW0y68(?pHE zF^O*i7r{?yl=8a}9_`C#Mx^@Ef*AoDl-tw7i=B})$w)JN?TiE@BfCgTM$#oCH-b)W zNAEQ=A~W(`S4KGJx-$}xb{v#eK~NC|?Bjm!bg6_v*1_WlNPnE|I>;t`lVl4rZVs}J zxoI$6hSMNxdl5-)Kr=;X1IUa~l==@=-x(w&*B`U?_Jhp)E@E-#coZUmyafIa1<8qN z@MCTEpue&925{UY7k(_nE|*^g{H}!G0oyOi%aW#}_%)M-cnL4tP2JfbGOKzti@M^74n0qgSA|wJ~u6;<9R< z49T1HJM~lfc6BjJQe6o~3@b$b1!P*>?o<~W&fGT!zkAx%wVeg$>ur{ppV&!09vaM` zu~YNZt3W2gmCePf`6Y&WE^9efhRK&%J$BWc&Z?OQKB<~xA>FQv$!{^aTPCIeSS1(X zH&SL9U36jw{8%iiI99)$sp>be1`gKO>vmw2mmd#~USIeiafvZyJtDp*k?N39V`2+v zrW}PlX)2TU!_Vn!94yk;m}#lJI3tGDBgYi`dLMhv zqN@{=A$c`3*$jv?6QK=voGhzw!%?0~BH3{D{faHJxdQ}Z<5!SZj1UjPwU{&6n9 z+3;h9)1W!{qP+Zt=?g2o8Uxrb6CLZ3ndp=FPWCX8Cw)~@)70(SW}&6FM;bA#)M=GS z&ip(6!5$iJbKkYe&Qs;@=2h?+;loIU&61DhnXv)X1nGsJ$TOykv{de=@{GA3wAAh$ z5ToM~xsxK&q>B`hKURG~5xubATer*`b9Q?TXB^R8G7R!)nN*JacyNv5Ss@3VIJA<9U7) zAzbLb6s&?`se+F|8+nB&qq#5`^#+NWG#XLIv)?Y1y5;WL@hSMHoK>Vn^g@K=8)NwMvq*`VGQM`p__8~q^u}4|vT;{?I5Y9Of+_EN?HF_! z$;4`@H<}zAe+a?gWY|1ZoYd9_Ane*oeS?Cwdic8FxvXOLlc=CFO2lRMd#(lJocG|z zm!vs=fP;sV=llka7vLx++uVPGdLPu>7-ZTHL35|W!SH#>aM%eQ7l3o(a`3Zl5+N6D zIo_00cA!jT5B8MHXUlYHro~whN1x5}Zschh!BXGP%#~>Vkf@5L@ z>gEGDE>M2V4$_#$q~#jIob>NiUyKyCIfcPL%FACqzp$rl@o-^?VZ~KCzjVmsjMXfC zdW;eC2e|Wgpk>YN!B&d}ySZ=nSf|ytORLo*`Xp4I@eRz$%$iI}^G_r*kB;D#i_U#r z!FXp$yne)+&Y-Y(c>#zrFASz>$Gtlk_dqG&-y?E`}XP`qBU7(g<17O$6z zDJ3mgyxubhF*bp=vv|Fy5;U`AXVI2?J!m_NH%b;C0lk1p4Vy*uKYslkKqjRBVLq7w z<}st0t&&No0^TG4QW6DU-Xq^DX+%HgJ?gm9zao%#$%Uu{-pMDH%tUA9of2QN2|m0> zzFYEb`0!3{D?JgwyW{|3@=l2>X}|=;d$d)u2cmh8{Ida*PuU5{ao&Up32Tl?GC3D` zvW&R6h48{Vx0=indsaU6XEsNI1(i;qsjHa(G(%vKmb_BL>KI1N=x~!>W5P{(9g}WU zC+DM*#)iy;jZ-sULoH!pmEhPHqg}2EF4I&XQ(YuR_(ZpTtu&W)u1QV+me_8KVqsRW z(lsfy2<9>Y#*{OeRCRLEIG!NM0^u?qmaW-fOoSdAB4JE2R?U@Rini@`6{0cK5*Sao zP7T=>n-;1DJ7-sxjU!90Cg-yMg+4W${jX}VK^C$A7Bu;Vs9}YLF)+JcX_hS@7Rw4l zRSi=KBAd4^#CyD(H{2u}Lp;W1B9ziR#+Q*q=+bb%BLU%<*h1G) zGtLzOBSzTVa>e0Sn4yJ5$3mP{gg=S%c!I+lURayAF2uXR%^O}=o3}p1yWY)PAJ*s$ zVZ}Hckri{)La^YB`1u8YfCZwRt*}IEB+-Ivh0AfAvEO}Gj7&CE{d14qjPsAv$5IIM)%XY+u4!nRwO__^LUy%xnVmWdfs;VbwKsEwcj%PRvjZ z)WG)v+|f8g>Hlo}H4nqhQ!Ne+QZ9$jYg|?#Oh1FS5OlnN8}&h8WEffO3HCE5CQLOt z!d+Gy8G0&icWgJVXHV-5?BEAypZ@{VFq4i)hta3Ai=fP&E zb2EhJ!DiqOe8D8bA49PS;bVx%Ohi70Yyn5fV+^~m}ee|czpI^EzzZ6!Vnl?4%?;Pmr?wj7;-9NoY12+w{ zceLcgd{7Ve`1Xd|$eTW^p=kPyg2LIS%n&G;J!3}x()O-_9kYw_A@TnQt9t)WEDnZS z8g#7h{YM)3i!>4$W`XeYOacbGT+n{v7e~v>hb_UO5&y6D*Su(rr0tJ+A74v zHk>WPko`go*O2`}JdlI-3o)7<`-P5E?H3A3-SaRGC~mRW;1IU`Lh%pTFBH&p{Kva%2^soWFFzGfNlvlS>9_ zxA}j&;WrD?p{XJi_cpL)OwCFy$zRo0JM+Z_i!y3wHfEf^=1*R;FyDXy z!8tIESdd>>P`K2aJioAZd0F-xsPPD8?QV}bCOxcbe(W*xv!-NE$SzoT+kzRAV(kgb zGAE^FWw2X~gGA{DQ<{h7L*`h_+sT0#PvKD8SOr4SG9XsD^%`e!UR+zuQ8ITE9bYGg5VIz#PYa7@AO71W$`?{v;j$YBbx z!HAPEh`}Q;JdtBK<`r#AH^MH~9*`9pQ>li*uqK+)**t&|)PN%x;U~&Mwj+!#G00*Y zoDriVJvhg+xqXXKxxRXJm?=fcy?r9cWs(L(bD#c!ZHo48%pE%V5o+5hS+%BeO=W{) ztt5DWO^&36%qTkLmUAOCd?AGu+bG>VVl#_YIbit1=&9rZ72E7k(8`tF7J8hip&w>W z*ep^NR41YJO%oi-rvioPKcRssT2S znzq1<5YN9v9^X@whY{I@WkTDuCJns=t&KF(_8$xct*NM9(NMX(yrHhLe$7(5W%|Sr zBT}=?GcG|wt-UxfHN*g-%fO+_rlFc0qEOqirdTty4TA=)ghd`El>7v(4t62S)u=t@ zVTc7n$K0Tt+@k3)F$*I~^xgIbd(g2VMw`vp-rLmEfL_$qzd6k4U}asMs}@$4FI%&` zqOz`P>56sEBSZ~)v55?AZkV014W)?TslEZ2D1u2xH3RlVwzD9^UYF(vA4|>uW3zBL zn}vE%&o-n!s4l(!c?XRvxWc0LJiiAkd-Y>nDt<2$eBxIz6lOcT6${lk})q` z+wOT%jCwKM6lReyIF4c9wypuf8&)5`vXX>Eg<|kW_=uEe_gn2{6=ExxB(~j#^^g zLB)n_$o8I8mv0o_l7@WNAhOMt$7HZXoh9;iE z4hxWozk~tRBW^>N~`VlNRf>%!A_BXfTSgx_ve!$t5Ymui0 zX{w_bAM9zO>BeEK3m(OUU@KdYjpsbZ^hx^fSPu+0FM?-m*uw(VKkT_n=ea(Nxj@?* zRK1KbeTbrtTqXc*yK6E}O0DzhYG9*6q04d~BML__A=q+u40HxZW^h!7?C7z|2f9GF z*oG-vx(7C4u-j2qS1-d@2*%<{$#9=Vb_}t(RvnHBJg27lG?(+o&<&?ckDz@z6!X7m z?Jj&!h-J0#aVuk1ERqFNF?2l8VjC^L@jP^=n#Pk{mM*KqlNSE6=CLi{9mVLKj9_Pw ziBOT^Oac)Dm(M$q3zTWoK1XZ}l+m^+j_q?*5N7u-ygzvg`E;bj)o~~7`u~n(Z;fRSXb4on=njN$&b8L`8Y%C-&ddK(!=1%=c{19EE5(L+t%*$n6oK|^-^ zeYVv@v3fU*!8_51Ar)XhSJA8ZuH#HapJxZJtj}H-vvW%9vOXCkc=mCu*yholWyPA+ zmFq(@k+HbEd|n`D@$xl+)!p44eF5qyD6Lx-sO;R-+S1b65wMRRALh>sKe z0_?VtHl~WH+b<2erbaL9LNaE55A*)A??l! z%Tk))+htZfDrIwrA!|%ym*-rw>@fuYr`is5w+DaIz{w7NqLWQ7p5SjB@W6$0kzw`2 z77W0j{Or3PX;C=z^5R=yxO~6p_9Q?1vj^de;ZC84T&%Uw<;6F`@c6Ov3kuoU$2|yV z{C9&lfw=d}i z4uSegjEJ9T3?P*88OMWg#@~(GzPRHebkUY!c>1G4fpF25VR(45$h#uQ#b4}07i}Ge z^N$w(f*{N2poO@G&u8N(jORZ_ zgxja0YypfQIu$gdaAqBa@tvnMpOvF9p3B1MiN@+MI?dRsG{?gzjL&nF_6r?w z{Z`a$!0_jgFDU=wLN%);@(ZUd60tVqn@8k_b0DJPAH_5MPncARe~ip*yD2sK{pzY3jrFVBy)JbdO@c;ts{rh*%PyNgRiLu|%8@-c7`U z;9?>M#ihg;+{}+I+0uZ~pgpL}cw#+tohWjZNg5&z&niP!S)Mw39MPYv0^&TyC5kH) z+3y*St|Z9%BHgXHP4UZ$XDeQ!c%|ZvigzjgMDZEL7ZhJtd{^-!Mf{K~>5fnwr^w^? z%uli63dJ>w(k=+^Q+lT&wL&o7xr!GlUaokv;`bHrQoLW0YXQc8LhlB+6#U2#m?@^lS3@Gm!#TymBt9XavJ&F%1KC1X@ z#WxiHs`#;D47P1jezM{u#c7IS&k5nw?Ml8CiW?RC6wgo;J5AvGy3*GxeqT}SF(Ld3 zrJqxLP4OMYj}*O_5Trg8Cn!!IBS1Q#e)E3Q}UQanrXD~jJx6#GPwbFb2mDE?CMw~B8n{!Q^S#du5%%*RB< z9K{ui8x`9WzofWZagXBliegU~^1iS1U5a8y2>w4+`botX6<<~SgW_SuPZW9Nm-$Rl z3@GL*ioIckS18T>v*bTfu}!g0@eIX_6u+)`z2f&3?^k?6@p;A96yH(&NYU%J(;cZe zL2wc%0%AMe3p>U!x+Gv60@P zc&?(@3x@wSN>kGr`F^DMnBqajR}|k?{6Nvf_BO^Fp?Iqz72(nUVIs!HL8V_N;$io? z((fw$p3J7 z)0AFM#9~V92_s*pkOrQscqOq6?WlN@qSz0HoJU9_{y`$*{Xy|V<@-$0kI#FQlSD+k zOd{eH6A`ak!^Lhe_%w+@~mZgc1My zq!I5fBJQUY-zOrUVi!0W;{Xe9(#H^2;Qd2!n)=UDdY;l^9~k`gN}s6Vo0RS#VzGFN z(mRMq=VHbEiZ?6XM?^UuQT&eF zbhFZ(O7|*#n$l+}eUZ|aDt#4kHu@_O>D;RRcN3A${p$ZK#UqN*qip{1iqnWFr`Y)g zE>OCP2>Bb7uV3*hBFc9yaRT1+l)j&cbe~fEh5G+S=~tD0Tj{?jJxDAujDHd#F9CJP z{2ZegP@F;p-y9FVev)FxKZXc?{#ZnLlaYQ=?-%&E(qiWq@_w&;gNi0zpeTPb5&4`!%);+YN-rfMy;VwYB$nfOM+9HL;<-em z_f^HOEAA(P|287{pCTfimlXe~ctp`Z#tu(WOjS%*oT8YgSgFV(n4Zbl-LCY>MC7|$ z=~Ii(wOr2zf~ubHr(i zC5k-DPXCh>wlQ4afV?mR=SP|zI94(QhJNhy-J_0c)t2yuJqTHzE0`y5F!77 z`u~<#ghSOze?UZf|4=$=yiLavk?+w;XDd!s|5-}UQ@T>=8Y1LxQ~$FR&sDrw@lwUF zD}Gb)I>m1*epm7PigzpCr}$IF#}t32ctG*jioaDnr1*QqcNG7s_@UxI6b)QlFXHu3 z5K9ulk&2jNgw9q(*Ap5|EQqcxSf#j35kpb@n~7LIZc*$~>{Z;QxLffO#XX8QD&DMk zyW*XSj}wvUrxgz>{z~yp#dj2sD1NB;Pes$Tgw<>lk%J&|GpQH4JirAJQ@%Ag;ph(4&Tvy$r_+!O~6`xmpQSmj!Hx#K> zoALjmXn1VerzrNp5iZ|I_!2u`ECUIex<2elHmsxf1~({;vW^oemMA!C@uEG zLH|=}YQ~}*>ZT-8TO~17F`zhEQNA}JoH{DWC-%dE)ZobV8_ypT*DKNvC26r24(wH$ z_J`>IWyM{JyA`SPk>OV>-l%x9;_ZreDpDyV`JYxisQ4>IYKdgHe76Ep6(s3T6{!`G z>#iilv5M3sK>tid`JM$jPw5$oa}}uwi~JReOBAVRk^Uzs%6Bi&y-M#?6#L=u-=*|! z#Y+_TC|;!~=U2ghqtZ7kQg0*Edr0wd#itctB4Yjcs^T9M#m+MPsq~QHpDD&*?j#+j zNKJ>Hbo{oUDBs&aPf@x+u}G1c5XoPoxJq%2;t7h4iqw=yei{TIepykz(}AYuMEZYS zk=hYS-=g>8smqV?b|_NGBx(6>2&9TX z()$(VyCLZBDSfx%eTwqk5aEw0{et34imxlm_e1beS0LpND$4gm(D5<0zkEjoJyz*S zikXU26{*3Ea%L&cRV-1I?~MqjLOre<*D0Q)xJi+U^SEBzp(x)UL7$^EmE&>!_YKAU ziZ>`ySsughQ~asoV~SLY$MxQC6@RBlP5$(!l04!kit?QjbPU!6^rzB1;zY$P#T-Q{ z(PQ{R#Y)9$MXJZ+`maHeh95|utSH|xL7%Dg`HB}QeoOIMMOyM<{96^NPLK4%iccy2 zT#@Sa7*17_#CH_`sz`Nv^!Hje#wsQ#28dWsW+~FT4C#EuQpHL|TION+N=53{BTb76 z#7@Oi6sc>E{^u)Fvp?xQiqySF`a6o>SNx$Ob@4I$3B_M1KC6gsZg{ZhOC(|rz-xi5 z_X0$$YjTKqexnKHxmZlZ^QDA{d3XsC^XEz;=Bf2W%qxw=$#{PzqJQ@g*^i0nk7p7g zbT<+8dnpn5zNKB%1Jv3EZLN9D8TXM{;H5~?u7WvsZ#N3@4&8VZ3L9n%QYi3qwjYHUh918Ng$=j4 z$j<1{Ek?Hg=WH~x13z!OkrUcK(Ay=I;y!?q*W1mu&2!Ft+ndsc`f_vHP+x9R3;DXY zk#VzHC?vSy4V%`6`f~G{ zynT?j1b&nki`$WR4cu;dH=uwIz|XnJ$LpK8-SU19d8`}Ci^uKAy9;i&yhb$iP4IIr zN8TOqaLX&gVgY|5F9GPt`z73Nd42@&x$az!yl3FymUl6PHNubiOT_KS`yJeFdHWH- z_Hr(#eLseWo9`y@-QX|*DUa7jxZUZ#i*%iO!J|2tF2Aw4Si%)pE-V#Mu2r#qm)s z*QyA4^EcS}p*(zc4$3aX5L`NAS(~ z9KN4K@XZDv`>}I5`Fk~juMB*wf9G=eX#WguF^pFdBDncBfNzu?B>x>g z?m=_&bwuRP;ad>F$9G@$JLhuv)0C~_KaSvwN23jwzvmg9yduBCgtEk%FcpJb1>>ELzxvseUjA_M17hB{ZobuLHV zu@UJ$i}g46138z&ml2WfyIT#T1QR{SWdUv{-B}Ux?#2UnzatFj$SaJHm%Gh=@1#7o zxg&2$guJ!0!smNOUPXkw2Oy7g6XkIY;KsKxu7(|N$ha_~AeB7e}&Vc#if;6*#y%ZaL`Pna>Lu&W56D$GtFUNp@tlIZr5l3(w7T zm3a2xghLJgIRDey@-f>E42=0W;ln3?@TKxG9~&Qz*`79P?e?AJX(P@m8Eq@xKipI-;rlbE^+acG?SUnl zo_Bhh+a|Qtd{|Mor|itCgafr7o;hPrS=<40&m)<}p1hj;+WhK*C6wYl7M}x-{m`g7 zY)1d3C+f(qf#Y^>yl?WL=eP&a%KrC!-e(4lf*Ha5XKMaa1({d;&OaotlzjIGMt5;z z!YjYYJf_BX<%;)>&7M8x<*-$8<;dEEnvvDXl72>8VO5UAM>@uJZF}0xnR^=XiR93V z!=Dvcw?FvS;Y-*z@T*gaQF2L%IPs5bX8SpPsran`op@JjfGPW`mfVq=VWdm>YBQU> zwavJ%Zwge|eHE;7`R^i!5%51)S6x*_uXUwsD>rxc2RI^4jN6h)a9|t14{Hbc<^d9C zhUDE0FXe_a8k>#Q-d>#Q4;a~f^8(p@f%(Affraw|6E+qV95=JD6N-KYdV0Eh`vXnX z@er`}d<1%-+5r(-CTuL|jQ!lmIh*HY_vHpU=4H1`33Lwh^#?k;TiQ484DjSpdZ1NK zAlW)SsPJQBcFT!@`2lEMq01KS&b5j8$DS8`CikGn=b%_?U6Tz+!{dwLIRLLOimHh` z{-{6F)9?Ec_&jFx`=m^N7NUDRQ6&N~@W5;6Kk zWFX471Vl7_V*U);EQKp-441?HZ-GAD{}>{jVdY>g4J)Z|n-(5ZhR3=S|8Y8-Kd7d9 zj^*#-akt=~N32vEap{QQnUV`8U)-~dFqLk9+-UkwqdPuMe$2|HI}uA8_)n)hCGIjL z?8&2htPwB2*cLEDvC$}Ez%%n&rnQ=KX6=Tz5%&Y~&nCYwE*kmq%%Q(O&X50|V*1C& zUCq4Br9aYQ+96y7C7V@291b-|X>+_T*!Yl4X3N6pZ zeizJw;GR+A1dm>(Xw1fFy! z_QC=4!%;qozL-o)Z^i%E+4z_0S;3PPag|Zoj z)pqGekbmuc@JWnor+eLNaHqtLBF}pIk41{C*eD;HGRnurw%7>26OK{*2T#^Xl#gdP zB#1w*OVFq<{V(>u1U{~+`v1L|$;_K2lgZLHZIX7fwds;3Z9^&0LU&r4?kN-~Bx#a1 zq1k9sC`F}c;fIKVf(Tdv*#ukw1%HB~2%@5*;(`dMh(A$r`GE_fqW-_%bI+Z5GYyOQ z|G9iVzV_vN&b{ZJyS)4ETjrf}kX=F+*{rc3bRU>foWPE|mI>S%u)7Bn81S-TB#Ta_ zmwpf9>mF~#mW$X@6+50{)kqrm5vFftB}+j$1C(F^)eeyE{my(d==23Uhao<-6XAU1(1MYBUkQCO(9; zScaYSB*Z?Aoj*@kAPm}#EcZCo)Kbk&WckU(xH#Dl6rp4x+A!{Mq`NnB;Y^c#D#IQ@ z_NQ!-U!$DJ?_m;5ORvBuJs=%#y^ofyqXau4o;{J5j=$32`G3`Sq+A?L?PqU(87 z;kG)H%aHV(VFRPA(QQW8k>iw>Q_o5s0&W}>_u%yqr;h~jI64{M}^Eo%2Ed>606>|2xG<=QxjwMRA1OQ1@ zvhf;Hyh^% zoP^vK8v)Txo?86d-y**jx&qE_boTEFHSq83@BP)=zHBT{^}dH53?6;jj^SJ#gPU=# zjtdW^=xlCwFOE940y&VuhaI=(!)pT}$IUoTf7EfuRn>lEa_u#1@rgt>{YQ>FuQ_;D zI=!s9d9t^(Y0a9M4HeCEgJ-6v)-0N`XkKMCoSe7RG*wsBHZ|2QIwP1o?p?vrba2X! zz>INw;pw|BxMyoHp03*v?7Ad4ZZ=3fqXhf;r<#8bpMQ#XU{+1dPfmh6^NY@z9sC`9 zjZE;h;4rs#(VW(%raAajA=|FcJ8rdc6^`dGtA~;H{UWEzM*_Q?uO0Sz?11fo_|m1&AQ#BgH&RC6bUbp@hy&!6_?GK^LP79 zOg9d|^jXMV5)v>TY~)QqITp~jxQT|MH59wl}R z4fKsR$_u5&;UTDi4$b6z!al)&+G3A(Ls9Lajk=G!Pg54HS=z-w@TROL{a87Z@wCpS;qd{^>0zIHJw_$^G1J6m;Jn%flP78l~icYK}~ zcb-k~!n%9Z@8=uBE9K(|OiQ2pXf!eNUhW_6a#Mb*^!wc8Bca`RTOz&~aa-k2=WWeW z`e;PDeL`dz-sUV#)*XnR=bazf>eCszeSZj!fGem%U^8cRQKkF1}` ze^vjFc#QFFA&Cdb{+Uf~7A%7|eqlVg`?s*jFU}bv7KAoC zPmH|PZe-}M(Z2}up|3m*qe~JDr z=u|tOM*?me<<`CgH-rD9{BmFkj_;8`yRn{QEdwOqmdAJG@dJ7MzC8W{@T{NbSPACG zWqJA6=ka&t@t+0H^3B*wFkc?Y%YQ77{{eW)bLP;S=Ktp9`|hUeMx8qNx3)J6{@ak* zhs~Mi^X~?Z{@prza20Cb(>Isvv0=Ig_Vj0%oQ%$HpHz3>85ybWxqJHd<@~CVcT02V zb=ZGN&%a^G^~dJRm5YbmVejm3eN5m=9K*ak7u}1^ntmzAgIK>t!&($k7*Ua14JpInQ-pu_#qkiW-6=f)g-_^N# z@%vz&+krIRb1U+!CC{@nfK#fPh*J$+k@WeBixhb-QGSi$CdCsKI}~>+4l3?ZJfO&P zgL-aIyjAgb#g8j~O7VWhhZMi9_(R2CD*i#S0OKqDRIF0uab)^oic1vNDxRp=qsVWY zDE~&qw<^9{@jAs1EAqHgp2wZ|4aM&(KCAeGVvxrZa&p{(^OP5VRN%!Q6_DTbP_9dH zNbzh%u`5Kr{Hg?ej_G(C5(xq;@m8LQ^a(@{pJ8kIJWXGuy!exXoY?o_0@SVPeJUq@ zrJ!%G@^9As|5E-^#rJ9Yb;^H8`8$*se^Z#}_bD%Se3*=1BaeFjN9BK}`M**AMdk5e zFa3%T(cWU^$19&!zFzqz|FtV^2Htj@?r;vlT`duLGE3Y1AnKM zzmJG@<3Z)cZUN=KuKc$Y#f|~#KT!S|#b*_tSNxsg9~J+i=wRbKLU#nwCBY)aY9i)S zy&{%4;du>X`clP}iff5zx2zk;KVH-2*GHtEMjn;Ox&f4R19+CE?^isac)8+Litksv zQSn2HvaUeSJ<5Mt@j*pdSCIcr<-eoI*SBoX(~7@Ud{ObQib0;wkc%nuIzm3JSf?od zE|K1>{Naj6DK1r9sVM6bG{`UO0o`Qqmg{1wXc zg)a5ppm?*Q_~S$R$CMX;eBeK$ysUHJd3|H~#}#G01OF@Kf33*t9_8f4Hjuv-lIORQ z#I&MZ55e>ILZ@3CoiU$# z>*geLo#lCeQ(WeCT911J5%tM@=S7t~ivyBKz^W z>~zedygZ#}ZBKm9_c@7~C+cjf@0Ko4i7!WP_QXtRcIJk5Ok)eVv9&obXYC>PyFS|p z3IFo%W|4s(=W)m|HRL`I@K3eLXkY_$<9Wc1^D&BjF?MbjV8UZMix+vya3{Y4yT2dX zz;o`}&FWi%1i!vZeEN9KT74&h_UpSA{Ne!q=w|itI^@@P1@t|Jv|8+Jm(@26+OO{^ z=;AqMH>+qz7>Qy^!{AlZ2z*3iWybhCLCAaeB8nfXF}xP8p%o13Tad(h{f zS61KbJbhlhu|c6eKEv5@S)Qk_5I1Za$LR0kJbhcCuK{|fk86t6w?XyUHqj3p?NIE7 zY#EP}^W>f#KlZoPy((wRZ^K|*YZ)Nx<<<>3|M>0q^_OpiZ2R^=F6jHV`X%T)Tr`+J z$d-+lHppeRuR|ZdMYNkGiN4`-T5zH6K*2hsqlna-0eV1X-c#mi|s}DbSF6r!A zyJs(csO0z6__q46=6Um5<}P@dZ>#4ww;&7KtAAU4-vX&E^KJE19=-JNvHN?2_;UK> z-o(oALsuNz)%%vE{OfPntVQK2HeSJBcgt=^vXUP3h1nUfMeorny8+|3{w9g4X#`zTtMYdsF z@kJ*;4-I++!#EN_FEj&*K{xzaln%TNa-phIDeZbopfngLc!NOX_Y?~i@T0@PAMhu% zDZGd>^w{jgK7`)6u?Y|f#C9M# z7&{eo$O+K#fmhm@Ej5m}>@oU}c4GWO#Eo46kwAo z_hz1k+!RKj1sH!2Wg~k~*M?}E8Qurc>uBOCB%?;?j24EWA>_?KW;8hgbSuir^8&wp z@fv@Ozu^!YVa71#(V!^yA}^b@SdsIDv-% zvEp-4HZdG4oej>r7_zZ4)0W54##lL%Lmodkja3{CI^kUhO|eSOft1I$AF(PnrOf*g zWzsJpz0!LVWooa4eA>GNe`7Lcb@*up^mNM4@>XI3#b!JM`4*2K^2BD6Uf}(f^0WB< zXJH`%_~9)l+q2k0bi`UX_N%BrLoA-XoZzbK~T|xO1S#P)ZCh9+l6TR1~W*bf=-RJR2 z9_u81n%Bc|>m$9(Vp%jzd^>|jbqV} zh_mxjK_X!)fPN=Y_;vg(^VVY=5=qkK-WF7lNKt8}C{2jcI;gu5Dgtz%5*v63niJ)P z)!U(^a66m~6xNV*3K412DXb+O@_qx+!aCBP$M-db^`sNteb7@lwI1TB)YB|9jdaMJ zQkFUf2cfV**eQ6H42RgEz(l6b_$!JfQprLt0p7*eESM)ZSBJFv! z*Gv|ZPCyffKR|cT$&wKii_oL^s^mnTY%(s%NoHJV&6up{g-DtAJdR(o@}r<{I zCv#jD%D5!cY};}tz*iH=YC32Nhd6ly^HE7OS_8%55d9<-FTkG?`NK0FsXT>C$0Jo1 z!J=w|vA)JAqHK z>`~7mJHoHYL&eMJ4aqwOr(N-ii%=_Sd>9$U3mMfg@&HJ$xSgKcy{B2U^J)||2da4c z+b}6`nYx)7N7Fk}0a+DmVG-11Yc(DqtN23slOZKw&LA!G}POU+~QGAGsk<1@_Se|H zS7Hv`nWPimSMaxZFX@!G8!azBi*%V+NBMoE%e}QsKl`2dTj>PoSG{=2c+?fS3uHs_ zsxv{$(olRHr;g*@%{Hu|!>dr?@aTwB+)8Iyo;ROExR!LvjN3ZOlza1`pm;s$O7AD= zQ1J%RX{YRbWN^3BhestGVtoUK9T7O?|JtBq#n>IIS-0YW_p=NmwLt9&I4WUl(7j^St1R%1-fOr+d0pQMTSL1 z1})>=iX<+!i6x|v!($T;&9eEd+bHG;m&mY%L#;OZoJ=+;6y#J3hjxJphu#HMN#0Vu zAFNzrj&ULlpf7TZUI2LXXyz7ktZ<)%DeTJdd0q&^@0L+j!dr$RaK}^p4c>i}pTIU8 z@c4G$oyaM6fs?$Bbwhzvg42YRxRY4zczPppFK6Sqfx(yioe-TcuxBqaIA+|+j}^IF zK)M?z;zJ)TThJ6s`o+O zz24T3R~@K7iN}9a|MjfD8i|ung%0<69)zV}r~Ht`ZlqubScHcib=;e%rJ9Y$n8@bd zTyq}SbHUW?1Mz+mr-S&cWQRi>lu28`xc5m1nxU{ldVZhuytWJp+|0nN1y@stKi^{7 z_-i>P58AdHk!z4^+WsIl{0?&E2YI|i&fQoprppiVAi0}NH@?7bj7Nu@I{voc{?rb7 z9l3+yLUjKplEH!GYx*fmIU3Kh9N)UvV^o~qLF`#1Oocs&cabxV=M}$WAU}$~u-2IB zZeo@*{--?tci4vW;n6)fgz~20cd&lmkxk}LFYXik*i8#f?XoO}Y)?_5#grzKNRWi<6v%oqe zm}I?&Se4K=#AoGE?lTp^i-?K7T(Fb;YSLm4Q<0UxLZ2;-dl0{Hv0D;eT(0&gle1#{ zWpj$pLIi9&Yj=Y4y$aSOo7^oEw#H9H=W7a_Ii)O9I~(anshY>sjb#9%dG*#rh2u8W zXS*`ZX5a$pYX!PGJC6oi5VwvS2wXmLZ3U3v z7vS1>sDPYP&PIYPo;jZmy>fBZ%5nkc^bir}4(L%fL^fZ($Z5XK7UfJs2Qq0O{+2$y zYNy8*z`4&_327m>5bCh!4iHXjc~uHozA;T>hY1gXvU%<1qJty?CK6ty4G&*Kg3DvB zS|rG?8?LjFO)%1@zad9D*Lvq~(~YHyEg|r@99sD+3bdjKj{ytX%~G5q+$?Xg&Lm4; zNwQkIQ5WUdRrZ5}$}#b8R?q9vPZP|5!&~LQ(L@E;>7Lz9ot^MZ)-=26l?@JzY~Ns4 z|DNt1=XHE;-n4yBUw?NKzCIrA>u0sQ2O0;5274O$bz0+L?JbQAW`Fuzf#&&h=Qg$W z4er@Hzon_aZ+p|uuCCk?O^3~HnLVfJpVFY5rrRbo4Rm(((qY>_bZo|M%E(#pEayL@ zox8hwIey&FID9~cbD#w>yLxsH_VhRHM$3nAYF(UUw`okXpPXvPEVjLKBwNcrf3O_K|31G9>jo3VWg_j6aPs0y{qCL}pm7)44ytoc z|7bf+B1Vjz626`9#@lT$xZB&6$-(KqtG&N(gc(?GcC`;}Kg~zBBWSE;M>|J$Sz1En zT4tnYz>>RR5Q9em%fc>ZPmf`IR#RAB(D*iO8*cW%t(6}tq$j)Jq!5+^gPr}RWXAT2 zOOmy=y4-ck@N&}4*jiohy5mEE;M>kg<3{LtCk^`IL+F?AIb+yY=)Qa{4?;_h+Ml{^ zx^B_jwNt8V@da46x;tEV>Zv)BUvb^qsdFW1m~IujhW6mc1QSyj79|6jUalV=+3c)o z->_oca=_*-n@O!$?$Cy%ebm89CxZbo=gi$GxneIYmuQEDuUzd(YlKmbE&W!4XiiOU{jEr4D-$ZLDL`#M(-?ikv9Xi{4yNj!rN?QU}hja9-Xv7PGQ zg=s`eY;Ev`Lr>>`18bA^L6~TW>CK@QNEzFT^~+amYDeVc_O=r@tZ)X`cJ5u;H!6Bx z%c729;kj%(H@nMO+PbEF`HEI&m&`Ksa1i=NHuUT^EoN8D2_k>>NQ~qU;)c$yU3jdt zqr+Lb0*7MfU{7~itVNbB-`c)*%U08Nr>lQxq{r!l=~w1JEgK!$?QF-;;M74YMtl1E zozcFoUG2Mj20Hr&olUD&uUp@~dBxiH^-GU4otIIswbZJF$JkrqvE6M7Qw( z+GVB;9G_;pw|4#U4$pBOVzizsptExG37b~8tyqg=r+GaC?58ubXGFA_S>81?IJ$d? zA8T{A&6|rh4cKXxJr0|(yyyY}fo#RbGRx`S0|Px+21y5aGC0E<;DK~NN5>9cyI9%S zR5+l(9ftPht2eD!*5=3>As;DimebIj(PO2yWsd6TNV}{=*>z-@ z9=M(Dov_L}0|SH!Af}U2ubmFKlH{K^*;Q#x`=;e5ni(&KfpS)^Xv2zTPE51-We?B# z6E^YqIiC{0+0wR0C|VEc%oy$HzCYR*PZtO0ZMhRwW^rNK^Y-_cnnD-@;W z+{BTyN3lyM=I(ZQTw+&tw)-ZQ_G;sn6f=HQOnfpi9!;h5l#j4d}8GT-4c zS3J{aR6ek0FQ+C{V5u6xs??J`(b{k#cJ7jkNA~nxvSgW)Iiq9+79q3H=|eGR0VP1csYgqs(10p)ezL;o(}PXa43DIPE1{R=Uj9z&Fp@H?Lm1p>>5Ud1n1o zb6sOs3QP0OZby#0xp1yHe#N@B?1aor4KB84^8U!!Ae%)Vi>+DQhr}k;85n5q?1xut zS?uj)c1zoetyzVp7F>HdH8N*2FQRg*<2!$F-?L`~=d*mVDOKA$F#H_ee!5P*oCb=C zkS=L5_qb@R--6pJx}G;TGhBno$yEv+XxEzpwH(GMx55p#TzzCVvz@Eg9lxY?^>WAF z+zl={V<+x@#KPU2fFf%8wsgzN_Po>DEGn|bOWV;)dz~|vt?YHr@aF1gZYv>)L}g4@<-L)7z_oq;5{bqp>Te8_2Ng# zWy_9CH}Gt18ye~#N$-GvI(QRLuNc_ggXf;^G(6{b!9ICt_sI0W+k#r&i}>wd{qITm z4y4TYQiSh9mZ{xqzFaMfxI!SiS{>(|NmF4or7pH6)?Jzw{~s!eQB zrpF{g@+~EvUgcd=AoGUG2*Fufp4Di9GZR^q=Qs!W?CRTSLz}BV>?}ckRzKfnWw#(6 zadYJ(&Q@e+<@w4jy9MzkELYwWdy#ZTgm22S8*OfLU&78(X#~``$5~%>)&6@_Bxdncad;jq`D^91osf zw2A#%1G%QH`iLo)D_c&UYvp|~?OFK^H0?*z zK-z0^=F_f};}V;LKiFsoDu~mF>9jYaTp*_N8NVaQd4NA$*8=!Gg==B}(}rhipjPD? zIM(2Se4|g~x5j~~j#EvXhM#XVeV*bH#Z`*y726a$6?+wj6wgpRTk!(Lixn?dyixIX zMSg?Ac70y)5ydAJf1=3m&nfq3Mb9;SnPOUTp5k$e{QjBcPEh2>Oyoxu_bKjIlwahL z|90hXP!#(eq~EUm#}z-Nc)#Ko6~C%T?_R7=K0E^Q=T`E+Rup?G@G-o5WIBJ~CDte& zsYp+ROy8tn1H=Rwr-QN>Rv@|$|5e?#&6iq9&(pcue=QOfa4 zEh6m{h*K5CeJOZ}HwmPRLFUsJIB`&szPHJrulO!Si6e>hTb1X>UX;6E@vDlDD?X$6 zYek6@3HcaK9F`NiQechpD-`8(Dx{yHe4pYT#d8(ku6UE8xOPRkPb)8R8o@uR{L_l` zB+2^IxYQD7DK1c4uDDrIoS;JP4a#4jc$wn0iuC_TeVaPQS3q?FYyn7 zDv@{D(+T%kK*-;w<&&7@e7LIRBXfAMSbmxjEzA4bj5QO|4Z@h zikB%~qj;O*U5cMnl(>J;_XXu2R{W;ocNL#f{JG-q6+@nBPmyAUVvFK2ifa^4Q0!71 zQaoEx;`lWnzK8PHD1K1!zZD-){F>quiu?eK5b(+4Fh}*u?HT?`te~aQJidSj=waVX0#6|XQ=^e?ar^*7OI7 zxbJve`5!AjPn?VKQ~r;NF2;oIEKrOoCKXE+Cn)0boMT6ep%%VKv6_hERj)Wp5w}2+ zzEp9g;#$RxiXDpGin|o$J_LHtRsKB1w=2F=@%@T7Dt=h;4#m$ZKB)LDB0BMy;tv#m zq{vU!IB))-=(vUtDe_A-=1)?rR;*X#2Wp%bM=6ROIrx>ziyb+5u_FhH-8fL}#({D_ z1Kh3hVmA(6?8broGL7@)625)+^61+L(Tn;!;Jq|3Nw~FJccZ_rC>t-;3j( zW;%`&rmBp$%qR4J0eQ4%F%jjtasGuAK?;x4Qc(0F5k-Vg6HyONO+nF*{G54?)a?bHu_e)H$q=h2*p#IXd-Zv*+&`5zjM zVLEZ(I6lQBtv|#0U4wB}Yz>RV^9Woo14%yb= zuV0SmXr;-LzgBK%o?ILFvGg6tliT_-a@Xd`@f@qeZa1s@AQEufCFdsd5_%p}mg7J< z;-|3t`>`K9*DJf(etZH6etmV&$MVz{!*2EQ9gSZf&+!CyyIFnTMS@@7ET2BgTYbL) z?bpYCG0$#RUnztrXOHi-p#5@|E`pH1-WFfIyq~c3&dAeu1M(`d+s*2mnx}6e^l>iK zVaJk}(YILj+4A>+n2a^jZpfBxVV=HG=-ZEhrar#mW4#>r);xXRhps_87|6H!PR)~R zMWeQkA$L+K(|_cLN&5_1H978 zby#IUtM3hY`YwaMYmrZVyi{0y@5&@}g|j==51 z_d1_J_i{G&mz#H2Y|W(Jjg=ESPP^M%6J2$3N3go05Pl>tPaN-ZuwwxajBpG2jt(Z`Oj^;Wx=IG09_ z^UzvPS_@}LwS0&q0zLB zBsJRGHfh z#m6f*`fYSN5Ium_2BQzNRFt;41<|vR9*#bXzme$kPyj~(8&J%fLeDI*Ye2$LKrd9p zuVf`t`;qL#X>03EW3m^gdn>PjT}i}OaWtm0^1}Fd==Wyc0Gf^hc=3z;6=h=|MO|N48(Q9=S5O*;VqlJs1AryZW+M-Dw5;_VPfoR}ENb(w|fDDJ&2-DM;_aaiFbT{Ed zuY(3RS_?7|9RMAS-a~ECRcLuZG=%nqqu)e290gnsh!uYlWeedbpch>H6v(QhfH=(? zW93W^#p#ADRzdCYXpKL?!9ASQWo?|tEq3I0p>0F_18m!(2K2ryK8boZv;AA+A12*KdRu%O<+qaV zh<8%nI0}e=l69T*Go<&%``PAGN%zH{$KO~d>C@uNIBtEUcg2G&yNh&x{0mXg{iFw+ zBAWAY2ve7Wpre3sXlHCa{=iWH$4Z(UkKPIePW%r{h^>L*G8xWzoFe6M{$dbM%tWcm zI9dZo0UXY>R245_Rdf{aCdh_E)D&xk6dVPx^HM<~VJZL*M*%SuE{hLf91=;=<#GD* zNTjH=Qj{h{DIEoflgMIrX&{JJ6v9!!L1;-DM*$?Aq;V8LIux&mXki^`FMbF97S@wa z#QEcbItnQMF$!fJ1r&#=*Y7BR9SS^!eiY8256VI~3U~$>KLLM|Ns>-{I`pZd05NV; zM*;C>^eWF$Kn$9aB_Bqy7_H9XD1axMj7xHo85g?bOIBP5k+S$%)Li)s(6i!yujm5>e`1sz2#*Mf}A2I-|r9t17RYpU#bAkFfc8b=z--{ojT(P3YwJb%OU zQsvB$Ly($eN79%LYI?T+H=08C$P>;;g2@Gr< z`!<^Dm8NOeogBhbd}&RLH~UHYDkxQ71u=%KEh{ZQ2%-28WqOrOb#8PiIvR+s03D3BfDT1z%~TLQ9i7Hi z`O}arJL*D|jny)@Y}xgo<5xg$*^0w)>{0pxWRxxZ2xS=M*DGuPCS`uiqMh_QV2(xE z_W3A<*>E8XFJeaEgOoY?XDoUJ$lG>BOwecqWLy0Y|0mtxOKZB_NyeOc1Br znlj^=AbvV!jAw#)FU!82V*}3wCs1hdY7~ww0lBSg_s6M!JF6Y#q;ldPp}g@-5MPW= zml@9l67{gmcqWK{jq=7bLHy(BbeZu?5U-`Y@k|h>PlB?u7ou&yaF+O-!mTkTobes=UWm`T4(uv>1@+Uk3Iuw71M{n!%puKoC z{+6A{^hErAw&NsrBqg)H>|~~w#h<49wq~TmGXa}Gm8Em|=N9OeGr*n(tXO1NRAkUH zUVbI<=QgpF6mocM!lCcjeAaCg^QAX7pcSlK z(~fasU6>{*x9E*Xitpv@FMc=L@5DFbk6XfIPlntrFFZEiUQNcsx=(q$~o}?OklU(JhI_70NUJx&!N;LX?{JM&utRVJGY)a;JtRm zJ20s3GzxA4Tlv3e9PROqVR;oBIRok3+@{+hHTgv(?FT#gmmqjnPWc!JKEJ222WC$G zwiWmihz}xd@@GI$5+cyxT#1Jk{^nMVsEdo88tT6RNni_tXbp%>ruu4kvl0?+awd-) z4_FbOBG^hfaz#$$1!$|?gJbIAvD-ZSxCfU(AkB9TG33qzQwH*z!2VCGb2{zzK80j>cyIyH-tkQpC7i@x0W9Iqq8X2{4S zod`P;4$Z{h2q*KjXE;bLyiqg_ZMYe&EI#HQBoy&}ZJRBNRp}gLjT_gBl%m^^zZ=AK zr-8qk74P2(X2v!uo6L!?%C12V+%(UMnKK~9eSGgsUYNKej;dSi)+bBj!DK~p%YuvmOab=x~6R z_3+h04+*d0fFLb?119+W2Kc-VU`oD%A^)UY&jAckDQ*o`i&KC~va3s5Cel2=%I`UV zt%KJA<2zuAKL=-t^#MSkYM*m}8k-^R29B9%s>*dbU_{=%bRuJ3)@OrfPR(Rc>oi|G z(KMf{0pnX>dNt=uR_GY%!66O?AcXmh9oFN7l|mj*=)fMu+Xp~6T#G4~-Ei1YKwc|$ zfTX#3)_NT$uzXPmjZ;Ayjcrz=#?TBSN46IHJrX<8$zQ`i+-y3m?@fX~i7RVnL+{yo zLK?fVeJ8aLJ2D6z*bg;|pCSSq327DBcnv8ozUUvJ1AA`&*z_{&uCqA}m}*G;m5sSn zh*?EGjr|zx%7T)u27l|YLx)MwA5(?lh?}iLaX}tWmTSQ*?6a}s-FIfdK+49(kCo2M z4*tP5mR>$;1{}UEcwNTzV(Ry=Y!m$-; z@4kP1!+N|7r_nOa{Lxx)(sW_EVCCCCwA17qVt_9{`_p>>ZZ}a(Ua=eQl`Qb-MqmhH zxa+|R$vZqJSnsB5s}QQx354A6aiKu?l8{>+3QP%h(ip#*w)m%{t7|~lrfViQ2Z|gw zW4Axwt=@*hRn;}~=FAMld^ry}ZjET19iH2|G`Mdem_Tq}I*8BFrK0e)ffVFlyS2T3 z^WM7t*!EVKm9@freCd{Boh*OCl1=MYuRGQe3nW-|({u^ePC3?wXXe`EbBtk%2IG5U z8g5LW{~-qVw3*Ddech6^EAnhi$27KoHM4nRJ~onPRQ~c7@pkHP+O^sFC+1@&CFeVm zJTv)h>yKZtY3cgSE1Z64u}YS7cel~p_SLQBt)V>4@Nm3d-csIY8~<`<`BGbs5#Ly5 z^VSeGYq$FU*L)tMYR=EU*=XMM%NjENo!0WN&rm+c>{$=FSU>+ z6MBCrxxdYxUJMcamZ-18l-}>K!&rU32Gei@?+|6k2MA7RpABQGa!X6F1Wnd;-r;bq0F zWIm?NdLGF!)0Za|%1f%xm5#315;Q8`aR2|rVqaRa8Sb_Edq&|S$7jHA*8=mUvD|sd z+F?o_i`v?6jD7Wiq_Z?GZ-1;vhvcUi@47XX^p<}0b4h1O zd=njxJ4^TiUSilfE?@T)EHlB_*S6q=ePnGk5DS-t%G`18gkZTl(VgU0xRXOQE+lg| ze@e)C{;&SrAl^gyw;-SXd|S}G%gUq#@kWeq`LbI`tie}h60)zbm_*z9TzO+_&;FP# z^Eyyi+REq38{7G;{+zdw(0{0{ywyw$nxDrrNsLcjl;KDu#3q>(Tkhu79)zRQYGz`? zp+!D^PVjBCrJtuf^^O(K+9!_o@ax9x_UiGe$7h8>jb#hZdf7I!`DY*H^Z5!ZC`eoR z6`(i^Irxi<*r zN!WZdC<}DXBdBeo-cn{)c>Y9*-^uFC>)}{EJ}v2OxLlDQqnW-}Kfa7cZ>m*NO~cMLrQU zSgu&7$Zxfozfh6R?#Z_)b}05MO6+FjA5i`h#hVrHRJ>2|D~gXP{*U5wiu`bs^?Cfk z16Zb*R-C2C_i9iMTx--p1)u+ z7u$RA{0@cX4=6G^6!~J$V6`G63NXDvRK>Z9$0#1Bc)a3v#X-e=iVV0wy$qy4 zyjt;AMX{wv`u)l?q8R0Vt@sy3225c3c*Ua?k5fEeal7K6;y%Um72l<}7QaDLA6@tp zdlVU~gZu%-OBBVX9_hCzf2ZP?6(3c6TJaBxaahXKU?|pl)q5<%ZRwYxRMAxH);Cq zMCkdrraz$R4{Q22HT{1S#Reb!5ZilT5cf3HJBNsVEF$7&WvTL~D2^z;N%2DBOvkxc z`KuJKR=iR17De1H$Z^5)D~K)#^1hnK3r!G2R|Knx&_73cen&((-j5L%EAmqX^2aNl zqS&d}tJtqNLWJJ^${$dCo2K(Vn)(hZeo*l?#ZMFCI3E=sQhZoZ?wgR$kK-x#wBj?0 zFDkyI807s2{tMJefEXK2ntLQanx5 z`3)E4_bK8s;P6`_}Q$KPU0zPqR27-HwY z{1>Zgu{8I;WXh)IJ;2P~J~A>p`xKKOems+#eRpIf56{Zqa4qvsyxFg4K5bH0$N(PTVNR zvUX!0_e-%;ztu;-A65sFGxP^QQ$D~%Y?R}=VK?S+`z&_99H04ky|9~=`vDRx&AbP| z+>Yyx-E1D$WdFE)2;-7MLK^ARYscm1plunD&IlT}#5UHK_h%nXxjRc7=dHeimgE!nR<;W2oiK#ghj2{GC-@(1L4=#jf-O%`P{h<+I~!d9R6pv0c3Lh zvzyJU0O8kn0Q&YrD2<)hWvg!v^*5x$h%CTF0ymqmS z?c@D;DRL{(03mas*zrco$X6uXWQ&K1k!1pZocRy&CTk= z)ujzTfOZb|BF+j7sxxuJU(S}grTK{0YD@hUb5Jt2)D5usUT|08N~by`w$$#*)E$f% zzTn=_^9vk__}%crBRxA7+?%?~NHCqY%(Q-9aF_G^f`ZV>1}WP$SbO*Mm1&`0{E2&1 z*N%pj_x^N&N`_Wa)=Ikbx^@`JmzSx%+pQf-3VK2XT8~q^3wpA(`17i@T`!k!+LFy( zJUD#pV=+6QLN4zq6SPF7qe1@`aKqhr{tf;ZHza{7nmPnr?;!{V0tM@s7Koh9=i^X8 zFSy`$n6uQ1G`%3O=@v(jKy;P!$Rlgn|sH8Y<^y zD!7mZn>SO+4fi2E$TuDZsgE*~lf0mq%W?1}rj`h~f~gaPEI{iDCJC8lYQ;hP4fCCg z7xvPahmjMR5GCnE?#175jC2U^d;Wx^;0GZZj+2bK?0>{N4%x9|P@5B5fY!QB@MbhI z66fNgGKKpob1DZj_6SHfM!Tm#>{$E_#%7|`At%VFXpvH$@>*)#S6S*@=yYPYqTO!n z>!>9Vy9&v{*sqZiij}cHF`B7{V^g6&5?g}5o)g^1HcWXNM8oezZds(hhAH>6lBrXX z>_q+-+9K1K>_xr-J&^`>B@uZVHAbeh^1{eFm_Cz!GgBCW24MVdlns9YWjAwGn;Andq_2^hLb}rIbTT??2pUYR@~DYb9+^U!Ip;!Vc_hKIEgbt*5qhhN z&gT%cM!HBJ&grr?!s8Y_@>2ZW5NTq27CjBREmA`Lo7w)Yka4!lc$`jScgOh;7i5ik7R(Yb9bSN^2ghU-_FY_yR6El8;>JzEL!fT-ItO&h>7Eb24ER=C6OtWpvo#1m&Rakuogn}KYJzmOj@FK0rVaX>mgO{6d6`DS)XzKewN9gaMXxey|b%N8;_M!&9BTmdp&AONQPeC@- zOi|>&7m%8BGxHau=Fvc}unRF7QZ2lcIgyW{>8Zm=honuZ`D`ARNrq-m9sWfy!8Gbi z^|NUBF8t{&9={NkMy^43ip#OwXp@R3vi1cLUe$^x?Ze-0C%7JJiYwv}Dm@RRTP4*Z zZz^{8AWhcF?uR0`iX^vjXLR<1WqG`$a>n;L?i3bUhA%{@eL3zm*BeP46f@; zr06@4A&Gbb@2_b6Fqn_a-38p#zHFWOOT`sWYBG;a~sO>-8P$oXQv61kVb!}Cu-U#ayBxjVg_;^ z%&VoAqqGqj#;WDu!qI)v+n0 zb-j;A?=0#{V|Ne!AfxLn>Uz6h*Bx116Zj;<5&yEL?U4~5MenF5zZUwccF#wK%s2Po zF(_$Td_HUWyuTJY@6g^);2hzI%+xd%)mQ1M7mnvK;%vij0{d?GMHNk+FzZmm0gm zs%%7J6QY>YL%y$psU4q7ykbFF&73U+8YN0W5^3zOSkN4a zE%r!AK^_twk3$RMyyd78xKiW_Y(oljOr8+ZV^Taw+&JdkIE36dBHY+;ZfW^zswc@> z{F{~k|DkU)ln0!*V6l4r29W>!D6jvV=)C_X#+Cnv;eyo?(Z(4b8fiLl&*1vauOYhd z&`A5v{yo`9!TbWw_xen%2@i?JixVp+T5l#YFD7v|GB0jbGm&}2q+y>hyO-;-i8KnM z7|Sx?nx(~j%QCq4w`4XluV}E5dBLC$*%y8i1sEw-4OZ*n(O$-6gCbn@bNkne6$H(} z_uV#LFZAIZfh--tDm4o{dd-~LfD21Kn=#i-S2r~^;`69%+9J2QvHF-f+nTC#(;sml zeA3ZJ=OsTO$<0m2^6Z4SJ zWtn(}e#0roF+ z>|m9-(f-6P6!Ud+&%l=9ErWeHnDQ0BiT24*M`93WbsM|KJ%flAxXaFYQz4HVj#IAS zp<*`Brbfuk#6HwXB;iL*CH*ugu14P zxz~jLMTdu9&+>Q+MjvpnA-0~i2h1$H@scqtJ%5>J4iq`ZIO4jbd@6*ppN{D)hZL{Qbs{TbZWfmOfxi| zuzx!F1N*YyKAV|rXx3fku#DR9t5Qd1kd1ANIHka(WkgZ7Nx}#lOG+Dnl&4Oggp5Aa z{!xQyj-qJh(Ab%p8vrzGu`Mx6RaH*xU5=ud2b$??!r1cQ&|$5yPtG|teI1u5vww?S zU(W5rSjN1vp5#q%Oix~AA2er(qQ%$%Hcts9|_cMqL)Xp~@Su1rk7Wx1ZMqc~<`m;q`aXqkOd)U2#BF-)s+ zf~@D*D#f7o`ISmUIOKo z=i8v{M*FJVe16rE-GX=zk(+PdBN&-OW0OwE76vtzJ(geKQ2Q&+mh2Y9>}QSHR@Y=R zK`|Doe+$YN8QGK|&IYy!4^Z;vg!Wth64cw06_eLS{JuQ?9F^y}$ylft z=HQLZX9$gZr}n`WEhyU{c9dOK_LrG#CIZDQ^Mt>w5d=L#HpuW#TSDW^rjf&#Iuw(W_-&R!-3)0 z#jt4dcubxKnmk^K^3`k%2cGNcx}v^km6y1# z;3cjrP~y4*C-EIHkYC~u=PDkpC~;kpzFm2V>k59K^8DP2<>;lIc(tO$bpfD+dg$gcsIeuSdLbp+bgf8gM5Kq&7NXdu0cVp(xfWs(F4@YjC8FFWP2Zs?HffOGPaYS6i;3W` zBtl+n(~$mA&HswxQ!4ii5x))oPWitQQ7(iF9n;GdXDKcwLciFk;bL>5@?AufJ4^Wk ziWewetaz#7Rf=@aO?@{i-lBNB;+=}0RQ!yh#G6IAFDXy&-qiDLMO-#yUg7i<u>mt@xzkQ;I)R{G}p4upH-X-tLIFEQA$K#qNV*DG37|&)R#$^E!{aZ{#J693Wo()9k| z;dK`E-%CXO;#eN_{(?N}3!%Z(UqOUkemu#3ttLXBv==|GOM9VD+5ukL0llrP54?;6 z^badPN<=-EDSrhK^?gG5PZ3e?lgd9uME!i=p}vqF$2#R3h#cqFWkY736676mj-eP| zG?~}>ha~ecCqEI7Er%xQ!%BXJd2Go~l?RyoM17Po=59F;G$Psk?;AnXeA|ITC(k>8 zoM(3OI2~oH#$U=|TFS=x<=>dcZ5no-TdyX^d75AERHWI>*30t?(^k$O=5^PabCt3< z1!QM^E3o_ZodmuNyWOn5B}nk=8};eqnnRt`w+*{rAHU~Hn)dDyRSAsFV_&FcF#5-4Z4#bBP~fzZw7{R+IlABj2#AEcXO z!e!Cc%W<$}sG<@F;T^sNOQt|rnsT>dZBE-pfwy_IkMq|%Yo;SHNjVod4{g1?&#+~X z?0gApdj|>KY##4R{Q53xG}jL5Fb|uuJzQ%`C#?Y=9^`jRly@z4Co)jZp`C;a-Q4*+~W>e zA(f#V-*XV{b~EIfeF?Uy-AMC~-<@cm?QabV+xG2&T+m#OMdf2?JdJ_uW>xc&3%Sg; zyvcFSvzbV>B+)lLoD{Cn^mD`Y*w%|7mA3iyEk||V{xx{ z2TRb#&R7hl?iIJf%eHUgmo`d^o8?yzZ3>5@X+$Vpx23i9RRWvh2FJK=PKz7y+-RuC zT(;e0ivu!kaW0f&tup6B$o(&5CHPQrj(^r6G4L}?*T7>uKM`$oCm&? zpG81Z+N%W44}-P=P3Iy70ZkubejxHga-jn8DzupNlJktxAFzxUp&c8lcodv3py?3W zb4WnbfUy$X%=XPgu~73qk_;+Ukh+c}x(4&W2T9&Yg(X6sz|;vsZbu6WCJ9;3)C#_l z^`1wMU>-=1U(rz{grg*#=$V04!;4yYi$7=Od@b!@G?aDjOs{glx>kncRBd_Yt4!01}ED_YLvP?W(6 zqZNECOGKBUbhPq8RF;bV3A&?IY)V=5IVg&zBcLmzYf0DgD5ayPv8;?)UGzT6PpAB> z=o?w#jOU@JB}ykw(V3(dL@OvWi}tTM=7G`kC^M%XGRvcUP>i;4>{msBBi))<${UqDS6}zZ;@uY|kQ2zP9L>F$U4iZ2#8iiR^D1>1|OyOh&ho?ufbtppAK8 z^e-%X5+{0Zw3cl+m2_Y9bkd!qPm>^o(LU0R>a{cUVG`I?fsL5LhMKBL!=cR&p!c@?U%y=Ph70aTd7>9V0ba|9j##2;UDN5s_lo3if zx`CM-zJYI{FwFx8pj^!ZNm}zj(xK>^k&vh(?M2(r)I>e$L^Q<0Q|pnNN;NWl8fh^P zJc~vA5lY#iKnW^N%!sit%me=$7^RtWVUna1eHht=MWQ2mCyq*CinJG9hQEcyq!|kD zDqx_Q*(LPp213jOd9um46izbZ!kET|6(bPAz*BSOy`X1B3s`hA$7P|6OJSOALxj>3 z(S^e5FG46B`YVTM0Drt_BNT^2^YFKL8UB>WAD;0@I)3&r1wDhuNLl?0J2Y3!Dq@D^lvSAz7CX`1sDGD>K&hEcGcLPiNqYLrm#9w;hH zmUB^!UJpgdi75(2X{MN*L^>3WqtVFjXK zax)hWvDx7eYmhLY)MJE3d_v=qD%n&m*(h@u6RCxD*u>*NhePaD;6gN}sCZZs&Yr}xV%uPXbSa@W2jm;Nj+{S#&tZctlT6A zX98pOx=9up&q-;d%K5s>O;NTyMzZ)82u$3K&$nC|$w~9UpNVX0#t5*V$-6X7R*Se3 z;r;zM&L($?RL$Q2Ox07eRZq!QEmEfH>TK0D*{W-)MylqkIchG4O2?hS^zv2g)J&45 zDU^|>B%BDVuDF{$If4h88yoH(q$OXa495{{6St~M*kqA&H&)$`QFM=x_HUXj!*Uec zRt>=^G9C+!!iuM;eR;0-_hqy%r}m{NWOS^gjxN@grmeWUO_~kXwCeMja@$z$00paE z_7&T5?C!yf@X%XBkA*jat*Hd@0}}jwXChA3+PNS$lUM|TZgIDt&5UWXC2ZHNP?T10UpTs9XJPe}l0T50-e{M3p zsC@z?J&e{q10sQ*&EU5R?ALCqdsd`Ok)kxsC^nWPNOzoH4P0= z_!@c{@TNj==3M;2cm&SCUon|=B!IMgnw?}F5O5DRW7sOUqVeu&BzX^P=0`v0$9uuf z;)%8rd8W6GeAV$h*zconsK8zBG}@LWku1kx9%RNo1_5u_X=K}8f-H|MTIV)H7J6nc zM0x@5r^6vGEOI)tW;uuOwat9cO{XKreG`P_?Sp&pN+g=|=S}Pyf37w?*v}p`U^>s{ zYp4|THuL=l2{aN>I5)s?-l<4&JGp$C?j?C8zsJ^zx9bye_NB(r;o;mbV3>-J;qSRg zzNC5rY4e=9wA3oz|8+1emqV6&aZ5`lWVx609`-&6aBrbaI``7oQQ!go~ZK?^Ti4&YDn4wvF*y%nq*%{fGn=_3BPDR^aeuva(%kkjM z1_Q5%)Ys%6hn)5{n-Ut-bB?dqP&YTr8XMbr)tKWg`G&TKWfPFi4HMdVS+mQ36@f{n zk%TmMV;f4U20L0$SdKl{XcP(ZGa$@{`KBz5odPC2XB{|X5Nye8_)#Rv1;BMS@j8&k z&XSw1A!SN#@|WCn4bn5)Kf1M8yUb#0te$x}%^e5|Fg4&bXBx^h_D#EJ3+492baew^ zmB^kz;FUMG0wib)HnQ_Z_IST+?%1+6R7^0kZGPGR`!>u##fzm4TkuylUep#~$80hQ zxYY#V8-WJNJ;9&LB>$kYgs*R=M4K%^#MOsY5ZGlHWD`2DA3CDER8u?A*A6xwrsIS; zLT(@!Z8DA>CKZ1(n+FQj63jtwBVe^;DgjG~wt$q*U{IOu@6*r8wV_cn=- zTU+OAu+5f0Y5z(=W;gY8A!OR_W%~XfK~gaPGnU{uT@SGo$1N{{*LA}(Zgwxv9}P%2}!rE=6k_?vkybi6|5twXNsGi+xopVb4`fY;OVo2so8;qPvBX{P1l~}nAoxJ zU)!{3N_8DR(aCD~i{sYJYifa!IHD`LYPak3_w0ZrCM*zNlLffnsj@L*9o#bj0*1>w z`a5^R9?zJApUE(?>+xbOW15$-3l|H}{}_Zf?&$6u?Q9(B8OtENzjJiQ|6}h>;HxUm z{_%6}y-DtxT#_JR^=88$OA-j1f`%oL9ZZ6Pf=S3iA|Z)M5OA;JZdKgsg1fd_`&y`C z-Ke#-)~ao#wRNwwYSn73TCG~^+yD3boM-Mm_Ywr{+rEAOzs@H&-+5-9dFGipbLPyM zGxJPW&)U+pP3uZG^h}vtUbkzI={)okpG#76B7>@3;$4d02qi`dBM-p-vkW6?R# zIDXHveeHHw5^logb=_7g9*gyBd4}U&cYJqq&e3f9=Eixmkv3(^cR~;6e9bSu1^ge} z^=*z|manX}EiG%TR@`Mu+yQ?iyGuMela3~p%d9alQL@`1jceDpwe+BU!<2DfjNNN9Y&*2{Bo35z z(#|$Fq^Te5q;~H{cK22T2iq4p7g~1A!niL7Iky!%}uqZ-RWz)T3dO| z_qa+`K+B^l!Vb8`F|5$J$Fcuazb?^AaJLL3cUs`%X@uE)y$!-KHdu zOo=2CcrmI^=Fbqj@nvxqjQU;9i*bB1e?a)>#Cb5zcR8Npnam$b;m2&R%kkXKWPX0( zNH{Nc$c$8yZOTqMy#e2~F$KwaB|4_GWlB;=`Pbh$p7S(%fbeH0nBnt6o5G}!9BGFP za;A2Ywf$G@JEuOJVRc-A@d)nAzVjAvn)bkb=f@?{Y1YX~^FGW*`#2Y99P_>05J1vj zPoeJvP5HcEv(a82Rk}gwko64EIHv!NA%LV`OQGKeO?kYBvvF1bo)W*?TdC|E*N3q) zUMk{xKlf3i<0ni=4XGHP8Zv1z#vA&crs8B&=^i?&Uq(`2^pH|bD~~Xaa?I3#vouiO z4|J%f!*&;!c}U8LoOKh#+SW3l2mdOFn5g8vzVAKw<Tm7e8sVfOBCgdQ}DGZ&7&=t?li><6t7g=rnp1#CyKvP z{H@|26)_AEd7~94E6!3}tayxKpW@kymnzb>JN3FlQTCC8=GtV2|4Q+J zc$4CtiuWt>v=7Srjp82^KT`Cd17v)*Vu|7e#X}V96jvyotoT($F3Y2wI~8fQo%GKY zpHX~K@pVN%hH;WV5p|uf^k`xso-;~MSCq3!A-7KBW&bzmZqgZ8zOH=dYW#&tU#Yb0 z0|)6Lxf&OD>iF%ue6+@ihS-=`d%XF9ZLV0i0AZ^ zM5KFJ@m(VFle1NUtSjut*rT|Kh_-u<;zf#=D&9&&nQm9ys(82J_Y{Ai_!GsS zD!#1visBy>-%U{CqMRLya4udZ{|rSgX(WB5 z;xURcUO_l_Q!u_;alPV+in7lh@iJ}!%D4r{Eip{@bw#m*1A3d%GM)h~;}_r)8qTd5 zO!sR=uAU_Qwjvi%lE#a&V1{Byk?S6LU&=TJ$n%&;k5S~V7t)gzxjd5eLd7~oE+S-j zy<(FhS5Y#2qvC0bn-tGiyh!mzMHvqvo!HRB1)HR!id?Emx=`^z#c_%cPM^GQSA1BZ->${?gIUo(oZV>Qt=r@vFC$+>JOxWG9Ckd zsI;6#51KD7jL%REDatqv;e(at3QO{Fi9B(F;$+1`6lW@OHwXC^D$1#4ppRA>!w<`o zukT0bkMc;PKZ+62uapqco~6G+ee+d|^;JoP96CPVAx;pFe@QR50OUkTLk{25h$0`6 zcm#;t7!h)d6eT~%A43`_`68bR(!fE~8~GhTgnS-rM7dH<$gL+0c`{B#`Ws0jo#a1C z^GEt3$^$L=L5|1=E#-r}*&08G2)QeiZXiPbNlKqaL_Sw4y@kkn`66~0c=sik@s!*-SR3yaPG`FQ_puGz%B1p$crM3@-TFa%Xy ztsp4JIK%fh1kgWTBYHu!877TmdBrl~wh%zsc|n5!U&7B?kD-muZQ9=M>C$L`h0o0B4M82aW1O`*VN zp6nSHC!&QQb z^4M;SypvMoT>^RJGtS7{kRq=G^18u9dBgBC@@Uh-q@j#oK;GSqRA=JOOOf{?Z@NB1DEhjEn0->oTpGmHAS=kOinGwJUJ-`$1)8ddKEpZj{`PEC}z zK>4V~L*PRjWmSl4E8aJI4x1p4HZ6@aUZSfRSvNo)ZAP&_W?xJFqWDoab8okGr4-LEhkwj8qOWyAVH_MGfpSGC=K-_UkjEF@SZ(dPrg=YRIVLO=NY^Zap}5484R z?XqHO@jDe0HvAf-jpCSKDG?;As&S=ZoeyQr=8B@$J^Og!U}!aX-VkUtM5Gk$%2QhzrwMmM z<2m%mjDlyi9Os3Ac0lwG|8QI?ZiiLr%|bXi)1K52dJMoeg~+#E*_PHoCV} zw&#hB4Xd!Jv7sM(8#PBR#wsGK=16D68(+EM3|t%6WAXP!EXXPDJt$V(8=D3!UU$er zv7tv-lpQ>weC-!9y1_4SF?$#}9PtfHiWA68L*ha|9yk^{=K2HbJ0C?yaIt0=FN&66=2PY+l{p?wYy?HI(Z zq8xI)4ufU2in0q5)GEqxc%xOTCJ5^eh6rp^sU+mgho1KUPs_FxU$5^Q9g72=(U)t&>zESQnv3 zSZ$#K)>dc(1%&PZC#<48MjafhD72PhtfIVyYBN?*Xi36YMd9a0V-=+hIU1`dwBFI5 zRTLhrYpkO16OXZq!q0ZbD$0J~NwJDDnOg11D#|yImlfi~ksT_8*AwE$K5vNT8hoKA zAT2Ev15bL0lSi7!>`NG{D4cFHR#9d^31byy7i!d4MVU{(T1Ckr zk6J~!nSQm3Qj4-0t0*+3YpkNY%Y4-;3RLgcD$3W$uU1i>#dR=NQLaYGj8&9Nm{zT# z@VXhRD2GrtwTkjL=A~9qE~Gwc73EjtQL8AN-ZfTHj-hZ^wTf~q(|*2Hl$(*Qv5G>Kja8I%Tq${Hj# zR#BLNv5JB>oVZn#O4Ng~ic*cT7^^50nqn2@N38mNvx@Rluo|l<51}~5Dhek*ja8H_ z(86UE<#Lp6cdICzbxyI0vJvu(Rg_JTXRM;ouyaJMqU>ULvQ-qGAg@+Y?nXIrXu4QM zVZ9iuDCa>VV-roQ4U20#wyAJl*w2{S&VjT ztfI6aSwlMLu??*7M2|QZ zwI5iCi0F|tdtwDHX8bbx?Evi@N0-y@3Gmx@^eFnhfnOp~^l18hk@jXQx??gTY=~Mi3rQ2&Nm=n zze}O(-|_gIit8Iabqj*S(de0fMO37<*NUD+pCyGmn}zTNc(y_G9HvhX;JIr>&!s;j zu!4%5#|-^}=jcCwGyF6x#C$y8M_Y7I>B_-AeIAVs|Ne2g^;9 zPn0*PdhP-%`d=K<zYjVud+2BI2a3R*J=`&x zvkuoHd&FeK`UBsC^lY&u6Atk6P3?T1Nev!4aZ^!>(Vh1LNVumjVFKxn8GIC)t#E=HV z#E?e7%4ml_ZzVLg%O&gIfOfJjPh?%5$Xd9ZtS2P0u1I7(F`o515JkyOrlcg(pq0T) z@_eWoyS5kc_MDK(w3e9;&WuM5J_kzMwUp^(x|EqZg*<|3rI1Wdaax=ZE2c{%_r)c5 zQL-cBILfd_e1ZV`9*U;Jb%ha>Yr((2)cif<7Z>xA9gY(?_Ld_t1)9$#82KRcN5FF` zJ)_`x37&!qcxe5z;81ut)m1=y{hXUDTnbMbE(%&7=dtqeJIqKM1f@k9SC5IbS@0Aa zzL69=k|IY^oJ-UJ^B`uj%#W6gfp;dnG^J9G_Rf_ZbdKg+ACP|`g4lBMuY!kPQ1fqr zXCSmH_%=Lk^xO{*d!vFM!Bd2qDSQf^6wSE`L8Q@Gnn-J*htf`jC!smTj-<$u6z39^ zs`)~f=6?n&T^)Y*mRFdU7uRMpv&e+!33>*@lZMWupa`D%^w28FneY@I#3-k#IaSDM z;irs{tzZx#kJ3XSFTzu7_(oFNNJ?@^DJg>l*0NZ&;cU#FtlG38t^{A*tYzeeu_4%( zsll~a2!V8rvbR)ZBI}h1dLLOAw81k5w+hV<;!Q!LxSlH>GLk%8kzzGLF5H2e(WLCt zO#AIakiF#^h;`cUX>7k|L&9k3Cr)RZzYF9T_6m1^&8eNSt-ga z1u3e`M=D1ZDcnY=QgWbXE_KbBqCM61Htk#}MD)wBGN{IZw^F}H%q@JS=+}!m(f!|+ z2zI>ZLIX#?N2%X-qtF>()4D;hwFwH{4w2Bsp5queV!b@)1f_X7akMA(9(N2JT?GF3 zgkKFDK@RV0icUjr5bg1wW{u_hJ!MaH!qU8)klK^%r>wCnz<(w9=c)`efw64}V}7e7 z%*l_!OL`D-Ga{H7-&Wnn~r3TFGAl&qs$rjE^-@xC+=*QZKZs0 z!M4&}kU8MA{XmSr7oApaqznNSG||K#%q+D5_!~H)g5O}|H@xCec=;PRq+$V{B>W8= zTEPXJ{0$sjQI1Id1`gv_KQZIJU)#j@pf7(FkK!MH`>m|-AuWG{n*U!k)C9XwX$OT4 zb__I${U_AlAXumJGjwhao@Q=qL&la9Y-Sk?Q5eU#Z8;51SQ)}K1Q8cBoH53h(}+FV zT-rY&nu{A@oGHZwQ@%05RA5XnIkuSa*eyz!R4O(`m_|BAm<~wTPui0`rsRSq8CDvd zC>53Z95YRc5|i=Y&QufbBukCUuotwgT0#sz$G8lABk@B7VF`Xt1bukiF@n$?kANrc z_&A2eBm$qh$cPZbk3t-)N~4_!f{}js^$F=q+|rXx0^tHfWDOyPA4NLmmK>2aZjm)E zku`3S3Cls~k7Vh>Kc_k<{hwDIl+HmRZT}Ff@k2RGHM$KB@rlG}=Q6@0&K;VlWp`+z zqL?5Zzkg0eQTjixq9~mW=0EL*HGxL(FK#y+MWEdf#!LP%%6L6}6;1@9E*=3-++j$7 zl!p^c%EKk)BqxGkQYKn30%a`=u6Gz-ruJZgk6TKOOG=GfN^*<;@|tH$re)U?d2^!t zECI7}-=GH6mK1&+zBN((%A6h&Q-P;0-+V5P`pbBl;&hh zpB4hXxckQ>@O=eicQ~K?cl)|E#Gp}}O_Gjnf{s^>jx|I_CEdiaW@ z^!&%N+ZQ!8*S|Tn|K}~c!A47GOGhb;6RqoL=`C%=AIv@Bf9u}Nzp1&mc#1^!S2Ps{>^{YG9wV34p23Gm-hT#}B(j{yE7p=|l0<3Q7fx$R*s;#Y# z;kc%_bvg9$$z|-<8eK=%>iYJz-5p3B!o;8$6RdCT1&OCn+&UfRJ+a)~u}%j+CY_`g z_TFQs7@y0sV?||ZM9!OadfEIkZ~Ob+VKHnfu;aGq%J+romzM3#vc1Q}yjih=)n(q! zUwd=LjmHT&2|0fPOCVOZAm3xVLQk{pSb<9iZ(y8v0E7r2HO% z>h5VdUM-uM8kbd8#x|9)eWq8VNsLNxy z8Ea)?OHYhqN!rf(o~|w!RP3>uy4TgyGGDTBr$sXsRwbGUtzOamT-*fL8pkFe3`w>3 z)JsfK`$G$A>~pM3b#!&E7L!xG?W;Rmnqy6EjXi43sJ3ch!up)>$-g8!fUS+zNUU9vgT3hH+rFuy{KJ8W2FI+sk%E`6YQtMEoeo4(-XMva4R)hLtO>6d%i)&_E)wOevs*1}fdcK z&@o51y1o&%6_1B~I9gew84*d`(b~z6sYlM5GouFgYt8KCjun)iggr}q4wvRXzaEz9 zs_Lp{I~q7P(Oj0>lG-C}bk#1Xs#@YQ($&`3+1$}$RnMrau61(xGLKHp9k*XkALn{5fJj!>ftD(_|)u@B2R^o*Sb|uS}GB(EYWQ_<`;3d zNQIjg&p&D$2u5|6q}0p>hJW0{h1|gTBBt! zCNAq2EnZY*?2g78uGt`F%$&MS3GQY~4H>%HRor5m^!Gh2Ya6jT!ewJFo=2V4(b(Hp z-_!xc-L`S!4PClLHav&NW&ZK_t`4UXX)aZ)xVaXJnu_6KUzZ`v#Dx(1dZJNZS04s& zESz>u)pM&C8$*DHEm|6zRbC#OIIdz`d2D=H`J}R`T;-D8`}Co!a>tFdovdtjg zYi#S}?D|>BMr?p1fZJ#Cf{noSNa~ZabTR4qdgA@k2HqILF z#Dd+p!NRV-_STJ->k(LWMAf3Y#N8Ed4z$rIeWUcMx@bIZA5l8uE-AQ`Y_&E1FynPL z&EDzw)&6DtUTkq$lSK3Bvl5mmjjd3&N7GeVs7f*1YZ*1_)oi1f2X&d@G$#1ePLmm( zIGv$1U`NZDORMWs1}pk#Kv&>&3398MHZI06rJ*Mtw&cWfve)Wt+2HO!j2&7vd}*u! zt~#!seU*rfS46MVS@$-zwKUgvcXhNkZM3SZQF(J`J7r~m$Bgtr9KY$~>(?<7V~-@A zfMcrkUu<@evq+z3b1>I6>A^V;u_@S|>D!lZ&V|gR4Z;=4{G4ln9l+T6tq#8%%3Iv6>$zMbZ|M^BuwTXAp9rAd6HvklQ201pO#&YHV6xp zX3g00r9Z*og+)lC3z{n8_e(^==fz1C)fz%gZ8^Cj!QxG7-Q*t}zjmB=wL!a`7ds`7 z)d1G4owVbY>4!>r6KRIZ{(RE8!p~jV3FozjTW2ciktT$8QKhpR*AZ;>Yav<&~pDUUe#QrTC_d{Mve6hy&Nqoc_ zlM+8w=7~`0KOA6hQLLZkxa~~~(*hgk_hjP-Z23~!O z`kZ$-XH-&@YX5QSr1GSQ64F_xy}0ou15)e!GoMH<-vp1_Tx0Q!7<&k8W8Wn*OO)`wXOsuXb!J396+GiDE1x*QsTYRBEAhBwf^!rXE7mDC zDoTALU6<0wE1sw*oSo_zxAuHYdVgQd(?tg8oox+v~`QC=OOERxDQ(TT@6k zS81^^1-en`jf!U~ZdT-WJ?hC#N5t)l4=Zx_3&XkQo%ovKdy1baifvBBi_IrsK5iKD zi)~Jz*yaR^ZBC%r<^+muPT*Bi)~KeHVqfsoS?-vCs1s20)MOVVw)2*h7*#n z*yaQ+wmE@fn-j=oozzQga{|RSCs1s20>w5bP;7Go#Wp8UY;yv|HYZSQa{{?JmGZw5bkQ;RvFSa>>Vw)2vwmE?f8ZWjvL5popV2_52ZBEc)n-eIuIe}uE6Zo+5 ziEU2MVw)2vwmE^V=$n{7&q5`hqb*3v0ZVU;;BTm;R}_%l2~S0HxW_S+m-&FqS(qr{8LIl zr}#U?KN5?vZGnjN-moJttT<3{2odp9h~S^D^c=+%M9AnOBEJoaUr{_y@lwStir*j} z09$}Wq~D?8VoMWpey;S>8gFc9exx)$2}-*Ch!{+bA|l;*#e)@RD=t)AMjV6Z1QF>v zHGI9|X^LlR{B=a|->mdjVv%Ltr}zVn->LC$5HYCuln8x%$b%RlB78rkhblcvaUv1z z=TIVE6&5MIjEM9rm0qoMhtkIrF}^#Ih;$baq2DD+ZzDqP14Im(9#{HlBIG`&^ealg zulSLM+gad?VcbMS`Yc7-Kw>_{isOjjJDiC8jwWI-b*$2TMC7+o=`)l*N9jw51(x+S zBGP?R@c|<8c|`H2MDRVY@$W09N1b$oh~SSYjv|78lE%+dY*IXz2z@Ui4zsN5l)jBv z2HUwxe^==TiQxN@;?ER!5h3So;wZfL;aZapD;6k@Rh*(YO>vguVTy|sYZR9&u2gJR zY*Xw~>{UEL@f5`~70*??Sn)E&s}!$QyhZUgMLe;k|M4l}mK7S+FPKk6KY=bn5VwqA znc`$cyb?%wmEv5*C5m;5$0)8+Y*$>X$h~^Z?+nHB6fab~T=6Q!n-#yIc&Flb6dzRl zzT%G+pHO^B@wbX^D88-u7sbCSa_)lqr7319%D5ciLzEt&$eo7duTVT#@leHT#d(U{ zKurE+iuH<3ifa`)CqX{WMG$ENjK~d*#7h*fQoL627RB2XX-kXz_b753Eom9&1D{a( z=Za#x7U9n;&3%*Pe^>EiMH=^DxYy&L*s=x9ZIlcjswm@q(B(?cQantNo8$N#k?}vU zS!tR@k>`^lH)@kUS@A21=O|vFc!lECiZ?3CdjrzTdje2w)&hT^@!U5}`C`KsC^l?? zFKGD7ioaLnMtt&rp!l)k-xa-HC!QNS$tUj>z?jn9u*`6=WeXgy^i)N8-$3|bO3zoU zQ9MGC`$Z|ILs4wof?lt**tP|Iy3!XY(q0+UeN9np-GaVVX?b4(y;bSE6}c^y=^j_y zsrW0!7ZrCYifvo)zoYbrik~QY(J@h8hGM2-wjy`GGG1)k0?U-HP&`r(7h{7FE9CNc!S6-y~M{AxwDt_E=6v)<#_5Zik~Uke9uC7NHI%sKgA)6 z+@(vtGR4V?2P;-7&Q;{`+~luQl=nc;$0~iCB8>)+k2`ib9y?$062&VNuT{KJkz0Dn z|6RrJDgHq5CyMg^2tMx0<#_BB#Xl&%qxiAn-xR%k4+MXPBDe9997#mf}0RJ>mCW<_r6CI5Yj4=esi@kzy} z6rWLiL6J7CnEv;Q+yTpRoV*_cIk8Hb<_Cx&#Vkb{Tw?eT#Ue#H^8(@ALd*Dr6=y0| zE6V#c;tyAPnWCJDf$&zPX*`B)LHH6`$jIZ)^ToEIp-zg#Devt7M+MCSRqFq!n z9M@$I5#^O}739(J{Sn=TAhMC?Ygke8f!tiuK*^C% zUddndLH^T711pITHe2xp_FK^BZ6cHz!21{Z(w+_TTTVoN(yu_i^b?RT^E#0C5#u3G z^qr*oB24rHE&4&OB zm41;3eZH(sZdqI^hN%;Vy5*ZpN|=1eaEw|f7{&Hn3Zr~kNrdTV6?cI1)hQ8@@;#-W z2=o2K7~f9$mW0n$dy1hQ?k=&yostHhuT0G6n)05CuUdvNMFhTXiLR+ezWdD&-@6>< z|GVbD@g>^%uE;)uj`y6~Vdy9>AOA7DwlG8zm&(UDx&!dTt+H1>-s?=`&KI|D+?jl5 zAjHkbzJT|aaV8Bu`-oMbN{x6I-{a^n#Sgc#^i|YrDSmEww}I|Y-r)$xw4cZTSY>$G ztOQaXy8F1iW8rtp%LBoF!8oH|D+1i|x*?Bcp*)6@kNTa0pIaWk3uhzDI8!etAiyo} zHppXo%0t%_mv;&LZh5DH=tG!sM!yRY;Fh-)^5!6n@-Y1rmv=M#Zh0SoAfItY-t`D@ z%lp_>-YmqK@@|LUEpHkMc%>1hd?dHw=ayH7&UgdDs2{r8xV)di@0Ry8$$9hdhS{FGtUSX8w`(|F91H)hjE5) zg7S^mh&=FpY9bLvFMl(Y&*+zf#*S&@xHCysr0@*~AKQs>hVKOBLw4dyz?W;7fadxh z4?cJOeINB#f_T>7P~~I!&rQj9+8*@#hVmhaxN7iyn-^9cqQ%{ulJ6VHcM1x^d@*$y zzixM>$omk_+wYl50-urhqZGco@EqRgx^RY%a})0J#^#{kKmha2cjfzZio7uemeqiG zBFwijl3RhFZ#qYLGp zKQs7{r@@NZF_wn5Oq~|uv!6nl!`--6x4W+w0zJMoF1&^@V>U{!KW&MCFNEs_L&<{# z{OiF9>&X1Z59`QZBjrnbmy|E#Oj7;~wpAYwg0j6U5kkjHZ*Tc7{-<$UhV7-7E`1pO zIehG?M}pw*wxrCFv_*P-e2c473(UUMA&XsgTMOKJq&9qAJh#2{f~Z zBnw#(7czuGIM8sE622{QA;XN2xd^gHCJW(`4M&ra6tW{OMEG8a3n?)|zDXhDlZ9|T z*AX(FLjD#PB7B_9c8XJBgiNH6LyQpg~0gr)y=pN{~)|!q+J2rZZhva=Hm4fh!NheQ|(@i?Q``cUYjHf%D>C!<4rQr*oeYkU7H!%^K zeP{*oLO!18vhlOGIO<-=ZBoCO9<>EcDK0U)a~H}!;oD0|B-;Ifs^~$T0&s2^0_r>7?Nw!)r_4= zO}YzQ^~^ky<(tdnvC6OKWm09}6SoQf*^4omalDo2_J(XXbQDXsH)Pr9s4pE=al5_c zXX5`S^IeBd-(yuljW1}%L=8V+k)bni6*CKX$Vg=lvl?eR_?AQ2mALx`d}oV$oQvaQQ81C|hOQnbsIj@(rSpHbl*(39 z?o3T=DV3_@RdQ97gmrXr2XqVe2s5%5mT!})T&~OR7pJpjy{l>9Lb${xlEklBQvByz zQ7~p);x}``)IsUm&c4#t(j&Wi)|8f&k6Y5ybu3Inl+HM^bVf(}Dr~VY-MFr^t9NXB zSKrufCsNx-Qn5w9xN3>!gw`Zcy0_eIHX^0rp}k?r!K_&a#Y))N>d?M>aS%{X411Mh zRk~L>2K}0_dAO^mcho<;y_uaeJL^5|wPc5EI&<>=qcl5qw>&;j{wHZZfZX!vchh_z z&6tFLjV;)wZP{bsO`GLx?p_G{|B;M^0Z%YJ+m~w(v=j{7c?({t7*;k<}Vj!&kz4>LK$JtNEf!zuCZtH$_9N<3%k6V8jF zNwR!1RB?E?Z!jbVm8`WVG^Ath8Gj@>GG{3L1$P;C-M7b zPZHyKPjlbl=^D?P&qi%soAiiD>f*uR_s*lRXmx9ZtMyo8sjw@eyh?yRGRIAd(eKb;csd9hf2%dE$GFw8F}8g z?Ad}mSsRM*gBT8aArbVEM07vh8ZIvg2tQS6S=$ME3u)YR+co@tBElb0`WdBvOGJ0_ zR}KHWhRYr-1B;1(#sl2&=m{^eFzcV z(~(4kuh8&TrDZK7_>Lz%9BafioCokQJ&oNH?<1ny`kBV_m^Fs)QuN_{mGbG_YjF7F zAk7|}$exZ^pg2l#tm2`>4D69ooTs=*k<)67Z&Ykk>`>(NG27MYisveRRgv?_jK4wg z8;W-*?jS61$SGsC=kF{2 zSW(tKBb;-{jFpq8w>$HT3dS6CFy)_U~FLFPj zJl&+B?*=0DpkscVmLmFz$d}J|BF8DrpU;1y=mkAE%T9e_MCenbD0)J#F{FW_FZ8P* z4HUhh=QPs5N+R@?=NM4R0lgP694O_1{&l2*QZAHZ1!-Ud5#?!Clyah69i)NsJVd!# zNTWPl$HH=4M1=mg5TXBlMCki-BJ_Nf2>m`GLND5Hpgx5}=rM(e{1*|KKN0y!J&j6P z(+67W1^G)ofR=iI9#U@5Qf}xYwY6R4(vAqqrrZ|IVS8kf+NFzBV>H&ib)-|+ajIM1-58j3 zgP-ysIxg>H@G(spesp)^Sv$$=`1zl4{CRPW+Q%LJ`=tI&*pax0W?h_ z3_iDhMR=y}LOk{3d9jg@0bSy_y3@9V~H;9w9_ zz!d@|Skql(eY8EJdhrLr?#$}HfArsfPpfXbt1-V!{G;=&lCtXXLsn5)5##*ZX20w8 zt=CvmL0^xHvV8^BQ>#lP?UHTQ#RpfH6r{~fo5QqDs8w82?cMtB4#^(>CU5suN2>Fx zFWok8mVHOQr1Vu!+2*Z2xFCIQhNNtmnYPQma_grbzH9GV{O;e&yj#m8{UM*5e!)Ja zk8bx|b=$V7+w4*H&Kd2VU7k^yJ0EVV$s3@Wlt7c#0^eMpY7*T+4WjYm3Yg+x@4VYAeVmE#4wv@?+y5JMuO3g@eTeXQGFG9d#wxzAkgbY0>Wm8_p0c#WalIhmd<-d2VV67Nm)4iWFw$Rd5{^WgPA3toSQpA&9@ zas+1t=(PfzY0n7K?}N_ciMzm>5u!I}w=k`rMGn1*e^#gz)ook85fI@IaauxoBHty? zP`~M}Juu^4r1kre*OEYp5rIP?GZ5w|$qI1)aUfET z5MSUY&?=BQ6aEEA+YDCU%PiwqJ{Zz{RKlsBpr2C;q2rO472@oh9V&#^6Y7EA8{!^Q zU+4rRO$+@MA?cwX;D3hYdlCtP1HOf{;qGA0a!>*8FAL^6x*UOsU><{g0WVSnhjMps zIPfMU1&6(c9HIg4^9~NDD!GBl^v4c>e^}rF@)SM-{#alIsxl~bRutf~J~)c}V*=Y4 ze&7K3D+1e??`Zm`AQb*FUx&155g5I-g5#)9Wnc;U%O}7;J8%Q)CRo9`pA+C=Pr*s7 zf(3z-n07Lo%fbM!TkxRAp~I2@=Zb>U2P2odzz*hBOZ}Gxicq`3I{H@xSP{Wx^fv_7 zLqc#l^KA~$CQ$IG=izS)Y@`my(BB^59TRM%|JVS>RKa%o*92~6+BNid1h{4^*g^kV zEAwgqt58k@wQ&j4!RbF0*^UYMSy!TRC~yrztiUr+KNOq>0l8A0p%7W}0%s#O6ds18 z!ve>UCyUh?ldMA7%!=oHcY!zEM@jzC(77ZWV&Nr&aM;NJ!wD-K`4skuSYDY%$N=Bn+G0_3^`|SO5qsKvRq(sR5G1^Y|u|U2Ch91b?DICp= za9|oZ8uBtT>9qpOAulsa5(TECw3$)*{Q>T>&m2HMPhe;D`)*^h?CX%szYhQAWDe%d zCiRjz#Hp7)@MR9A1+Clw_w{EEdkOwAfhXx7!Frh{^^zH*ZnG^PKR9IOzX7InACBv^ ze4Ei?0)d4PobIc|{{c<-mo5Kz$0O9ag}V@(<10W=*5aQdJh#G=bwnF)`)W{GN8XA5 zR)F)ASHzwVZxW;A${t9Yw!4a5{3#I+}hTkEzdEaUZA&H_$&Uz%Ari8|jY)euMv6 zC(vIMVC&2}k^YiEJLR85|Cqr2^q)+BSpy&( z{`0#j3>h*X-&trNSy!9~Z*GYvYfA%aE&}8DR@SvIGl#%k_@8whMMWbmJv~;|_4E%2 ztfpQ!P*iT<4$8QZS>y#C!~d+C=pPpNJ^i=P9}93=JnL49FOnN1>kgKi_a^1-hvOft ztpEB0f`WD}lRfhhSVJECvj%a|@ri@3mj4~dYwaK!HaKMv&4QgWh?}^N9aIfXdI#|k z_^v@M$ZP$e*TLUAh^EU<7-UN-niX6-=ooOGG>E1YE;x128A#kai0>+WgZ_lP+6PU6 z|5(cxWtrZl0kb?mYR%3)ijQwN_Nn$3QiHcbhMjw}1cT&ExI=QhKjeI*wPh&6N9+*V zjI#$b-FlEiyVl`Q?6IBj;#~V)%O1({ei!8MpWT@Hoz$Jxrk z5|1@v2m&uh;J2MX&+&*l+~aSwMjWUyKS_=mZH<_sF|Q=YthYu~Ys_cKF+J9ZBM@^7 z@)-_&kU7qfW^War_3et8@5hGH39Ota3?q zILXVIuAEhVKF9)DK|Vo-rJFmPTCZTObElTnI&;uk$4i;@BPg4XAlCYy?OK++k0npX z$+GLL5iHl{4l7m!R@MR7(!7+OT>gW}$4=BK8K(s7naL$93Y(J6Y)8T0q3%*L;hTVF z<&;dwbtI)^Gg&fz26aj{JE>$bU`3%Q07D}x2f zdw?aa9fSXLmH63Po=1eEd@YsF#^oD)G%s3h72+3=oXtxr$@pA!F4IyLhDKou*YaLG zg3MAb%5%z9Gy-8Spj@$W@O;koXf;|p*W&}`zJ>zn_=0Y4@k5NG z6oOh@5$RJa`7!w3XhzXw|_e%kV5Y+UX;A(O+JJ}~IP=*5GPGCGjb zi!Z|^91!899eE)B@t56pz!c;(167f0(Km7uZe0Gd{yg#*2<0#5v61Vb1Ao~oN3KOO z{<1zCc>`qgm-UyCxsc6Y)<+}xb&S8P4?b^W1^OayVvmIn2Z3jrgTT|cIRZDe*&~rL zQuavrb8K&BC}a*14@BZgxTiDdv$>A-Ic z9!<&liDwA*cW6bB92!)GIe>Q^a?ZHnWaTmEo;)-ToH~weEag(1{m(nywmf;8XCfkI zBG4}&D=j_K7r3^PfUz)Tr-KdYa|m=Zhj!#Sk*FOFGr<{a4)I>ft_ zKt?7aI@&BXIe-~~;`oqSg`X>k;jWT6((4Fi_&Ft^uL3`4La4-V_aaJ%U>!~%zill| zBs-kR_*6wGp%OnzNG>>fC@|Czn3N1P*C&M3xbsdvyqEQ2TeSqVV|bSkjF4KFkR@&* zwbz47TrU2{@I%TH0Q-*r)RA0)FH%RCKW4QN#^dL-5&9-M5rj(oc5ghmLGk&j=2Bnn zr9S$ZDBb$A_d`#yoHAYMiibDu$mg*(3qKnc{icEOylUh45bEcmqI0Lh1l|LVUi3Ll zQFcB>;}P)09UdV}Nua)H*MwnCC_x^3)>8Py1@WJfT&-$0NRLD^f5)-+psSSsyCX%= zQM%iGnRMSpwjRt4;{O*rzSrz~`4Y?GOINLH?`SUNo(#;t_LL@_f;)C%eZ^Rw#C6C$CoZ>?_9TGQbj2i+Lx|wYD!K~DyMh-8=bsbY7X!nSGo@;+5Sr_FsZ6dm6i5x z#9D~bai#IS5sf`fZLDPej-R|%>U=FM0h#jjw6%A3;R5y~4!liDzqV;z>4u&ulgr~- zm@|AK!<^BZbi(fcel5UE7(XHH(=oZ2w%2oMapT||zh(sBJXwq*v6}_QX7O-Yj8UC~ z>)^**^O|~06S%zG>|#is$z}XGm~XDnJ?zj#U2 zA~~$J_DdW&3qg8TUHy_7H8}9LuByh0pKR-#JR3iB*2RpY;i_iU;S5mhXKL?k?Q-@j z@e5Y`_`Ueidy;Z=wlUV7M^ z`dN!>su)zYY;J9x)mq;npW%|vnsv^+>-S*U-Ai4yE%gl9cz2uV zE_-{LN}a}0YPOT^zP1CzSjD-md++~d~7QY;q2E2OHTEArCAPj^I{)@gJi(+%IBm?_#7W1-wsy6)a(m`K;k4L)_=woVQKYEh z`03r!U+?`0ShmZmMRRdUlaCZd_`>DHhLvU)zOOW5q1xtIZsKcn{Lh*)jx{cHm7 z|7_S+>Eu&Si0#Dbu&d;OZqA1NpFI>7Wn8qF55gHU7vK_9*HqOmT~MdzcJJx1*w%Vy z9nHF)`p$K0orfvL0d;e%&pRbnv#P0@fy2PR_>r;y+XuyBNXwUb_x@$Ro*Ty(=NXx? zO=)NP&hnm(Q(8A<=JHM6xJr29XS5!b8UDODP9hms36GxND*2mmzQDU>@I@YH)1Kkm z`O}4eN}L}rNG`{{#mW5s6#nE`?j=qR z38wIKwk6@XcQ~1!Eh6E#cQ`qo^Rx-ay}_JXq+^UNJIjQWf7}~v+LRM-rfD1=p7DC1 zvO6=C*u>#--lWyMtiK$Y#3G9$kF%KxxBK2rr)=JaJcoO_9DT^y8qR0`dgLbs*RiMHmJWx|A+|<+wE^2C7d3*pP{q)+#4s5r;Tb>NiwDWeK9x4wc^FAqK9(`3cVb0=6 zGZ;0Y@|I-YmQ0|W5o!qMUBJ8(aIOIBt(-3p!O{3aLK;}1I8(8PIL7(H5FCqDPo&2= z-y^u+X9H;N<6(UT%PcEK1RwhaezBQGH1U|(O^QboHu3zVA4KN7r{d{x1DbgDF2Tu~ z5Ar9?{Xx9zVff71&kW=TX41SnJO%iGL@eY|3n!fAWBd%oIf{!Fk5D{Xk>w?Sm*Vk? zoQ7uj>53OAUZKdZ5RCt};{A$ja}0k<@dd@-E54`rH$~a^34X5oXS(5vC5p78%W&D} z37oI=F^aO!8Q~`?jjwFNf0d%_-$eYaO5dTlO_BQmC|}O10sczq7ZiW1_`2dB75}XG zi6R#Bh@5nfg98+~4xRExDNanSNy&rS5i}-Cl!C8_^cuqEi?W##kUmSQ~X%bi#cxcMToez!;~ILjKSW6 z(i4?FNa@*1&sDls>0^kHvsURIjX#NqhsIYl{tCsdijOP4s`%fET-ic-r9?d6rVt@# zo`xS!g#M=yQKqYi(EBM>V#BIZ;zf#AC|<32gW|1Rn}cu}Cjx(>;bNx)w2Tvh zztQmDDSn{1r!xq-wv+wSeu~2tW&DWn(MpTm4$#~iO8$csxfqo65=F7M1G+)!4#jRo zt`;T#If@r6iv1mgZ&CUNMJ^{L|J{mQ<;i~X$BJT~2lOwLmNN!H?^2q}%gO(iA{T$M zAGOip5dDhW^F*2p`H6!RM<^C3a%yLDEUCH^p8L}BN6&croIqfON1O5 zPeaaT(nxdfN%scEJ>`XoE9xjs)$%19-VRuev5+ZRnReTNwGb;UrVrHeE zP|UEHP|Ty0k8_`-Wdw5KMY3=H&)HQnXNH!t&u3ThoI z8E52iKGH4kGM7B+W#n;A$1RV~_DX~qXXN!Dz%B1?mplx|$}{P%1HpLX7)QsklberoKK=3SO5x*N z5W1weGxGkTe3;%7_b4JZsGmSS{?hP|X!PTlpuc?gS3ar^x#ox> zyiXzTfqu%nHbvfwgPi9swzH7uNr6gE>}Ji z{wnysWeA{Abu;+f*W&`{*MNBHm#=)h-){mRhAG|fin|!kUB2fShiGwl%#tte-iEyI zm`H>gFVVDRGCu}+ry(K08igPA<9$lq-16oeh`l-pGtS8C?rYa?d!8eg@wD=euNZp&9o7*p=MU_qkXSDhtxCbH(|wRzgEoEdp)$;bX-7tBoGdBb~tpI_pr zYqUDM?N6^eHYtpK4Ql6?gW7A?B60O3| zzuk{ie%h@lXI)q&AMNz-u-ZaxKOC}b=CT>xhxxbXiJc1zT;!zX3#O4W!TN<&^6^f) zdQ5?110#mA8kwQCd2NBYmwfu(dy79fp~v&7cYrm@|Bx+p?j7!V$|`vadG$f2^-$%^ zthg@DRiRuzWO?U$r9POyQ|>m=`+p~V%JzZPwD>_gAX8A+;kgmfZc2fbAFKYa@30OJ ztPafeVY1Ju&o_61cS3PfQ&Vw6L&Fp;!+G)gpR&zgCY6lw*|lJ=?pc;a=PuY1(d*OZS|sboMTmj<#`l&m`$m;(fuoa@e+* zOKY}>bGCg2*J=K?!nSKNLQh-y!5GWV--oZ;h2Dm94)YWi;wrLd82jeCvofpC+&ZZG zs_N6Km)%u!SDtYDs!Q(5LmOu*w4^kv@YaUc8Xo)Dsw_P2fW}8Y&b>Hu+g)v_tq-gw zyWkqX_Y(Wk+9B3|*XDbxDP^EYIT&Z; z&8kA5eeiWNeNW|}Fa79syV%-g6+8E=yC&>u{+;97{MvI;JLL8iYwR;mSuycBS;_yu z;(rvq;=sB4Nj^q*Uv-Yq+1jfQ)LuPv=f!V?t=fhm4d~?;lG`bzQxl$D?B!oaFTdlB zXeDB$?U$PeCi23)(_Yzr>`tM+fEzj5yIvr$g}_A=)QlhOnBZjV&&hu(JA zw&%9>+~eFwR}@XJA~?r~}qZB_2gUEeWxZ@1hl zQy@?K)QZS1Yt&!yH0RYYsjQOsb~-8f6#KmTwepc`H`DU>(d!s5l|*sZdv`h#2k!Ee zIQBH8w1h-$`ys+zi$v1!Gi$ydGDXd)KMuESlr^U1&hOWLmYbOiaZl)i6|Sbn&I9^lt6E~+ zEj>82WbL@1sf)ek=L~jZ#TzIihDD>XmJRKFd(GKg2{OmJ8+&_WjXkSleO)n)i1Dh% z+IvMJhSMEb{ocPlFn4*Hx;mSsPGikoEKu*dZWI4TDb;4dPn(S^8QX4V^nW83|W>PBFK%6Ho*wuP>Pv4BA zd3w6~QO88scr|fet9*8G2A0=cf)^(m9Wn28EFbKheH-7AV1Mjecp>n1;~gk{_#uq2 z{cDjLhRnW&S7#V9+lnuu-l2G{Oh1&9Hn3^-Dww^kA%x7}dl-zF@e8ZB9PjJt%TE{K zX{RCD=NMB%JT|~v-u)4i@$AJC?|TxH5NST|VCdi@dGax!!LfmH$a* zn(liJ|1+TNlr&Q4kSzc6z@`(|arCT7!j; z(M!|I!4irN{1_7b!HQajJOk$7MA~b%g72f&f|K&#_gR4>sAp*G2E!B@=P+^lHdIPE zKFfP0^bLRS4$8Oj{uzD-eXQjj3z?CqD9aBykpUk3_kMu-iDZkH(-VNs8F*HQSD+AW;fui8 z9xjQ(rc8K67<<~n93MOuzA1<=E#X5^y2r!ZZ2v^08?j^TNG3##wIiG<8D~d2k#(sZ zc^v7>?8w`wuW~z51Q>5eUPhJ^?8rr+D(uLyDE!5Ch*^Vp*TmmW3b~KPOoPlfy^#vE^gnnbbtv##-pC@b zEbv8&k$j;qQi=Q)`Ct*wvKIRyqfm!Se35&g(pF#O6(rb(7D3}SnMd%uUEqEc#GmD- z@uc7tsG2OXqhtktL4T0JG^uwv(E9_hW`*bt*@r+<)c-2{;aw<;6@CZ&w&nd8GNYj< z5<^BDR?!kEO0a<8qnLRlSi$hoAHz?RdMv#ELe|(oR_TBcBZ8$6HXwXE zOBZ|y6+a-d6-~|;ybfsxWIh8wP3rv^tX`TWiH_}sH{C}ioI1?*FF=;zN5OA}Uxfra z97K6NVH@T3hG~?=7ybjn)50@wMbpF0Xh#{A_hd-R9*~9T%#{{6(v@Dv`%N|F4DudrbNOt+x z;GZ3QjA<)a_j7{flsSo2upoFb>NR^Zo6Eu=uUqy(W1+*6;0Fw!eiHn3!3yeAOZ}Gx z2T)EO{VRfhM5^p%^fv@QMqAHb&U~AL{1lsg)Z6g41>Zook$nvP?ZFdKi`k9z9~6{*K@s%&UX`wN{kkScL=bfrlpbZb4pwq>;C`>w<1gqPqmMF?GJXn=(ZlFY&sd1i(MQwG&DNM__che)=;7Rw zY)CG~(#p{}9EhgQMvt>~)(QX7w<^b- zkIEdKyB$jIEigZF1nV$0az6gLm!geEj9^Q-xd=OY$nT++8hHhOhx0kJ5%v#zLu!PB z-VXOM)A>+Y)~}G*;;grkq0Fq$p}-kgZ=t9|vc82bm!5ScCe+ldpP+8AL(Pw&G^Xf3 zFqs*e4QpdcPNRiu;B-dai@4{Enux-jJ?aavdDf^kNb1Z{H5kI5z=VxhW9C;v-+c}_ z9kY-F#QhG;hbp+cqWfu1IEz_mA zZ^3;Omh_H0o!f6Z8SIVj3>u%2!A*wTnRL%~+)?OcZoz|$dnuBd9iBECiD#Gxu%@e@-*ESPYPCt_wmT@1m zw~KDvgZeqsKjR%H^CTv_oHM!5b5~I1EINm9Rhpi1)Tfv#PwFYd=(Hzw>*h)QRQO@* zPW&CqjNA@~HvvQ@{1wtseLGs5$aMAzCxa7SWCrUsVon{AnT(&F@fdOtDYyta!;Zq` zk`tLlW60$$G;Cxxt>%0dc?h>&0)KZj@;=UVfL1z@0oJ+MK{#ciyBb~-@Xk&rWg>Gt zj;oNANycf)G1-)3{34i6S?PArmQh)mP+4VECJsk?Pdw^1CYiGk6(3&5apPXYC?@%J zCi$Gtpl^jwG+~G1VJDhgt}sD-iqn~t&vNf%2|_P=YLT6EPUhx!kxp7bVc3^)K8YD2 ze7v!lf}Uj4JKm%>o)0yp6uC3tZwp#vvd$?r#WD%Q*-g67lGEV-K{$31^2-}|-;dMjK0klnMxycB(+#9#t$i~SPP-yw@pgV2_ zYBN;D7M#Ea3{~AmUv4WIs^)Yx?ran)RLwCB!|V+?OW|~98^iyG;R$9om`&$6K7H-Z zrt-KFBxjw7$00KHmrSbtDmZzJ#V2%NBSz^o)QY)$nEnCN>f`|6G5wR6Bto4$lVU1y zW}qRRku%}o$es)bY%WJgc(JK}h*5k(2fm5$nWmW+v)W7GIny-fVm4sisf2r`+UF%FJ3$U{Ls9LL+tU~9=)W`jjc-X(|=naj&X9+<6AmM2xK^sJtQ0*|ocC@r( zuW~l?-yzPOjz6D5nB7m%G$PZQhwkC9^$QV(>bAkm>yQted4&jMGgkWp9ka;fAt95Wfxof@#ogw*^CYMCGKo&G5o8k3E%T?uH>#eXKI0!C^uv5vw7ZlfdPEvu&@K;`en{OHI&Th><`Rp4C0J@B+4<0F7tgv zNauNF(Q~M#NLI*8h#fI>Ukj|!}@nfuwpmMyfDNtJ_KqqFbuIjRt8v8W&&ailYt2s9dIw|M;k{f z0~z=PLTm;qO$MeQ!3thcL6aG%@-jfhkC6de)=Wz5HqswNR2`t=~Zbkvm7n~yc!Fe>pfpQD%n3Kk(CRF=M+l9km zCk;#{sS#ezm_>aJy^F`#`U1lc9cOcsZ;WDmpoJD`(tkfT`Hm*@Z2eu$1 zw%YnlwQWY`ZMEz+)w1|Gn8CO+PdP=-3YEPE`5;S(JUG|d5~g6CHHB05q8BkLB75U$ zl$tbt5Km*TNrU^DFEv$vkx3(hsPp)AHkWgELKzieD~&4D&^gH)F>Yfv^v*+cHg35B)ci zqOM75h?7o(izeZe5P91eME0F1KA{7J5~!oBD}KPv>52W-9r!vtF7B%CZQYs>PdD{)G3?lSnTHwO)XOS(nK|m4@JO7{ z3%Jhu-b`DLEBg=-zOtVa+?Bg#ap(BcIaTqZI5mQAo9%=}>!uO2o%G#sAHxf99$X}c z4m=O9vgPpNe)-2OfFt!i{=IP;ry!gooR#Y#hOg{4W05VuN<53XvMZMeB1R6zTdLid z{mMkIr4E{wx&lfSQ!((M$>rCRkZnudY+7m*hR;$i#|{}oOCVNvcAf4-ZV7SS6~1yu zCG!mf4jRdc+#b3BDVU633f(eh`EziXi`Y-v+CwH+(VuNQuL`j^@UKHOuNQ2A-+*3F z{AqOZaf{5ii3pG46FTq?i-TJbg<-nxV($7NmV{Z^N35IF2(7H{WQRB|-mN&(gbtjD z>aTbJOHzc;{RC`Q+=g)h`Df5@;XL-=97U_+;pQTjm+CXvc0Yy#4ZRLEx#&koCvs6d ziMve_Cr6oSysHEubT5XSa=Y!JuzJt zHs|Z_qkBd!5A$O8F9@sbLFKYunVZO$!&~rua@i;0HyWQZe)n2P$5-)Z1s&hQpA+b~ z8GkzHIA4x}Q()m)l!q;&YrVJN#jZ`UA+%r?S`$}s;j_~1P=PCp4O_{}$6e4h-QsZg zT4sXRkmtg7!I=oqHKd{?E+d!m4%LNF;N%?nZuk|(oB%)OyzFTF;m=9SC!nO4Lj&`X zlTuQ)1mZ(fmk+7{G#kJ!!0SZZ)P#OIZ?`ULVw=7g?+ty36FShA>-B~g z!;BC@61KHp4D(BeIgpo{I~{)r^X^71WlUXvG}4ujjy3l>UNPF_T8 zvEM^YME+#tiMTUhCO0N)b82db1)Bmd)J{^t=FL~Z7WxC78I|xhJ)ZrHJ-%Q)9Pv}f zdk_-SqwZ{kaGDO)nRUvgux9W5)$!h+Grj>?PHA*+qCfBcr8Kgv&YC)o#2oxVf59ho zV9XS+dUD*`;M3w2hygb1wzej_w6G7stgWb#z75INR<6s@@-jost)V=Z_l73Huci=I zIdc9Bllrlofg`isdl7gX0xOwuDzMi@X1P7kDZ3Vay!lhU8uiRGSOlMxLcEP{w zcsS071Mxed%PKSRi`;{Ze>VKCL2$up1iXv7w4)8v$r5<58x}B1pnI{On+ks(HcYee zNx?*=n_F<^rWR&h({WwYP_TX1a;^V zAj`)t!k@@u)AfD==UOze!$w3pJPR#)i{T5+onu;J&4dnol5J=vz=bZm{9mDj3t!HF zjd4|u+0cO(nW&i?8(q4Gp`V!k`z{3A+#hkJ9YZGfqRU0(dM^X#An;>l;8ZB4$8*7GCKuzK^e(s;eVf(N9LUA55Mqnn zY>LhAkxa4wgn*BcswpsaO;V%q2oRHckp*rGB(-6xmsFcc>gsrbt>!&m(p3ByN!wDL zfsPaDjwcm0jd?RHEWr)5ZkMiSVE9;maTVz`dKu_DW|_c?7!Cnm9i~S9f)29iM0TTn zadYft=o>P(&zP%XbPg+C?IF6oS+;06qDS_}W0snjTc_EK>^CvbM$9F+mABtC_ydqk zRrf;4mN&lqTL~{Sg7~m%VyG2h4emUMH5qelPLuXTf&pL}f|g7}9POkt=|w|WoI~+U z*{=R|gs^rFnM}Rr6-Tw9;x_uVEWWmb3N5NK9+bKp4Q z>W*iRd03kEBbu!or=SvkyqQaxO}yxm2+H(SyiA;4LkIq@GCgI=v=oW3Oi!6IT?omX zwRofTNtuSJd7QR+8UKcyd`vBVhKIx9Oj~s-+kJQ&v_yR9!2~X(|Qc!w^i@Js_BH7!#+Uuk0134d>RhG zIpKS$MiRq2s9D;KLJ`;yD^4TJOCe3Jk4nF*hSk%}8J$^BXtZ$}O3hF11C5j6!_V-$8?&Lu&+zLSdAF3Gyd8~a zpgQ@K>;*K?$$!|i+GH)S*|4`#V+hzM-iF8!*E0KnIuS7c9 zdnM9v@9jq;?AwnTh~#@?(~QKMlV&DL#a)C75^rLf z<$FQX>_q9PHOFTTW3w<(5QY{dLh&t9yj-R#ld@N0vvJL`oDvjlE~9y`ikj!;gGDZ# zh<8RY=BCWerYXu&oRSets@xxf`v-+mtQ>pYGV?YpY$!n63m5@kR{4s7d@qXU&D42P z-YQftsAeX#*q0D`=MouG=&>rX#7e(13$Robr0``7Umw~q2IWa%_;O!j$X6BTyJ}w? z$SZtFKs?UW030h>z%U%E*jyze%=@%f4_2^iZH=t)hb0xc#`YFwXsxY&wM?x#7;4Ez zkf}Pqsf$7iRL$2X+Cu7hf0S)CyaUSXjrMI_4T-{8kH+M;bzxVXga_&dJ=*+^-1i=!9bEAP-Gk8yY7Bf$60-^gf~PyYb%?+zfWAbfac;oh_`zh6Jp$(sDiVxmvzM^I zuW5{hBh5dc}f8-L>mwfUYiUX{(j$91}Q-$)1J;{9Yqspk)xgJtLG9~mR4F4fi6 ziNEo~*VwFQC=zTBL?>ojUt&H!16#>jSVu6btxdXY9@Y_F%tZI#>gz*If20BoATg6q zf*sOkjxJx@vz?N2O$8Rsb%uO!BN#t;0gGZ2tiUjgJ z27v% z*BtryTZ|7@aMiMVRWa+Gt4FiqXjBoHV?!tP!N9H3Ur|+cE?s@vg6umej znMs_EB79I?gpH5M9q#zyYf@a{*twaGMAxQ^Jq}g~j-cc^Wua&W^LqM?bTB@&Ju`SI zAip9$QnH+R(29gFnNsVEzvkeWWpQBEdf30dleaxvHTuq0JnON{Fz&@+BGfR|<9U+4 zYVhTUvo2C3bgrT<57t5fK7ssj!h;tZoNk)%!r?Rr`(z$T}MFVh%RaO=a>N z?(0I3G=zO~(1vXI`r3GhiL-%d$7f*9Y-0^F2Vpsm)YcKc;(10Ane+;c3StRPG0_@{ z7UPT$lR}7Wm7&gQ`1#N1k+WumoY5n0q}OtMrr_1Hsb@K<6YxItzDVko?Wq&8_=D{6 zc|K+6nS-U|bP#e*vL5zqORum~k~F{ry)jUMY*RID)HoZ7sm57PFxP{|F^l3TLTzKx zWuvY0qS*lW#}Aujb8r)AD6DB;} z#H$;zRs>r=`oKirRw99G+Lus^{;EO?`Gf^ccc+ z@Z*U-dmbHn9e6l97pwfjaoX5fQdcuDvg0Rg7KjK=890S6dqOP@4JQzhbL zL~D&QTQCJRvvAUlbgsZB&4>#pn7NMz;1OCl2_A6A58se&hi7uVM&aX|u6dbf&i6HN zaIQgJ5NuvF6aJcm#bl-t>M63O5}MREM;884n?+`&GA(nVNQb?stfz@u&mS}g&tDug zNR_a8Y^18qOCwz7V908GT&MBV5$k2=8GAyC=b8DnUh&K&ypqNVt))6|BNYO& zLk!0!xgx3fkqTib!2-doey9bO`hTE6yExGho`u99fu}!{p2B}~Me>nJWSBgd<{K{F-FH}_!6Fl?Gpn}}h0`3I_}KRUz4eHlE^$p_rb`>&EDoZ^lv#=t!a1;N(v3oV z)rLx-EuPa+VFaTEPF%L-O&@3TSfg~=X!QiEXJBgfM#DOS`lydYG?QAT^(8zDiH2Zb zoNpBB@z)%8hd96Th?u|p!;ZuwYW~`k=&Y3NX>O5BlRVf7d2medVAgo#S(v2hWcBj! z()qabGR=|9gX4rpg@rk*(8+PZBWO2jd=vVcBy(2b(QXkhH(QR!-4ann_M-Ci+NV;o zSC0R`U2TPU_2Jdqkr-9|dF}MSY1bQY`y%X^SLB5Ll|9r6;edI3LLc#dcD<;+tG8%# ze`j5T^ReDd{PDEB7rs0HgE#RO@eQ>tc+-4GH{R{q!}rn8>BKftb6T64+vb$TO6SZi zE-624u0e76+_^s$YCv(nzyT(o`b)%>b)TUv`+dfIlOBW>;IZ3`4Pq3e4KcXLy3qI5~MGpM@v(r%^paWk`{tsBod_QtfI z?+%Vwh-M$JLQlfak(#=9pth}#HtQ0cPRCkWw>9;*_rsBlJBJGS@!18xz3{OHq1?!ldF=fKI zyP8k+yy1*tlQB=^%h)})S3na5<@5Y|+B&ympAt_rFP*jmY6X))M^`6AR1)(H7qahF zhp7a4<9_i@Y@F8D+S6&ug=E-UV_Q2)5ee%kZrKKxDTv}wr)77ny=yxHFr7B_b#=6{ z7I4nofvw$o`Y2!tg}TGQhMRY#9qu=SD}>jaQ0{j8k}wp)`%mPzI26sTIVs@zC=^c$ zhpFg#ijTo>a;_7Ogi@p1fJo{T{w>a5lUwATQQ;1$SU3&83`dlR7iDf}q%{)vnID=~ zJUef(u~LybcjesqQ{fJ~d!lYe#gdxba7L1WpE;rYqN3ayXw+?rMBK&O!f8nge|AFC zauIrJAwnmucIOucvT#o*G_oihNs4qh6v{`OFPsxaq(3`_ZhxVh6%9muG8CFx1g+nm zP1Co8Hl2_g(0Cyfnx0#H<&-rgi`=o%KpK~aZN`3RqJJw?;f@Pt?Hl1xUQM7p_YdOt zz#x8)4C42bLHvF`h~IPJP;_e1l3YC8=00@Rw!L$5QNZtnqq+Iv^rU+Fdl>cPx<5LL z_4Li~`DK9$*z2NomMlAI2^%iC~>!NGQKJckPuwoLrRWo{k<`7-+4(r=cd-U{tsns{$S8ndaxq-NkAYD`B!qn+Si%)k83Uf;WDi@`MR+kn}U0hsKGO@TQ zH`mQ7w3SpC3BeIbbq{=k{r0bsmloHe7r%K1+u^E|ib#t4_8A;XpGt|s{pJ}_w>LU5 zmtEjcH8EHQ>(ObNM}7$PYC>dnek2^e0mu++_nF4O$BfYPju^OX|slAXAEh@q@YBw`ywC6sZvf9nAm@+G1=YG`2v3aut6>xuQ$Q>OD zcs`9JgT3L`gZTY^5WiOk@p~gRls`8w7vp<(g*!Aje)pw?@}?9Q%nS6I{b`{oCAsKN zpJ$ZRq{=*$7RsO69qea!qZ`dr)uKvkevw~OSG%q_S;Ep!9!0 zeuAa{OB%aFXi8vY|069la#A$!|tMwqj}H(wbQH`t|1J@GDl; zu4-86^kU!9Sf7E}2ezeaE7n<#IYq{J(sb$?Vh!FJU^#Xw?Q7Lr?Hku`jx9ZI>8e`q zENHe3g|V19C&!vvTAaS7w)S{%S1-05>0@%RS+_=J@G|*TD>lbgY^YrxtKPJ#q0zy) zbKYENc^iYqHw`uWigJfjzC)hp{n!q?h(LB$)i$oLZ;UlGE?vGRR(Jdgc{eOySzQ&Y zTGhbU)q6Q#+OWpF#J*ueLwz;&rLBs;4B4F9WBA=1ve;*{Ii+vAvpN35+6KLp3>1hv}ViG@|7I&34D=Gxxh)YB4UBQnElvC-~swNZ2X z&RBO>d)wAMj0)3OlxTqi4^w$;AX^|?n7U=FV~v~Zy;?%U#Fj6usX-=}ZI0RMb?O^e zuCHEN6>DS_qOKdO5pw*}npIUMuf3?Pezf0we}1{~Y;#XnQ_I$--b69f#ZYxEtlp%4 zwy~)Vir6O(+Zy+^b#*!{4OVnis$ajVZvCpp%`xPoCf2xWZMD{!d5&e&FehfXvri*Ea(BCs8{V+kK`1hcCxH`=vb(AN)_ z)vRHa*vfIX$Gl!_x`9uxp|P&M9z735s*cq!UB7fKM&b6()|Tj2>=EsFwb<3Uwbc|o zF_0VTO~3Nj#i}ZF?qK0sTHDpW4b^LHi)~!Gs&Q5Aaf3DE`VFv}xi!|?-yCZ~ z^>1tK+qwf|0_8NLw%Sy9-G)Z5E3b_$uUo%<0|p)^mi|tE5p0fGA63;g@tWgkj-Ad1 zw&82o?Vw~D&o-gm|9YqCq`i(M`$N-CI~e>itLm1osm4GvGl%Ii+uED9BbIIH4qV=J znH`aP<2f)rFR{HbuN2H+!evN*t7A4qZgET_@HOXVaCwawpNjhIL}9jC=Crh&gVb3Q z<8%^BOe>vD9j#`%4c0(#A7wKHnoq;bv)vBo-mN=YTVnA!fm00f7wk_a+JMaYHdnTF zY-gOhjkTyLJMTE&-r+RNEwXXR(bwbn-|>$AwZ0XzZ7=%0e`c}ut8Q*azCQ!zg0981 zuW#(IEAG^YPi_ah#Wd22pJb|9+govxG2?|@Ag&*;d~b*&R->JId;4+Kh<;_}O3Xn? zyIQLsZdehs^W55o<6;e~PN>G*TfKfoP2EQ4xY`ZT)n|N)|+C;7xR` zm_TMmPh7fwZBc7Sb1N=sTjrF_Y3^z2+`1#$)4Hpz7xPneZfSApg7TB1b2_$m_bB9i@=_cJy~NgWKT0t$SDLoZjv^EhXjjY42+4n}a(_i+j7a^=<2p&e^`b1*&zu z_Jd2k#oIgkk*zt~I$C>so3^*kX>aY^-nV0MF}ymv=CtnK+G^%U#Mp)l#=be*deGtK zba$D{Pk7qlIES4X5??#V>2wZ$Gfy%a_W5q|%P?y%b?tcLnHDX>YHl&JvaL)r!I?8F zwuO(6HJ*OB{XM61ZFFzd8#XLoUfs}O+Vo@IzEhvVjRy7&tQ)W=9qN`B);-;QvEC-% zWUP+Wv&W>|w=34qO#zzC=rN~`q%oWr{$4-QAX&?4C>A&ivZR`wc@Ajpv$KSaQhj`N zZDS(F|KL_5OJ@d`di_qW1u)n8irRzS&~g93p5@{*EKhgd9B1m?&LB8Xn$?8w{A=e_ zj&nOKlSXUOR_(SPjUih;)<=C^U5y`gRLu|u3Wp8o%M_uwwgfQ z_MVOwFx}8w2ir4ZM-xwuy_}Zqs>iHa?cKB(XHvXuz_wa^fnu|dB_h^Ub<2a*%F}eb z`+4PMTPena*Qs9u>xV$B)pxnoF#4mThR*9B;hV)A~^g z&ZlP0i+DTjDwOB6hMMZ?df!RSG$mK3Hu3dXH7vvATD`WuakKnPX0!^za-6fRy{51= z*Mo}0E*`5I*T&ZGUe>>D8!C#OBgPpjJ|s5SnS?#Js~@L<7`IX|XBMvt%vltJ#hiRS zPuq(%4Q-D#VXE7O+BkUD=ph-8qqdeo#;w9ucI`+>j~5DxDJS6-G!M83nZ*syfrV|kQWbjS)d`x)1pm} z!*G3KB`#lGVjyelt81#4HrO@1Spe7;Feg^7s*R(m840SIO-Ey%z3V(Sqb{y=%~FGT zuSO{)>d!Pc8`@?Jojl%`2f1o6<6Np{3CJrCyNK*<<%=h_bt#l~vCKHG){do?uKwnB zFK@Q=@pQZkq4)$-Tep7g(wgLnQfvNL%r=4T=e?%7?FAF=xOTQ630=o>?b6EW$M#^e zPGV*8KDDw~iTBBB>fn7MZQJ&kNzBeyW~MNI zlKLu!P_>;7>%FVQ_+Z7HwYa{9} zHYlr zyHt&@t#RS8fhS*#n>DfZRhzwuDsHT$6~ktSlel>AXzjqM$;^(tiGXvtnH|lWbC9Rx z#jYkFufg_kfM5_ZlDRHnudp4xYIAK2HHgy>W^|q-*H+i8YKYB6hp^Ymux+lCalUEC z$<>^J9CHEbZ>eR~Xe%^^>97b`v2<0uS5(>+_G(b4sSx#HIy1Po$a^1_s@ zZLKhDroF2@mCp|P_SV*Jo)qoH45p%<8275SjpKFox>(~%6i-W^dZf$?M%+vAT`&%8 zPt==fPn$X>N%pyT)^yLJ7vD4WnG1QV=9N)5bj-`If zaxAOfT!)L$wM#eoRz#+9kW14+yH4*!)7n#!vs2%c#WWYixWq0mg}H$*72VozVJ2de^pt0fpXBu)NFsRnNx>@-6cXBO`^2Fhm$@nk?&^yiA#W_~2YkaeAe4)^5?^PyUjU=W=Gl<#q zxKQ%ORcGsNGc&E_rOT?`Zy2wG8A%TvQE)JSKK=y% z3#jis+QTT7#(&rak-hFdhVfA=@8Bare%pLhYB1aLkKoTog)*<5au0ya4gFnyEk=T%7u}O+>1G8{At^KnQ^)ECl4pr1M6Gu zy5c7fWq;^cqfM_5`1Ak2@%QI5YV0T4#Gi&QpGkFBYV$N;&hh8TUstw|kKG&D=U#|< zYRSex_vgX3#o&4H+x$3rt3uwEX+B)aXGEL$$ek0K4QI-7ds}sFsgpW1ZS9b7M(Xg$ z$k3?J7L;Wgw5{`;Lzcs_a^K6XBL(>I}o02%2(qVFr|+R&>t4iA7=E~e)iy5 zB5}BQ@Hshs_S(eZq9&8W6E#aM7yEW6hmT4`HsNCe;e1X#ak%*9XL9@;lV6Szd+^tw ziNkfq2lTN=mhW(#iGlEm#$KY-6Mfpu|Kx!FzDULd?Q5MCN6f1)QbqKgOLlhZ2?==&dxr~dqa{(PgK zn1AO7^0P3Y&!3?r4i`T>NKU`P*y9{;51!={hl`C?lJ%FEUm9~h;dwo5He&JXR|fPe zjr|>QmCCq_4%b;0&|ek^uL^`GdWwy|A`rg9q<1iGWrfoLH}lJxCF~?-E~>cr9Z_=m zR+{)N3^d0|&td2~s{{J0jsBLn*6O&64j1Vq+YhE+8wjrr#IFm4*O~MZ^GRJme_cR- zT|hs+Uxc2LIS_tgApAs=UgCUm zVnF|-fc{B=@K_)`X6z+eB^Jpx+t@-yR6xZtPXYwYJAy zbhu8N`Asw3Y{w8}|KU2PnxECsk7u3`WdGqhJDuz0!RwU}WdGsfheKbK2cBF;c)LFs z>376`YQ!ISh0JJ6sJKq2^F4_+{mw)nT&~mQJSiVO(Hw&8Kk(C8=Qr}O_PhPTh<}>% zntbff^-c?f_c-s#$Cj@r5Z)V*`2r*Q;=Ri%xz1V6UWIcd6@u(PT<2`_7BBkQ_Bz`ijPji0 z98Aj3If3wVov$j~w%56V@bk=T!RX1AS;#pr5Pp6@-W!nj1>}nY@+ATJvVi=_!1(@D zAUr5v5eUE1;nwor!Fl{w0r@Wh`CkD!JM6`_*8tIg%r`oF2koy6$eSS3WCWk#SpUZ) z^BguD>-qM8{%(bHr7;}q`^y61R|VuRN}v64IR1T0a+Z-tI*&+Z-iBlS&KH3(KY2zT znBJbk-!vlbsz&a;|fJl03z^AxWO<+?ym% zcb=47Z|u)>ehJw-m_Keu%KWjv4##@+pMmgHR5Zi65*&{G=8j^;>tOsmg>&UK92*_Y z3WP5S$hFd+YxGN;6C`uK8jkhqcFDFrO3ll&n7)K$xMZdMsF9aB4=J4Y1%^9S&Xa-ozgPI(CVYkSW+0rq)p`fh zI|{!a@(#))Av3)`e1>EFhW$p3Z1c0)DGcZ@Q#j2I$HoimB-{F^b52c?*ExKbRHD8b zoC8Vn28ZuYO_X<|^Mqtu|C^ncB-{2r(fLPG{FBUEofyB~O-s&7?063cK+QOyOL8HyD4~C1#%Bh{X!$mz5Ud3kH5=N2zrFyd-J9Pbw=d^-1N$ zbA3|jf@6GAS?L0wRGJj8w9KDgS(26VWBfMDO8lDTWf(DuFYEb~jd@5nk@vE4f8NXI zB}sD^1Y_eH-kttjF7T&~=L&sc<#{8Bk~mi0XZW}bKR$=*X@$lRdpJ;F9d~o2O(oew}5XF%S6bfx#qS^ZlAWkJ*`Yd{4>^jDtX9jbCq?_`U{FB{Im%t1pyQ5dTtikmtD>W}XQfnXyAF=!5+3_MAJ?tA+Ivb*XYWT z{i@GAqTmIq7W7z!U(fe!zxf*0i($T`_5||?MlhDY`{)T0Pk+Gk#{&We=^gP%NnIfB zJ4g>OkJAkNy@c`h8rz?;?Hoxydb}seZ(!{ji>AH@e$&riquQTL#A4&E`jTN1Ket?7i>s<92tsj33)IYQ|5b_k?4gPL%v9 z{@|FDCOt44at1DH3rGkH#bxBvcK?>}*X?;Ud>b~Uhky8h-3c#zlhTDA*JT;c*!5+` zFYNw{;m_Nxv8c!MG|BZT)8jgU>G66uLS*knu`M^qUG2=~t3Szm|O3rbqo9kZGr#ycWwf z68260p=as={h#$kyQbeGpOuthe+B7;%=@&%-^BV4GUK<9uxsoi9oEYv=;OaL3Ez4p zwafO{I>OF(`fnAn|^Ni_N*<5*}e5fC$Ptsoa z0UIy;eY|UfGB`@iAs=#_T=HSNUlzlQ6wZ88UMj8>>%>M8UCHQe6M5%}@pg;*#7~RY zi?@iT-$5T$ZsL7M{Gs@~h#_mj-w|CDndz|TWRA%DRFsRvMdEQH?<+C<6tP|0Bl12F z!#^c{M!ZG5Q~b901MzwBMbW%B9d`aIneTb0UEcQ~Lt)D_ai};#%n>JxQ^i?giFk~- zSX?fy66?eb;)!CDxLxcJd&NECx#C6Q72-AG0r6(>cJVIpKJmNa55ymfPm8}6e=oi& zz9Ifo{Ex_2$*>-?#1Y~+F;|=+7K!EJVzEl({T13ajkfY*e>oC_lo;P z-ru0zTgAJ@2gJw4XT_Jq_r-Kfpo~9KjEb|x`C^sG4JE0+Mcg6wiRX%!i`R>X#5={G ziN6*9EdEn;aSmlVM~UObQn6C35jTk~Vz+pKc!l^Q@fq>=;_KqS#Su6mG5u-cTycrG zT0BYIA@+&qikFMmi-*L!#KYo`#b?FLAvXP^#WCVUk*^hI{$`3rV!60hY!Ek#F|k$L zDfWnGi5H8X7C$fEF7gFcO#gf0uf#uyZ;SsCGjKwu{unV&EEE@t$BFC3X0b=yEuJmz z6)zJ%FMe74k@%wcx){RRjQJTV=7>|p5^=G(THGL>BJLDV7cUUMD?TRvLVQE~mzaqK z5A!imEESIx*N7X%X0cN|L%dYHR{WxPm-t`ekHtTU?}@2Hl@D>I*ehNjULoEfeoeez zd|dpa_?8&KLYC#sCR0#eahm*#$p`UXHp$0It|FnoR@|iUW)gktG|8VJ5&vAtmrDMW zk7!rlw==Nny_|M$tGu)f5H;p0g7=S!X`d5vVgG>73Q zlKAk&sbq(^mqa+<+!h{=w>FDklK)*K7QsIvk=`%F=M?@2$$uB$lYayYb6!vLorjD+ zT%1DUI-^K(rQ{WoH%dN9@~Pxa*t&{@J-#uO`MpKFOZ+y8FBN|z`DqgQ{iWm=CBH5C z-z2W%Q?X!Yyc`nxd6M}8P|D?!SCPoaI?2tFcapGkn*8q~aq;*tiTFQ}{G9l@_-}=$ zq0Xs4o)#6`*Zk}oHre~tWa5${v@qvF%zZ^$U#94+~u zB;sd|u>O-s$kQd4OI{>-g}9YO{yNA!d;=``LK5+>7H<;o67MCU{}_q*&yZ8`ZeGc6 zkcj`T{D+_qQ651;FP}ubB62!j=_Gj-3B6j$n@HSWIf;bcsU-W8{I4Nl=O9_&IA4>@ zcQn$@cP0Nw@)MGON$$tGl0>{eld#KII?{hME&ypamz;y^R>_M<*sYYjMsmI66UeKu zwH=9g?Ii5>%l|wQb}uK39p|%>zevLFmnGjr-s3p;lhAvFgqBk$-{w7m4dg*x5oZ!@Ju_#J^CyO#X+& z`$_13pR976-;&UON&at({~=dkeS^7;`eRAxO_V&HyaM-iNa!sfQNPRNe=>=9ZR9G) zxqw9ci{*b6d9~wQr|?50;(tZ{KO_8m`cHWlkj<v1Fa&Y$jpnWcjyA-Xoq(LhmZ^Rub{QL9WBQR!PMFwfMTi$7AlN-c<4q+&3km zzlcPBs^q^~{#TKWI3JR*cdz6}#NUvx_bRy&&qGYKdg&zmM@XI`&J|aa&_7x7cClUl zd&TR;uadBLA9(`i84~$^K*ZuyeWmuakU0@|PrkQ+!nZKa>0%d9vfYA^BY~j7O+xH&YxbP89P<#497wKJ{c1 zetIDPeiHG|kbDvOb;sFHLjSY!zmdc^{Td1V`@{zo{-oqzioX|MRrvejxG3TyKZWA4 zB+^?Wd6VRB67~NHvKi|J66xL|-XlIFK1xFWXOf?l{G#MnB)={B9m(Nb#Jd*z`IAUz zEQ$Q&OD>bVNOG09Ry={c2^*Y}u+v2%ALoddiPwoYi?@@lScj1hI?fN}|1f z^DK${{zm?P6yG7CADIFoe@By$Cz8l#KDh(u8_7$^2XWq!Tr0VOgx*&9cao=Ko>ll2 z^1p#>$9*2j-%$8{lD|j(7uFdh^qwYfa-8So{{e}7W#ofh82=>mNg`h}C6`KGAbB}? zC%%6o5pM$tyC=(k9|=4A$sW9zNAm3??0!x1x5%%eeo5#(M#9e1@_&Pboqv)2$mdim zk0N1boaCvJ3nZ74-@^HeM7$Lw?AFQuMDqKNvyI&CIA@cvcY*w`ko;+d-!A`q$TP6s zQTS7mUsCvA$ulvordfNV#Bt>JQLp5eP_HEP7Lv%=$Y9!K7c{K$VciSn1pe<=yQmE?AL9obyQJ<5KZc@;@l~ z7Rg@|A0c7)XXI@dzmi`fF+Sgw9GPkLMu|CMRLmD=iA7?$xKLawR*N-ay|__4QEV2s zi5=o;;%@Ow@jP*#c)7S=yiWYAc$4@=@hjq8;(g*_@geaM@h9R_;xEPL#h1ia#J`Gf zi|>j55gi=7e-jaLIcelEB2M#0&J#<>A?PP!g;*(KNoT?vL@afTj8m>*tJo%fLd4KA z;pn1l6Q)I;%@OA@dEKG@mle7;upl*#IJ~l z#RtSk#mB{;i@y|K5ML6__lL;myON!-%|G@=H|59_j}j-5=uCOyOmU9L=cyS!Up!Vc z-ygz%wd7jy6tP9zDRznG`$EJ!OY&avV(~NL=fzvZ+r)2+=6gceG2at{KaxM6r{?{r z7sNk`e-{5PzAHMe)sKjyMf3e2?98G(0rzP{^SvPCg_3JX48nSGqj;j&Dz=H;VxM>+ z8O8i8UMXH99uNErIMl|0mLN6wHtJp(M#dW)QmUy0cnfNL3TJbaD9isW35q2J+jC1P4 z;$z|y;_pb@?|NA@-#qtC;a_7H=27 zD&8Z0OMFaxLVQO2mH3kQiuhOYZ84H&(@Pg~#EIf;u~=LnE*7iAmEvX+C+U;K7IBAo zn%FO%BVHh0Dw^+SVdtRaL*iG&yTtp%!{U?V5?n`!zZQQh{z3ec_@?-GF_LcMr;B6A zN_-z6=85KeVEE6GTq4dFE5tQoop=hl4Bz{RJH;+>mw1MFp14oES~TAu!_Jo|SK&Uh z_;vAK@d5E+@iFlU@pt43%pc-w;u|8@UcBFzDh?IR_t5Y+-#gb}{-8ftD9#nDNG#G; ziglv-9vc29NIqFK-$TQ{P4aH>OwoJ~jquAQUoRdIZx!zl9}piF9}}MtpApUX)v)_J z$uEn4BXPg)9q|Lv&9MIFdu)VfOCBZWh*2?LEEmo9+laSRao)NQ@pIxK@mBF`;y1+m#qWquicgEb7Jn6U3-EU7RH@CULQ{R9q?6hz+9oz8`vRk~_pcakqGm zc!7A0c!PLQJS2Wu{F?Zn_DRmQB;P9DA%0u@uJ{AdJcogJ zPf318d|7-|d{Z>fWkCNwlEcGne$8_k@EKDu!KhcZ|pR*LJzFSv&B(jj+iG-6N|)oVikD?)>mSk z*dUtcR1n@Id5738o=u*q=T^W=B!5!8TD)GoQM_5aUHq!}fcUWZnD~VFOYwQ}H4-=D z-Vn`mFOdHwIR!Te89qcDCXN)Rk>}xiS+PVc6D!0@akW@0ZV)$%JH;+>mw1MFp14oE zT-+~SCw^A^ifEp*K|b%3d{}%)d_??-_>}ld@p%|kqlf_oCP3#u?#0$ks#VbYg91qetAlW?61Nlpmzbu;PdM?BLRLbD@#UF}4 z6MrF^=X;>{d&%bc9?0)V{y=m`S$i2`wm3@65u;+hSS~IUahYu9K}=WXzBC@|F!zh2 zB-S`HNX+HMB>L=p5_za3$6#ENxbId^PSEe!CgJ`o<*0t&lZX2Rl&9+VBhww{T*?LZ zRm!|Sv!5~&xt^Sd>tYi3MQ$Z=zu-=?0{x3zg8N2fC7x3!m*M+=vI^gSkhtD^fn4P{ zuaGque`Fo%kz9vAA+Qn8uaH=84h6UrAzq zuO~5oZz6k8ezG6sCwHU#B<3k|-x>4Jxs);PFDB1J`APJj>&XjIe)1BOpG5of2v_QR z1u)&QGMdN z68!*GWq1mS{?H<#icG&i)fje@$XuU@DlzwA&lJxkqxfD>yqHA4xm?^&qW@eiUQePQ z9S{$a=ud~lTS@e*JH$K5V*9Fb@;=JwXNScHNc6Xd#Yai>yT`>R$qL+e5uYW|51$iX zAS>})lK2XVe)*dC23dvcD)Ai>{q%j&VLPM0n(s%zbjmf3lO+x#(SJvZV@dSm31XCN z#P_h`3=;i%wpdJ}f0v5$$t`Gqv64jpsS;O`==U{ZJ&FF`C~hKmp#8;DNQ{RTaR=Fs z=LyAb663>s{{rr&jB#?NcrJQ1Z_mdbuSBuw^XQKVZgCxe&A@Nr7JhZ=f zC%G5xFWyIDoE;V)ATiz^79S-s?j9G5t-n5=%*yw?*#Hfl%KrBarTjyVO($=J)(IK zZ7^Fhs?5xDD7pz>Nuu3Q1S6k9qWzEsBO__kju%V5oP_a%k`Iw+*ZU+NCeglTen9$9 zQ=Xvn59F69qrKme{`+K9L|6 zn{k3dn{fcyj04Vdif{Tk`pwglpC!?M-jMt^vI6r@7)1Om68$M(@(i*P?IF36T!!Ayf3{qY@zzfYoHj&wnU zk0sGRizSzm=%?m;K7^a^`LM5Ak{>70pI?&v3VAB# zamnwH?Wm^+i1db$=;!&8XOQUc6_P7S^!rB1n@DuNZpr3%dKd>6OTL^u6a7!}Arj-_ zKFNp4^RVub{2aL#`IGz(iSd$!F=6sYV%+3QHow=y_;Ct*_jL3%HG_RUmODHbcD?U& z3i<9er?9yfuY|{&<_hUqh;4W5>pG4LzSbp4C$VkMKyPi}lN5mcIP7cO%~n2%AnaJd zO2sN{D%ZKTh-9SD}!M8EUN2mqT;!Y|7>+~8}i}W?SeUJd@;ViMkgePa>aLw_X+Ey$3FNKxk)~gmyepy!`7U`eS_Wm=Eoa zajigK7?9A1b~Lj^OkyvZ!Sg#v-}0vt?2Sob+NtIUh6iiJr09YEJ8&PHgyF669c_Gm zNn6*p=2I_K?vw^};SfH$5BZ%gxUQ@x>-+ z1AVy}n&*oXP)T7)Tuc;|d!%_v319A&Hqe)QrkP4K)iv1eX#?YM7qx-D+)Zr|U+k*J z(_&%3v^}#7Xr)qYu{MYiY_&FsFScA8=*z9w2KsUfHe1Zr=Kk&I#&{)NVT*A$H8=Au zG`>*e%`Z0L1+m*(d;MMmeefw?Zh!t7A&iEfcX;J}0UmyP2Yltlt%A5cJ{#+| zcPa$dhj)1PM#0~&w+jOEfQI zetW#u9c-NQ8DM{TABR2avAh#~_U;eZ#J(xZ2N#xJp z`#yWQK6@#M57l^kR2;!x2>yP1Bd+90b#0;_EcDr{2-qt|LYutINsr?EfV~N@7ll9TkLi2)+YqqF zdr`QB89z{M=2#c7R{?uozsD`ZxV^rBy<1_AYZ&ifx;(lA_NrjdYu`$Qd->zNJAZqO z#J#L2!o9=G-^BrYTYULj24&CQX9M<1VXx3L2730c3)pLcJ#SoA`Rsi?V6O%C?)KU9 z%FDgO{O#KZd#6B;?SWgS@%%j&us0Tq!#;$2hiC7RfW4<-&sz_XUU`2Xu*bct25S%Q zVdl@@gzId-V*Xb9?7bVX_YUk0HqQB*1;4#M*z@{Jjn5wUMDw>VzZ3Dw2(@^7Ob_S{ zgC6^fcX)B8z~N8#Or-1eE8J?1*9-Sl^QSxG2=-P4>|O1%hg;Qgd%UOXw^w-td+h;x zclhiz`0VWu*yA2xgO!)}jQ!<(*k^Bp&)&TOd)=@XMYwl({r>9#d(Xk1*KQl3j9?@G zB4BSn>>YwV|M`;X@^~v??={$q!k_*0M0}{n{0_%B^tZ>e zf%@~xn+-jG{bgOBDDO!=d&L2JZ@}JQ>y6m~dm~}b>whtyJ#Vjn3?Fk;BGZG-L)lLLASpf_0l$^v@FLl6Jshi7k1 zK#zOk4VG?GK(8HoBjVN)`8z$JcMkLhtCvqoZ?0_I0KM;enSvjk{2U0_+k{5*_bV^T z*8_T|LJub6ho|>wK(F@*_I@utoR-XSG4$Avyu%wGzkwdpsZ_vTUw=Lwe%|;&1?;`_Bm2ER?OlYASB@d*1YR84_%{|K{C?j%JZB2@ z{PlAAZi;bWCTT)zn%rVDAqxj~d+^U|E zaZYv3icrY2#InqYmxt-3+jdB?>#G?!Uk`PbArH0=BO$KIvr6E>n=CxSq10;ucPjpb zCKef=^m|c^a5#l8oeGE39zt7$BPm>hxfMt{wDfqYMbf?lA)KCaF{Sjh79bKyX~S0u z2)hJf?t6?mHlGnI=`3DDC$B->XM6BBoE}+ajHmIQKuQFe*#g;}f@(?+C zFP*7$4mZw5I>#6%cXW=7GtSSkpyT&IH}xN|m!6V77P&~f03Inr=yWgw;m)8tg1wvH zhUD@#LrNx{LqaTnYC1P1$VjJt2H&d{a@-g!r)JKCmvn~irp`Bv4)@XyWtg3(Gd@9! z8Lu<{j(Z1?I)YmoDAuSSFc$l)lW`ONgfe(9Gn~O>-Hf5ke#Vo`PDT@>W?YNEX&J8~ zG~ID|do*=2H#kWBGX9KAojMMxY5Zk*>aFKv4Frx_iPR0Z{LwuM$sjf3 z3HYQr?l1`HNAt#BR(JY{am+>;LefXtyl|KJ^s)4gq;W@r^zj97XQfR;j?yRWh3#Q! zbEz|tSs9s@Pj~bc_)ka+Q)kLP=ttAuq`#@N{InkW&!qnBwBJB8z2GVQElqor?pbut zPvf1?^x50-w_<2V?{+7>kol=h+sn8moO`O$%2As1Qr7*-wEO5TXBE_>)zHo{>@I85 z*ly_y`P=&Xv@vuq{uSJfX*aQq4b1(blk-kJ6<{7v6UcYE5!%v(F%9ZojQu?mm+0vsVU z;Ui$`NiaWqNIL7vWO>LCb{Z#*n@|kN_%{rUG}SpIlPY7=TA@EA>v^b5NaKqIhaAP~ zjGCl|3};d^V7eH(=@HZY+L7lO8JR4+NgyN3CIHJ$#?Zs~J2LHIu?y+gSbC)rU zMkg4f8OCTn%v}i+?rh{Mz2hGI%^I7TTMH{goBOvpnR#?NLt8sroXjb7N74%LH#48^ z^t8tiA#*C-S!ro>Ph(3C8^+l!b2{CT(BzTB&S8`pMx2cAEz%<_kh>0<$t?IYlFu5J zHS~Juq}{>{WYg)SaleqPqfDfCVRWabeFY`SI-2e*SYq|NynUQCJcekg+Yq`k zD~E&4)JxVlTQ61AAKwV?k!ifkpEcq0aL-QTE+JWyST7Z(Ub3RhTb1M9!w9*zK`A}* z0;}jZG&zL33#3Q5XZq3HNMN}6!x0Z(4&iLJEw>Ob{Q+n7tq2=BssKJt=;+YY>=Bu6 z_W0~!ILU;1U{o?+*U{2TgL_D~0BVAK`{ zj1wyA=r`-+xsELz&$*4)(GRJ5nRv#WPbTUXzlZUr{? zx3#wv;d}S)w)WPZqNbjXIh|ddt#ewNn%m}-#Y*SQEiNfPZLUFa`P{ihHEo^!yUR4Uv=((VZQa2y_5UBp3f|@Rf13@|anbgzTLU#yR#;kC;?<8+SX5Zl-QLu< zt*fU4id%P3o1b0BbePB;c;upJXKPPqYkN@-sZ@z3fQAhXbX8Y5y=`Z- z#`^FQzo!zxsE*YwTODiMTwm=dIj6aOC(7E?*Xo#O{@S}bw@10g zGY<2ozmpGCwYC`VWi@MJRn;|4Cm%LV=rD$~w(e-^QIZYSYZD3XG$lfIm?EkQuD&+B z^s5I?k+yX@-I&Jh<1ogzqp5G}4w?D?*n9W*xT>me{G2(HoSDodnItnwrq{`(C2f;5 zm)>vWI!W53NpmSJMbb1$o6x3-NlIG;DRLLkma6mt1O!yXTPs#UMG#PYK(HVpAZP^@ zkmpfAE(H{R-|s%_%$!N1^7y{L_x=6xJD*Ix`?B`hYp=cb6b*H@v3@?%LR{F~!nSlZs+piCQWvko~T){!uGSNMUIoq1M}| zW7_cUorvDqKQ`Xqr)^gP!#qHs*lyj?vu#XEP>rSLmafkF4e?S-VU?)SrnOBSjh5CD zQts>->Q~h!nGcTl4_Vm^kBkov@060Emd3hO940B z+^&i~ZfQ(Xt8*I}PP=KYuWxf$&p^-4z8yH2vAd^teDG|Pmt_A&*CQFiv_{8O>8op6 z+dE^TqT;voj8XK#9a7DmO)d59am}%0$|O}`QL^o9u&RgXE%lJwXJLGEsa^mIWoLsuy?Or03|$SYwhZk=H^hBWi6JKAQQ^8 zbJq~K4fS`AflWW^v1hvqi&eR5Au8Bz!)Q_Uoou=T!=vM>ozZTTy(&)Q!{a?WoaP>F zineNfI5V$*RBKq<6{#&RkEmWT!m*`nY5CGf5nli9hl=M!HsM9D`Tav%`q7K^l`JgT zg15$c2O{W12FGAsk%|Rn3zjbWNTg&4;;%&Ch?mAoh8Dm-KCo+O3vesKw~m~>pk!>M zq_2DtLv{@JjF(_QTRAqob$shcq-5K+K9IJKSwD1{E4S_31+hxD4)u?X^=#`e+0nmq z+xWoBG6d}$F6rOX+pnw}DYgy|4UU(n*V0NxhE;EeK&wM3p}OGXv9D|lC1V(XflS7C z1)YAdF$(57h)<UlG62=o*fFXeKy_e!!@IV?;&%>@4{qJ7Il8a<_4R8z z;|8hv0CehBm&tZFG}PU*125@yvp0x$R_vB+)y6wSO0u#;i6ZGY_$Q%%$7A~wGNeC?=EzTu<$J(?PkMN?8kZs`up&bw`w+a z#~W)CT0?E^vPcolv~zfP$5;ddbp$3EsTY%sU{=)&8yOxQoAbYH#ai_uOx@J+lA2x& zXHuq<$^bO-g2o@36M#2*l`mwlb*B|0JqvstlLe+Ow zTnpoOq+A9iayIP396locS%MXUO9iV08w7c`%k&!sHw$hP+$DI9;KhRb1wSu%tKioK zza@CL;7#-=KHeX8-lC`hWiC)3N8}ldKTkr1y2{;EVxy0haeYWNUzSL1MU~U zTE~I^dhy>ZsJ`1m_&3D=Z9%?sMn3lnJ}CIO;BN%~B>0w~i}xT%mnO(}x#+JDTqVd= z6^4IA@EpN?f|m$hDR{M@`ko8v)IN~FTP6IffIm7FQP+iJSJin5bI5?b#Vm91v6Dw9{!UKW}&W$bd2M5 z3V!Go|0NxtdDv;-RPQ@PsZ6=-I1WHiw^k+L+#iMC8rttY!>RIH0kqSQ^c?W|Json{ zackkX-HbZZOng_sj$4g5XTBrZRzA!ZNz`b>a()CqI}M!L*@!%BE6ou%C~lUU*K7<& z+zZma$b-LeL7R@}Ee+d55=Xl8@IzgU*ip3@pgznqoINN99=pUYTXv#DZ0%g|yBA^U z_;C!Ro*|0TiERV$s`HenC;PJb)%NYF9vW@y8O1FkALYcJ;Vhl}8O{Q%<1A4RasKr) zoO3^`a*I8~S&p}_je+Y1avM$@U47OS1D6h5Tay&^N8QhQ2bM%Xc>DbiQjbrG+U_l9 z-7?TKkQ{w{iWOH@hs`gZF$(nK?#Il6d0Sgw{h;i`-M@S}yC5^S_Lbvfo45QJ?(M^R z<@Rk_#czkM_rrEY!W(5RMYQ7_y%bY#Ywse6*=RX+8bf@w|K!D*|fuep41 zdLw0jqojxbUM%$LYInP|L4+ zJt^Z*L;qHmy6otshpEk!&$A?2QPMM0RXIzanqH83T+cVYa?^knQ}Emqs6X46`b>tR zoAT70f^_I3eRTl(2q=97j(`4yzwG|M-fx7C_*ehp4A*h~*DHP3>XEF?#&K+x!qSzQ zhs#bXTdkv@B`?2PZPoOq?Ux+(9XZrssB}nM{=myYJvk;_a*-=q_3E|zAmO&;MGdcM z<^ynbH5s>Avd=$eLTw`}vy_8f10<6RYU6XUg8wk6Nj3$$6< zDKBh$;KXHZsjqoFPrwE~SF`&S!|*@z1Z)4`BPZPZmPae1mW|a;TY7BdE)@g2@D?N^ zeZjU_TWyOfimp0dk^kBEUjM7>y{wKq?mGJByXR+-Q{;&gmzAa5l^oHIdn092T3^+4 z_bd0iw!MPI)wiNVNF2N zNJYW^_x}3VgEhC^<9h9bN=o|p%Z1xmjtio8DYU#rxx=rPds?eK?-_?sqLkFv{J#6@ zQ{tLCToGL^nk&1Db$Mfr=bq2iWItAXyndShv9Kyt!BbVQPtD3fxpG$LqFlL+xyKFB zM9OP=$}e9e7t1R49r3F)UZk0om4Y-Wt9?l0SGr*N-N$KF51bg^uSJrmi%*}di$_p? zes{C@s9xaP;*0LfuRT#XUq5(E)%Y>U$sU!usGa6JwtbkIx$r#~dIxvGEoarfnrz&% zMT;0{8R+&K`p2WASU>2%J$}vJ)~#E4(;vSwbMt1+nP0hlWikgqUGMIW4HZb)(%(1O z)5OpE3i7I3+DlHWNwHih8pxV<4vuR*yKz6h7b`cj#+F59jYU=fXYE?GEHZV|g0kh6 ze!m8o63e#{$a|F{{HQetyohlEiL`$lubL& z23cQ(Db@_ZeQAF#jF2#{ow~e%vfH-yA4K|6BWD{@dHdQjHL|bDKMJRg~_NftrJdpO~N<@ zvpr+_R+78TYcVCkOBYSJ^~OsV=T1Y^f2ZPK{w0jpJ!&g$J(+Jkf_f^V@KVMeOk9jG zk{HEbS7gcG#|+KjgLk8ZS-ITqxnP_$2eTjjDg+qqd>WkmY4AVa+{5{?S%rsVx;f7L z%{$1@UHyYhZF`F+!#GMHx+(k7?~%R@m&mK@pL$s6u_DE8VGczGUwa?zu61kE|c$y z8Riib)NTG}F38M3V5uTmGpDoUf9 z^JY>J-$ex&=KZKEw|PHmB*{DrwwP?X@!vGLEg)VMxeujFnK=RuKHjhfQ)Zb+=I7y@ zDYJbD*8B&d#FSGQ?D7AN;YBQp-#?5Zrp#gHseTjErp&7aO#m851*Wh{e3zlTZ9X3( z{QY3&^Djg&G=|ByFZH)jxZD3Rr1hn}2X`~l^7h}@3iYRya0S8ZrVy5X{2qRfVZIA` zOb@_8H*;WFF7wwYgkkcfIk$Nn;YsEl&_lAh4m!cBB7Si5XK=kBZN#5t!fO0{d&D2K zWZ?#f{#*vT{a-}Q_^0NO#6QAh(`W+$zn9H3zx7C-CFzibb<^!Lzxmj8U@?jNLk zyZ?Ul1^(@H@9=M;-geMEqy@;1D$FQ_Lq`>IDa)4wQz%aLP*+OiRBscQAr=0mro4&j z4=Q!0`bd)NzXI8&`X55lY5s>GW@VsdLi8&|AUO2MYrF7FLFzt&6*5kei`|spF%fZ70Kl3*y%;e z%U4CbZWhS62)U)teG2T-0)fJ7~z|?942mLeAMFyr_0e7)~J0uOvpk7ue zy#ykZ4X=va4uu8sJ_jOWE!d}JQBPj?Bh=O*)Rxbii~@Vze*DjxhJQizkBuLpY|Xy2 zVTWZIvuT3qcpRW*obo5I)BLQXj3U}(k$(qhGUm`-tQo&V&1cNb2Yu4V(577Dpw3MD z5&Y)kY6YSc?frug7dbRN#4{q_&_i6B(p({)DEX2RItLV*JM?FeKbsW#GW1!M9OCY~ z$IQ?akZ6I>k72&pH#cobR^^{5S}&fVWG(1KoaQeBW!6Hv-Ad%FMU)<0=O2(u)?$ic z+=1e~*zk}1G`}I0_a%ekxntm4CS`HpxX?nW8iA-~LjSgyhrp|Zy z!8HZ7rO$6=`VUZGx89=Ct-8+9G=B zJ|xf6L$?9)_0T7R*bq(+y@I0Sjk%-+;HIiX{{_Vly>F?LTbb#BkUIH15PA_g8-dVE z(4#vL@?&gD3WQR@FF6q6A)sa;^fw6L34}I+hc^)7j&CV}&@WILUqA~`WjsV7m)!}9 zPIYD}J5cYtZzCgbUWWjJ21l1Sm+@~%#Tno$4mnjC*!ue2%zCwQzF_G|{FnfKK{w6$y z;X`CyrKdykqMZI=R8@`60|1J{yHR?*PG1U7S(pu2k1EdsZAufwrOZK=4h3Ubrtp{q z{_H?EN{ccxiu5dric*7DkQRjKZbs9sN+9e$Zl_^MtTg0cje1`9K|A{4ShRAfuEy(r z458}HQ?L75HYLkyF)Ah_azp0N+R-e1JQ^|YG3MJKqAFAj0(oA-6sD(2?7+Ejd)?ci zhork`#8)9cSQ3EK{3M*3nMVbiOYq-iw&K5GUJPq+n-3vjlF2=?lTDrwY??e**kg9! zzt`j*{we0u5W{Cah*W;_*D#1wlV?YynSAn*Ze}ASU~*U94D&Z=GMVN*u!b!22k0+? z=2aLJL*^jz$~NmjlViRI%gr^Hf+lRDfooIDPPDhFCcoX7X8sL4lag;k%ROJ4dIpjO zQ+1x0ki8UDpBv2Ad8|Qr8Y(qdsCTkx;g?`l!CA`V4)=pgaJKSz!ram=c#87)!?!`U zV3G0!!e4<^1?MPcL2cR`+W#fs6-?KEPo4cITZ&mh(=(StNSC=9nP66o|8BDz|C7vF zkRjPz40%oS4Ul_GJ|Xa$>rk>3vl9CAnR_6O-~1h7Q_aUP?@ZIOS0bkaAqW5>_ftFr16F7F?h_x#6!; zMGN(_fh$>p7Vj|%$(6d;13d6sI{J|wknxgOu*iUe&iWvy+WiZW<6*E=G`#Z3^N@Slxgzd>nt+@chGzuDhru+prW(Q=TU?l z(*U1b_3CHX%!kQLF~0}_rzF)&x6*n*U+Q5K{9=xBpp` zD|9+L36K8>2t%9b_NR}Gk7=PZ`amD>^APONM~V@i>*ow9bS7ycejT}oy6GDY(}4d^6i&H=;WtFi`z|fi9Q(>O{5SpWZ6fw`vc#m?=)h~c&nm%KYH8oEsw}?LDYv_zZ#rgrn73wp- z#Y{_7T#-KG7YGbl?Ff5>X3vCac|ybbc@R4sgiVA#s(%M0TKGkJMs;pTkNd)XRKeQI zK$X10iwIq%zfI%RlBS@x)bJfT=l38D{RFmvS=7%sGykJV=vn^7!t6c>r3@Fjv5O zGtJ*Zk}UI5xP#_r(anU+t07{x*^9I}<`CSuW+!q9hgzT+On8#gC>@)2=;+gwc?x9H z%;yoSo9Z|Ovj-9y=3~gkZSsJVB=Zq)NH%X~o7DpB5?)41)h$5UMkwLl&~Ir0>4SY* z=y$AE&9cSG zulsrgtJwoBDagqO0)GH^_EoU@!q6_g6GE$bQE0b51h*Fc0_|n5eic0K@TCwd^fBe} zgfrQ6&(nEKfIqw)H5@u$=UD*OEGu+@{wzGf@MX|)=tBK9cyhyCY#bLW&$RGgP_v;+ zl_z3NdYENU2~R!P6Br7m=0umqqUnO{?4G^uF+2Xtv3R-=!)^=z?f3^{@l-it?qCd6 zq(o)L2z=~pV@NXfX znWP!I30jGm5qk5obP2v9bZm&Fd`33Al8 zA~OFl8m{XJsxzO~<$8kEQSfn?i0erb6jXxL^{X9l76y^YC*UBDSp~Pl<$C&3{HMB^ zwb#wI5NHBRk}278w3Jb}z3!{RNtia|tIUGR`Dcm{Wa0Ap8iOlD zLG#%z%cpgi|_{SGTBE z${P5KnY2W2GybZ#mFVjL>q_)?Ks#ReP)EgngC*wGho64^XVHr1s~Z0XYrGeMWh2|6 z(QjOe>bnNs^4D4OhnehQdR5&Vp&^jCd@il!TU1ImTos_zSnd(`TROY@G#v}YytK@uxz zR3+UVOWLTC9#Tn{vupu)9Dym(v^gX*~Q`%aY==id8ZUpn~1; zb$M8bLt+-nb6l%{g=t?x*kuwnj+k|N8ES;lmDI~O5l!;(Tor}JdKwzLeyL?aKR`mu z?k=@VVBt+1yE|iLMaiM2r3kdj+Np}dgp`Sh{7rSFEQ#xKeer7eZ?;JpxS|sw_aj89^BClL1_4S&{9l)Z zo`>FceUYV6QcM!Nlw{gW9uJ^jqpH!Ef&yCFy~fh+N_LLCZpBZ(J|A81VlKJyow&tw z;cy|d6${}gg=19}sZU*l|CSI{74UcB_e(HV^jyqV9ERXnW=B_(!wRO}hhPVT8Y-(1 z40yTT`Yze9UGN(-t+EdPXiYJ5!FJ`!lR~a0?qzXm;dmF0+Ex4ZcKj`i_ z=uY~p@uq853Av3Uw!R4IUFSE!$;)bE5eytwzjHntb`2VB8M{Y|?reDHvxjt@&nC}z z@2r036YO_LT*EFILx&c@&-JM{DB{Hk`aR+n?}dZ=t1ljcBMmjKMM+L;Vl#DJp-A%( zp-30fLDDrO)s|k3Ja}D?pML#HOtTkrf_Wy%z77YE6kdEM9N(hjemEY7W7VT@Xw^^S zfA#P2@6`XsKTEOI@4{Kd>`%w9kPOM^bvyYKv`NK~U+Ss6Ho&pWPOyTU&qm0pWbQi9 zr}(cR)6Xz8o>4^+__O_Aq1RUL8@ta)@v1iW>zTRAA}+O zl2!Qx{MHcVRzuX^=vQYMCMiw}ym)@5%);`!4sux({FeJ4ywzVzcR5ZcZQb2 z(v_}3@8~+zgm_*x`03Z*5wW|qL$vimbT`&?>JX<>crir!v1@6Tj8*1*9hq!~uQDGF zibavoyHlj`LJ{D8@KypOYfXn?``L4!K!{bI6lScZ*)Q8vUaje8V3|Gd-8j|Lq`C%z zrK4DiSGv}8YYaY&Rd^k8@ zGscNFSE8%P32i_~fN$e}9FA6PZ53FU`^MmDdk!7A>lU3WJr=ih-^YZHzubZLH()IM zt@|>@0sgWcU3V2!!(Y}T>(bGV`OE#tx;K!3zwnfG&%uoNoAT3jvp~mR*3UJ^m(E=| z8F~P;ALeUkxBARE{snV#5x-!DB&Kp;rmwzr#%+ZC$#aVJX|pf!18IN!+hu;WoSOJu zbei)!==8+zpab@I&>8l3(3x>w@$aCs?C+ql;Ud3-4k`K2i)P0JN#bYHIl?`uDT{*S zk}1?CEG2XbnOR?7PnGY$5rf4shdLCu>56%h{R#IB2ODHPvlQAxToRc@)Q93&3$=pC zE>Gfp@%Vg+S6`18#J}Z6OQEMJYe9G##-&+47}M-h{nW7a*?FA7DIk-t$T2wuH+q!t zbDYv-=$Xk?x!2UFPsg!;fGre{51JQGMQ`!tpU`1FpRM7P^OK*@aeH~72JODoDSZMz zyr^S|)s#gBc?B)wvh<&`q+c{$FVABB8+;p8 zr7TX;%2k>rq6=00rKLg^EA}!Uij|25yWGhE)6xZ!eq}s~g9L^<0CeahK| z|FH|bGzixbtTdejOb8fCu+ntGr7oyQ2-`SDm*Iyj35%3-J)zMGT@Bb`ISJ(C(l}Pd zFZ9C*E4z_s<#bv+l2M91!L5E1mG*ytfVG7p5?Eqr)h_tSb%&X-}R zWJa)w~ zu}lzzuwOaX6K=&XQ2+$sjTX|bBUrSZPTIJhSkoVtFGadY@r~&x9<&}2>RN&SRrnd& zcC|zPuA28 z@QpSDJKpAE$0KR%A}ul^U{V8TC&97~zQZVuF{TKFNK@t8bCRZuX^QYeO%TeIGoeec z3e-*@F$>gwBVC1xww=IaRsBz^Xp?hLHdL26&&XZ$1&r$VhJ+cpm8+4woMb|$=iDyW076+fSLpzkxf z=YuKUzZc%ffU~T40y!ka!ymgG+UGa1uuKAP?N8QzDN?bTiPR88A~Yyx`v$7KO^J~{7@`Ph-Ec8D*$}M%Ax->5EbHQXa~-? zk&Zbk)j9$dqQdF0G}A_)W>h#GyYK@)0=2EeZM7TPd1u7h8*{>K7KwuZc2aN>rs0PX zIi@Ulox{YR;6+^$c*lNnU1lT3?z!kDm2L6x#x6?0>LaqBK=y=x$$y!oBd)U25vr7P zBcVn)+Xxo7jc}=pX=qKl)?o!;swY7dg;7?D)9q+VM-r#`(0?<2c9nk#ZYHz+aH@-G z$7d0S9RFii9$W|_SOtr*BGU47IO( zIG8(8bY(|fq@w6^+d(LRb@d6_F*cS-_zb}+!CM7sKD=x?7LOMjJujL9FP6i;8sS)t zAC}8(q<#AFqmmi#y#8;L<8N4=aB1A3`=p)061JV1bYBe| zzlHBsV*5<}^G!I^*FM-RYzRkoIXASLlry)^iShk6Hs_iDZ*NidVK(IYKPphd?rOG9 z{=c?oT0)Kf8(Wkmq>o#Ky+fSrZ8khMf8(y5tsNi2fb1<_gu{nOf&ISJ7GyCM6%HDB?W4k!?uqW*; zhpr^Pw;Z~(UK}C0V|W`lBV~MZIrKZR&E?Q3+MCPSPT5>edAY9l|Nj!6XUAtH%yMwQ3_JOD;Jd6P^_8ts@l=A8t-TEt(i$&z+!~ z0qjl;(baxV*6wx45IaMm6G3A1@#rD(C7|ubM~{{hODIq(2GF-%dN!W2>bl!y2Yo}= zi;DA&J&{HEXKY-ax2e3mVE(D6<;`D}=h71iU3#v|l@Rn3mu~p<34sR;JyN)8M*gaj z{7vO9cS3?ETzX^{Ryr#pZkN%zV$G@vY4;g=zA>~SZ=li$Zf!0uz?M5sS)b7LNaaA8 z=5p)FtCMdon3tDtWUMwuB4GI)ojf1@INe8;`*G#|hI0S<jm$$~)RjLwfO{=-456oXP-xW-d;1e!=MgENZ8AYd;tjs&rIA`nZ!FdI1NMADlMldw?j?cGJ zxJ{1jlEkJR#fb2&Qp3)U*b0H?Camt z!hP^|cJvI5?C5Xjc8S$pbxp0^wXJpawcXLy_Ll0-?uNxUWE2e=`&n(n2gGW_Hre!# z`($A^t*zJ*w0qe4qL@KygFD&B&DzsT?YB2li=MT&W2C=#aO+@i9gp;$B)hJ)Yu2`& zwpOdH+tA%s-QI!S^IBRu8aq4Nw1fj~x%VA**;8crhPbw+4jZC%b~o15wh7zz>eIUG zsynN@>pD6o232>qSJ!GCo$dA2Eh=qob2|%MTi?;y+NRn2fuY#ece?BXELZ*6ICr$( zPFladv%3!BHn&#eb71UQ3A#3X2fbx`?bdCR74Ni`>h9*&j*iLVTH5Lxx~r>eyPIpZ zR_uEjt!`@WYOmLh`qt zEzV5@W5wi$#Vm~a@|(jz{lwhrIwvheI|`t7sO#o?Th{iNY7;|#Cf(bQttw-~Ogp~P zRti&tPV2~6N8;Xw$ThBa_T1RTF+uqDmi0B-&Zye861{FSPLfUB=M9_J(q@&dV{cJ> zC^w14wKTUmrAqkR{liXg=R;Moa+UTbm?i6=v!lDNtG&9jsdX(jI>l~|&doV-JT)5E zmVVVFoCe+12K%j-W*}SfP1x}?(I~j1V@G3Cw3D`o{q?Y)BJ_g!x}|~m?x{3WrFE-n z9gVH714hiY=V(+N^|i3WZfwUFMRTle?ONOEw4rD-s$6X#Dm#1H`-AeMc9;dCXw#CW6#*(O|zOJddyQRLfnyq?5b6DS0SKn%zdTnF% z+J<^g=v*>g-m*+Td9C?!i8X;aA&-8KZHa zO`DquzLRDX)wqfjU zr_#Dxn$}L*NZQIL53ZOzxYFKx)Uv;B>umc(Yn_-a%hx&F$+PFT4vvnEx8d`Qk?I{= zb`4dJj@RI;#n{f0z1)s>lBTRI(c0XR|Htia+8!O+>zmOA*VlJPo0{vpJDN_fKWPJ2 z#}=lOWYF5y1T9|hG~8!ftr~r4OaNNH zwV|?OhgXM*x6=~TsIIm^ZXez?&W*Vz2+LmGxjUwVK}%)V(gg824##_<=IRcNLG|lc z632jBzoD}mQw@xB(k&{Eq5zJ@s`uI%#oU5hmS}3y65DMALn0K1n>UU9l*}2pABVPk zv}c6dDeoNLs#z7{+-h_bd(L8-GdOk{+nCxsRc!$3C{DC_{e*p%hw4X1cMfZ6U$~to zO=6lc%vnZm7cXH_5T6}zvylIPU)%UTxAA>#RVz^229HhFs#_Zx;u{V-Y6|UwZR4~W zyLC*`U^(By%x7&~b-Rpf)||4oI%-!JI|q)@O>N4UTGwb>IQ!wSY6&LYuG_`)pRy> zHrHdojQ4^nBiS*0k^)!PR!+)G4Uq9&eq~?pmhOaoee10b+8P$DCdcl>(QK)x*i6RR zWT_rD^^W?67Va>NuDC&_EEBdI{x=ps(Drb*qSdSNs=E{G=0t7y&XK{PcutHRMsgYs z{+UEgoMP;(!8XU+apb|aomwwuN$40(-W#5C8E#j^8=Z;eom3HtcM__jidVih<&EFk zRJUPX{SP*0O1Rw_j9)^Pxi#>F;N zTix7Dah-emb3F-G&XFeZbOPMc>_mrZ^+1Vpi1vDjQ;R-C*$It7-Mvp%t4iawZR`N8 zZXw}_14jYtsSr>(07=Ud<&wX4Ot>ktDb zg<})=NmPk@I(sL&el-(|;(U~zac!`#^6ZMno<24G?Xw!7>@F`0Q|flin(M{%8dou9 z;`T(7a!;HSS`$pImRrkrkM`{D=51wMcl}x{%xR75o1~tcXMIf6SA5YaesV|rzS$as zt-E*UzW!D_o#b!{jwWc7==isAa8p|EKCLEp?gL9Fd)mvP7%(&IrorlqvA|*n)N)K5 z9_@%7t72Qkq6L!;shnzY+({^*HO|CrUUvU?bPKeZ*lihRYV9x&JneBvzqUr*om8u% zHsbwXLR*={R63g4+MR~mvSvL`BjBM4XdyVWLFM-}2&`RoCquP)+jgLC82NR)Dh+H9U7U;f}u^-Y$q|R+E&~Q zC7grd&>#2Cbn3Is9<;4BUv&DTJZM8H*{U9#ii8nw5DyzPRc{@#G>L6R)d?m$S&?o1 zqt^3?e>LL5T;euwt8px#2R!|7yCE0!qU*(T7#w=<3G^? zF{0y1gX3hINt9Pz7hA@ZO15w76!B!DHX}uMwNt6JYAG=$ht)dOVv|~pum(#t4RTH| zG3HJpkm8PI%J|&}8oFU`O~Yg* z$Fgp%8bjEmdEyb)ht5`=o5dSLI_~`}WsYfm_ByRYHTF%rrPf&A#+&W!)=DF72Le`B zC}ZP2sTxNht~G~(gIcwEQ|o3tcJ|IhleGJ!4R}hWmZ*7??l=?7Q9bJE9D9*2J}IlM zUR#TE%rJsmZA;ArSWu<(31OJ^VhS*ZxmQ0Q%S5}@*7Es8cXw?0D>fCJP;44@?7mpd zIMlO*CbaAhXYFuokI!$$`*-Y^?1-SaTI^?k_VHdjDY_Z~93|#Tr1Ru5bw9#7-~nmq z`eR4N*;>S6%vuP7)>qrQb}e@9)wam92M5=ty2|wxw(OJNNZRIy!Lg~8lcmXL*nIM$ zhK_%{#Ey~EVZ)e`(~7P8G&$l)-Go|4Z6)4VPrT_!jJ4{?=50L%vGfCR<(M^xT4MM0 zYTl*oZ0Xs9CfClADb+~7rNnbS0>X1T)pd989!1|{9h+w@@FyM~)i*fSi?x9*Sf*8{ z`#Bm^L*l)^eTR>hC5o`c#vE4N%s85WIwvev3@$cJ)QY2G*+ivnV%qAEz?vn;*f^f! zTC-49hq6qTc>LY};j^FAyMrI#!lzm1rr}FFzDsCde1FjSPGKMX)oHmGCSR0vvHKF^ zQrD;S1bV(7=}b?*{VpV&>V3%Sw7`Y#i;RnzmT%nJ7vH6B$RulJDv?yiDWd&G$2F9P&8Jdc8{Lw^UDuU<(;u)6pz zXH8n}MadT@UE;pfxD5R3)5=`;r6GeNn+V8vJ!{emFY;dOxx~CQ`Ld+T-JdkB0CjcR z64yvtp;I2d4+{NSd3LAys}~{Qf5X9C5FvK`?>Lu>@1n*pw|uz67k~NgCu==^8Thy> zkv>UY*0n!+;x}9Ii|2YJ(wka2qFDxVb|Kz*8F-B=k=~<4K@g`$r8_R36P8Ht)iw!z z8PJ9J?ZDz#8v8AHD}d*SC8qajI}yeFXu~eP;^nvuywR0NpNc&bE&3{;3-Qcv34Yea z&tT%0ffHF0)2GW9Le{55F2pna{#X+1`sBC_yxyLeK0~_=@syXg=Tfh~#}gP@<^=jo z?LMI|Q}mf}Vz>;{ej@)YCC|PX6~E_-Uj|N8OQa8JzeF_UVViOxp5-~j0Ck0IheAWk znUFq5dk!%qXIpb2p6UNfQS%?ZRCHX17M_qkto;S?Owab`LcH@baAJ}nvQS^TIzcH; zVW5X(e(}rMi%>r^u#27*%W~_=O5)UpHWORTT7HZF%y=YRh89uV_An5y5+Z$s&e&z3 zHPuLZwhUdhu2?kE=W8tz&z7LW@4O7H!082j;e_%OYTc5)P^IVp_+?6Z|~$&c2xc-{EHv$EZxSzmUt0 zwaUq(4eX2bOU2Ln&)|rTIlS|QqC&@da650K9W$AU{`h^IgD5`Xas;Y-_`(Ec)*YvV z#+oNN0>CN0SQfk0;BrbSmeI$?M@EN7N)x79vAZWZE5yM}R$&|=v2_IJElKH;%7qoq zML{fSCdMx)FPrc%IR4f|B4y>GMe(Nud{ZJZdO^iPXGU0kltmX=LOS`eTtbMwgs1MR zWYxv_PQpn`3!JOQZRDpsY!DcdOY)_l~~7vxkQJ;w4khKh*p!1)WX=c10`` zK1!mErr=Yfh~N^S3*YIiAmSgtZ6P0~Pr+ba zCI0m)$2yi*ot265 z-b6p-xs?cc)R~#cpZ$uf3L9J!;pc$^M5V{g@+}|o`IZL$`PgEIhQH2i*jtK4+ zJXi2?!To~Q2;Lxgi{N3wqk{JdJ}CGr!RG{D5PVsX^9suQrl8U@{Mf8jg{KLI1q%hs z1zQDA7wi`t5!@$ui6F-*=6AKAvMcy+6#wmlM+NT{d|2>F!QTn~x8OU1ewW3+K#=1M z^D7f<5agD44DS%!DA+ByRZwlH2)YZ!f4ShN1wSjub8N`(3xeG1m;SE{ep~P!!G{HZ zCHR8iUj*M1R6BQq&rHMO$AgT>cd_7V!EJ)vg^uxKf_nvdTnNK27Q906fZ!p)uL^!w z@P5Ic3;tU0MZv!cejw;WLu5WVf(3%}1seoA1iJ;d3+@)YQ1H`&*9m@EkVhUdpL+x! z7W|dq3xaQ3R`7Jet%BzZULkmm;4OmR6#Ty6qk``UCZR)N zz8Qkk1-Y9i!)pY)1bYN|QUc@m3SKFAjUYEDX8bn=zc0u=_8I;Q!Q+BFFM{EJ6D+{k zK>u>V2Ek6jZb2R~M7mvq9~1n9;N^n*1+NjjLGX)$UlY7T@Oy$!3jRg#UBM(w2$^p{ zuwIZ`1u*=hf)@y0CirPVZgNe!&kNov$W84T{w=|~1n(7mQ1DT~UkW}e_=4b{1YZ|a z8`y#`k10}e3+4#U5S%4gELbVHOt4z8QLt5Thu~hpO9T%J-X!>S!S4w^DENfn?*#uW z_?}=0VLvXs_T*1YHb%NX$h3U=|+%C9R z@RNf31wSWvz2MD)w+Vhj@Y{ml6TDaOCxSl{d|dD;!QTk}LGX3K4+OD{s^rDaU_@Lk;g1-=aTJX5wD}qM4C1<+eRKZHYQw7%uo+j8U_%Xpt1wSKrqu|#B z?-YDM@R;DUf-ecaDag4s^_MOEBZ40nyj<{ef=2{@B=}Rop9?-I zsP-Xj!#0}Ye^c;1K^;46D7zC(732{_3=a#=6r3eEPq18YvEWKUo`KEuO@gh0U4okg zdjz)$4hfD4?iD;=@M6I$1P=)Qhu~KPzbU8=SAZPf7ypk09~68<@E3xA64bF^M)^|( z!-BH}%LP{oHVJkKJ}&rM!T%C`M=%-pZseaO7!fQHTqf8c_`YBYZs3?MTQFa+RB)MK zRFK;tk^W4+BIw>BB0mH7&MwS>GKug{BO={A!7>S7CjL{!-z5IE;@=?tj}XD{qkKZl4mFkAfP62D0N z)#8tef1UW(6OryL!OI12B!cf@!Mg>2N<`iLO8n0gA;*j2S3C2=|DMpLW8s1EAtKVv z5nM(D|2`sUcN0N(uJ|t(|JCBZS^QrX|F^||m-vqnS7CEp@&B2)0_(a&7H`Izet>=vszdv5BDDF5%;XdnKG_OfvoFh|91ZAmO(V*J7SdMEso+{-mHf zmKA)Bsfe%9aAc^3A>uNe2SJQrJ%)(%wSqi0k>MXDq8uM17GUFV@qdO0`db8VlW-mz z%kb}tpGP;*{}2)BA0AoZWp9%ho2tLnB{F{RB3;L!b9(oN5PA7tX77_eb5uv|U z!7~K666fH4QT*ovL5>zeI$Gh^QAHVnjLSN;ppyWc=yGRhqV0{OZ67gzu++1wf zJS_2l6nt0Eheu^fe}dDApr1R|gUDL8tYNO=X%m+;HQ ze~sYvM5KE_;*SwmVf|VBZxNyIKpqhJOd~d;9~OTFaS7Jl#ot7%#eP)c9~3-a@RNeq z5PPtGE&iJXzbyDw!NY>z7W}^8J%T?Gd`R#y!CwkKD|lS+MZs4DHC!Awu^gokLzqHL zmlc9c;Vi*o!7@RZoQg*kDQpp3C)i8GxVcSmr{JjIUcvJOFA}^=@EXDE1aB7nlHk_` zj|ko=_yfU*1%EF1q~J4xzZ3j};C~6eD)_eGdxF^iMU^L6Fk5hnV7}mNL7pYSdBIY_ zD#1Fz^@3`>8gxD4SL@dB^9&iz6Fw&R3BgMQ)p|AJ)pHQwEfW4^!MlhVns&F~1A;#l z{JG#S1bNU5>0cInRqzc#9z4VN6u}HZK2K%%G{J}<&w=5*pi+>hlheOSP_28zzfSy{ ziI^|+32ql07Q8_4V!*)x8TKvxm{$B7UK_0{60^R$9DO|^fKTU9o;0(dJ zf~A5r!~&d-BG@9hPH=f>#Re7raJLt=A(R&vjw?ZwMY0 zyi4$Y!3PB&6?|NfhrKX858os5%opO{1k=!$(w`~F6JO|`CCGzc=wB+x^Izz1737I8 zoX2bt928W$10sA}{Cfm>FbwHGC3vmi4T9ei{Epxc1@9AlT<}T3-v~Z0_`2X*g4ldY z<(DkTb7YwR48hrga|L<4jGC7T)(bWX^3)l|pCQ;QxJ{5}&@lcS!3zX07UXF(jOQ`S z#2W=~5&X8`or3oW-Y>|bVL0zm&m({@ieEjKfd6&zza_}CV@S`F%!xt4T)~-w>Ny7D z=Ze2nkjKj~UA177V2hx7&Vl$1;^+A@q~9*6o^!x|zW6^acu?>b!7mGbSMd9S4+=gk z$P;bI{{_KU1l4m7guf+z_51^V9b-A^)pHOakG-KkS1?a-mSC}9ncz~vm4ZCnmgyS> z*9mqBo*}qda8Phakf-J_{U-!37raXFGlGW%c_<9$kzW(ML-4zTKNP%A@Cm`E1b;91 zk{}O}AwTYEN8|>;M86=9`K4byZvm?3Ex>#U=b6{{po@| zf;=>i;XI{=xJQuZ)X;y0pn5I?|25+0xiyTxT~Ixrf&Yi%e@O6AK^|;F`acS)=QZ%Z zF8+4}|1Rj}a~s5a1TzHHa~*{9KpM`!iv)SnIsKJ_Jimtis9=lWI>F6?eS$j#M+DJq zHP>p)--+nMFcnbqw+IpQt0E%iO=>=i`O6aeF(0WSg1C{0@wAN?LAxZPzuruot!V?q zIXHZfi1O?qLS9}>hhae>%tWDIP|1UMEZ3><2oZb>1(jUztNAui$%TAYFdkSXSSP6D zL-{)C2P!#{=^69`Hxp6bKEVMZ%DqES$&d1n(+^bTfF91JAGnX0EARUNFQXswUn#hs z7?JmVfQRUZ+}8`6}*jza(qqjFcJFuw&0ya=4>I%m43+e91-&Pq<_1Li2Pq9B41T6?P4$BSx7nH zFCrrUI`KCWA&1gC;y2I_dHN)NfC#zviT@%ZMPR2vtr-c4lVnp6Q zLi`E(A%B4LarlEol%q`i3y9F`2J!Qtd6w%U@n1$n`Lxopy+hav3^+b&;ehQL$2P25 zX&*nSF5NOVrj;_FH1=kpdcCo9w10;c?s!$uic!>#(D>_*Cnbo#1UV@`^*ZFFG1gm= zlSWzZM@|}*@TTO1Ql0d^ozY z#FrxZ_N7Xv;(F|Trqnxzb_&NQ+ju+E4o&34w>0gTgspoVf%a}bc2FV*zP)M3z<|n2 z%O?|zuXWl~iRt-zryb*v7_WI+=E2^E_ z`k5b0)Jk)5$PVtQC9x1&!>h7GL)b~unM!Sgq?J~;x0J>{O&>$k`LGw3Ry8y?4PA}8 z76{saTS|51;XmmN%d1VKV;rwj@I%-0FX=dDOw8Ao0a2QrFZ(bj9hRq6zeO5eHeD6m zUi^r>?%Zzmi%a1~*Qmxg%5^$^PJTB?96((*zg_S->28MKZIL9l=AkMa9YxSjmiH&@|j zU$%TdMu3wa2i*w5$S)N?TfX1J?c}!vo?8)SUpBwv2ypU?fL|5D$dBuGHb1UaIr%*c zPcGu^%Pue1tekXQ>v8JYraN^4T_GYke%Y5T-*V77<=YJTHh_ro;c;n9KCX4)FV;Tp z1;0BPAeYVWBNO=D34WJ>hWs#;#`uj*;I{`1+>`L*$$?J!s)nrj9r@w$Ta4eQC-6HL z#$JPX`?B?N#RPu)!H@PveqsFV`uK|Qv(xcj+*u!pR@Z|N6Y@QT zd~G|$6e5=IuP5Yt9Q;UUUv|E)O`v=AAIRrJ0hq=v2M5N!RO0XnB0+y?zMBA)d| zdeTvju?g~B34WOfw=Y|St0vI#UBk)r%y)6^H01FO(7m2#bBo8_GC{sqA>Rf(-==&; z_}TK^F@fJrb2RNEb|FD$=k?`Ob6X`@{r(=Pt3@ zB}mj|^E)r}e49vUNH<;R z?5J}=$8~)Bva2u*I%jz|A3%M8i1IDO&z5g4=KUbzr3t$@#4jiNae!C3#{4I$Gax)uK(Dq z=WW_Nb#un%(+6rcZ`f>X4s5=5K(DxVvlbb-Tf0Yb$3Nd{&(W-bjngtVUwD^ybxLDK z9(j3JdmFQq%L7+_-rX;Gst|sU=WaXQWLWa@tF4zG_Iyn%s5=o@sU66Vb~Wu&vbnD- zxGO)}-c;rAwTbVk6I$7CPpmkwX`m{4sKFRWI#@g4K9qdW+#k71oBi|&t!iO(*KtZ! z*YpXMzUYWMsuj3T=*v%w>c=~yX7tp&h>A&y299V2Nr;I=OO^age{$dpGxdY6gXKqxED7(@ue+n>ua6ZD{OCokuuX+&#n&9WWgzLcO#_D- z`ic%bmVP|$^B%jTX30IXqJ;xX?n#EUuRT_Oq&RxZfc76#l~xKqW{haZi=y7ccfa&1 zdIxLX!9KHCDx&1&w`)_b_kDg}^z!J;Xm(VaTYoHI7mN3xHUDL<@d(aPgZ zQL8Q@o9*y|0d3x^$9{PvGEh74*_V>FsgN^yM&Oz3HB>9#a&XA9}GYswgfL(N+J$ zw$E!EKbFOJLr(NiH1#^<6RnKaM3+XBl=SWyY0u;+iDo~w9TJ6_vR7wAqOT~4LTkdu z{nzOSQ;=?1v=$pVlnpRf_l)#sQdBCRO66(ttoAjwzEM2Ja+sEE|J4ik zw%BeSx59mD->t6ELD?&$w5PYXe{5_JyXrD<7k?MW$|AP`13Xd?=<^Sq_P8#jy{?k=~)cm9zS$ZYmo}k@FEE))uajv0W;olz+%^ z82cwhEIEZ4gejUeHpk-jj|5T4M$|?(lO6o1swaK^raDNe8Ycz7v>E9c8qqZxch5#)QHQS#El`1R)i%z#i7>iHg8D> zc#?jUN^n1i!4?OC%*0gMI0u7d^7J!Frh7Pfb-R*y+_>9iJ|70Slb#2KaU)VDS6^&# z`!=#PcHu4|dE+XEc+5vJ^+Y!7;Y)HSyT7j3CBF-H$?osLV=~0Rq0*X}+d*eGc$;PC z(D@%z;9PPgohi6O@Px_JI13y-Q?8-&&&bI$Z5Ew#FtmH7vrtBXAI^LVZae^aJ;f@E zBhZMagtTtAdl5=)@HA7;0^TNiYU%W5_QCDlj)f3!aAOdZ-ka$T(e2iZqY%flkhf!= zuhHqv{S4_}0AueIi~boVnrhLfY($}SiV>W{dmmpGq;>iDNY3zW%mGW^br3Jf_gma$ zCTj-Y$x8_+S+Br!Bqie^BsD&b)!CFx<>W_EDLKkH1*J`yqMVf#f_`GylrSPTD?S|HQj*zh%q&^>=ekG#c(+Rh4A(&}CzJ;f2#v_dNEsxWrev&kG zAklmepcZuBQB|azDiW4%U43{@g1i9 z`Cf+ICHY=Xfg*gj;=k$R8`~aO1C-*Q$7pN3th74r+o6=~RvOiL@JJXPK?=2gy_Nu9Tb^b4r_ zM(+a3xri!g_I?j~O}kCM;nH1q*)KI%N}Ou7fXK4>wmhwihy>7*H?d%O3%6u7t3 zy~BGs%f5r|AuXNksKUSvaOkMQr%_(dLts;!?xC)f$m!mlV5WJu;$M17Itm?B>P+{M zB-gtd^y&V;C^e5l%=C1sGorFe&tz6bU|j>+WH&i^8c?pHfRBY&83g=R24|uL1X3Rb zqo9}X2?WyU&h_2{-39_=I!!SRD5iyA$FC-hH>u*G2f;ZtHzRK|m4@AWwTyf^wKSY4 zp=A`%?N&WhMj_oE?=y(Zm_@hW`vLxE%w|mnGOj^B8K=j{V3&1D2u`Hm4`H&38sJIUgSO+^fxuAOqwxFqjhW_KjJ!SwK99UKJ@^t# z`wcy~gFaXARH)@8BX}VwG>8ekMapEzU0H z8xG!`_?KNqX&}OXf;79FS*CN4cpnaB!9L9xfi39yUNrpB8E|@ZzrG5dY#M}~ru%ry zo6U`#^>lSt>dxi_O3&2aMhtq{%dsx0mo_1Z=NvdA`l&oZ*!xpxK(GEHb5BzVYRJj$ zjWE2H;U4b+_ULsFfZXqW2Aa|9k0U(b{VAG+9-ReuE-Hc%^S=V3&?|7d^%k8evWM}U zMQ_zhs6j1dA6ap{)GL^eZv@)Vd?lzC-PeWxF5epv&hRZqUAlcuP;Qd%I*y$_KmMD( z42b1HO>!ciay=8Umh_vi2D9K}Flb#5?gouZ51tPeh90a1Kerxy06I(3gIP$ItOtLH zx-j)1Usdtw!2t;6)q^ES9?*k)9{^yYDkFOETVS832X6qEd_8zGOr$^$w!r=hbuDEj zxLE@Hf?5tPVQB(E?$o0Pf)yyTD-h)S{zf2poP%H>7{s`k6bPE&mmCP@K!au=$WQ(} zfnYl;BoO2a1}TByCJeH^fR;`@B_k+@U7419J2f~>&qPRwpMv@I>3Rb^*_iKY`b_;T zQiUH!Vm+d>{@h{CcJw?&Mct8EvYWM^?4}8Yz6#YY(TnuyNEH4#`Onph;nuUxx zZ-B=gZh|@MrTSKQJmFuWYVHj* zj%A9$wD2tGS6`t&fS5=)L~g6}7vL!jYtXJG`nX~vh1`5WqOsU2{{_H@uCrk|(MS7M*MP*S>S!yjO+1*7{ zNC-?BGjbGCcAI4DOpyF(Qz^&H$*>VN3-B3)Yb3JBZAKKE9zD@{J;|Y~L=>zQd@ZEv`Zr=d@C;85Uo|1iUfztH74|0!>Z)19W-6&a#?>Eqj&-b*_ z*>4e>>NBB_G%Yj}{P*V+>xWV3@DEYWoKn3KQCj$JC?;pVl!I^b<&;S|&H}TXaw!L@ zO3V3w*n1E7II3$8cxGo+c1MyWTec)yw%4|7%SF|4!MLf~vem5OLdjZ|WJ_2L6|hY= z%@7DBw3iqRqyPaDNPrL$0tAxq2qlyx5K>GaKprIY9>VwkpEuvNNMZh|H|($zY+m7{0T!9%L3R5|>cc*GMcD zA}5Qh@fk}*n%pdIkIOhjTspIkW;{#HYXIF@Kc=@s#ic*%D%6UMWkL*PwW3@ymYb|< zJB=~S#JiC)8NpT>nJtujg=A`wn}wxf`X@hxL`>o#(v$30kX?rTAxg@$pI|R;cOxGx z`zW~jZ3_~`*+;VXvVRGm33f63B-%&9Pr#ml3=i6~!OXU~6*0;FHsnmU3y{Yt_LC5E zjQwN8nrd@nS(?2U{>R$iLcxu*uZB6@{s37v-cCSYHo<1w&#)JQPo~X(QfAqY!RJJK zKLng)pNa%#+Y1oyWcvkhn_}0XdQ7#y4E`be4se@h9|=Cw?FV3mH`Sl2Pl) ztXCOgj(IX%emCWH%01WQ6fN7Faw&4sKTn3bkU1qC4a+~@yb^9X<`izX^XG|Mt~uow z5Z3RsBg|HyBtO9uV*KUUB<0r$I=kA zhE+)M>ds-1nJ^~$&o*yi5XtOwOtN+6JN|Pq4PlVr9gs$52>#tK!fSdC>H{W^@kju? zltCQKR9X=WW-7NJqx_~ZhHd6uc$Rj9XqrLa?f)aQD5b02@IS*`G@PRR6LZ?{6#cVK zuHVlL_%r<^1%3iHjyW}OE3@!Y}#|e>`RYI#1%m?D`=b%}18|1RopfOqQB;W-mcMbmQ)JijWtijrPcyH0 zp8a_L`DQh0SXK?Q=R)%q^2_=OOZdy;;Lj>y&A&*9`B|KO`!5!v*%(9LT)&cn3xKbz zkDZSI>As2k;1H-qv-f3l=bjO$K-cD*OzTAHR(w;~=#v6G6Q z5tlW`nt~E@e9sXt@-_~i$) z__-Y(VZNOHZ-k@oI3Ev}&v+eNVCL*PX8>12_%0B}GkH>&r_C7|BIajYyr111mOD{G zWA1=8-6emMjfCr9o>dAL^9Jdc;Ed59=fJx$*KDxfFdOEY>jCTMnvH-)!@j?6lyMWqB`YDb776%kw$V^B=*F?=+KPsrfXIB{d6n$P8qJ`AgX6k43_`B`BYt zGyH#d0Si4BJ}9F#FaH&gZS?RQ3_LKF{}c?IZDROf7W`zYq{XaY=7r%_G?mruGVxIZ z59U6edoM<81~V%Y1QKnQBFX&cH=g1Ue(1)@D&5MXZVU3&4fejo=}`xxBkp{nisK!`R)<>MQmWMneEFpUxvK!{lp2|-iKP_ zm>01%x#o|^|2~O)5kJS&ycjoY8oo)sB_ARwi!7wc_XiQqSds-1b0FXWDceO-TF*#n z8HdF9L_X%jZvvw$;btGR&tz9?@%+6s2Utj5n-_rX7#QFRVbb8+P4;otd{`UBd+_r&I8yxpb8txFPF!geGX?L# z<*`_oe4}s*BMdqIcf*%a(TpF8;6p8(FAd;uA6sV*C8;Qbf0U$=4OgeqJFcQz!T2#6 z-b%PQ8^*+tVbmdAESzItHy3s@VYk&|w*+?VCXZLU zo$$Thw+p_@)c5%w-}}_}w?_J|x>j9`c?)3jT`AKBnBu~h0?hT-vE!dJ6TTez>O+v(_ax_=Y9Z=zUXEHUodp?ui%pMB53}GB^MMGLhKD9# z&b#s=wAnHI?@up!DTx;S{>7NvXO8~@Y-GNC8Vu`=hHKGe(ofz6qVZP<;LE)>B0lH}tDLB8(dYFDMuSof2jlgv4>C zm15frV1f)JM?x=w@1S2?tH-(Sb}rnr;gaMx$;Y)%9tYATQotmUMD;i_!T&I-$B}vJ zG(=edo3v;www-Jz+4MGcyg56I-p0j=Wa$zWdc5%w5##7QLAl2{+%w1&#jDANEpk-( ztCxx7?1(T)m|~qG+mkY3J6U3hbJH-z!vZo)%|}MiZFW{jGAa}oNh3aWKt%FUf`_K195n5+|$>GeM+Ok1NT_Mj_48Xe4BsIW~S(=A~tlDYK&tsJJ;-qX!F; zbI#ghqFP*BhUzr;>^-tpNqWgW0)7ao>*cFBGNHXi8qZ-?%>$ER2az++}q>Ig)uOwF8DaHO)@*g zMhjmW^}{*Sp&3r?6SP=xpFwJn3bqq1 zfm$YtHGxU0a}&;p>ovJ(P;_gEk}I>8p~%-yLpRvywWYTvwE%m^TXViaLvM=Y0*bOF z#sbw`9`r+oX*3YfF=GmckJJ4$q2Hzh0d-X!V2C_U$o1Zk>m?+;o9J^rkK?z|>$lP4 zmm^Q)LC4342}{Meflz@rAoFq~K7IxTj67)IjE_KHOv3v85my)-4~+zeWAnir@m=IR zlzs_bh(I7m?Emo#;D^sFyqv|ro@690*B(J>&nFk}XkJ}=(}<)!Y844h4k&TTLg+;8 zFpW(Fe)2hGML+I?A&ogf;L459I6;sBEaQUPPjW+A#tFBdKNTH%p`ioylp>y(F=Tz=FE9EnRznB8=734cs4}i+2Ca)>TD1X(TS(Q z^~vbHURhWp?WjEmiLcpD!yLR2guu^iv8Q3NV^7e`s@d4m5V@tTqvBj@EjGLaB z{%D}X+(5KuC`W*e5(GaC^%cByCOXcC!z2%#&%sM)qT}2mZup^dCtf-ePsHmf=aI~r zV$+CWzDGk7zx2tW`BCkuV`NS%#!YlYi&ViY;QU>@bOy+CC4S$>YZ@B}&R6p>dryjZ zNC%$@vC(*12ZBVN2{0k0gbT&EiEt5Kr&ZE)C0^7M!Zl)SAlxFxO@s&W9^8nX2EU%* zu-@!ty?%d`_2w(#$6>ub%6hYxb!#{D#{aU2MY$#ICrx#3D! zI&n4Y(G{vwcBcE}BWD_N1 ze@A2p+PfiX?|n3!-xUPzeor{RC!F66$NZpwQ;w^MZ4G|e6nw^T#GA)X{P4%M^R$e; zu>LY$j}?95i<(vhrzy1%*w7EICQc1pPjLKh_WF%2_jGu|NJDV^ZuI(%Ew3c;+eC2u zZu0t#EdaVOqg@{(Gg{sK9`{I2A*h5y_(YF;BJJHr!zX#%DVIFi_$|hZ{35InV>6-7 zu_ZJ*wuDZ{mOyssNnEa;La2ZXzBTbgGo-4aq{61%;!M|q?ptw<3A&1C&iA* zeb&Hm4&Dp#vc|d3UKmERr~BuL{{hUNd+4>l^4P z=xG}+*g3FdaZ$nGKwCjuM|Kn?#O;zH! zb-1g$y#Ocj`@6b31`1jSdh&YvdOPwuTDNxPEex0B6&DsQI=Wb(a8Yq_K}}ch@Xkdg z1>jh)y{#=aM8U%RlKi4b3jRk0iV@uvp`fR=t&{o({tZMK+yzAm-rChV(ALTP<9p2F z9?8z068PECG0@x5T`&O2`%pFmpIRrh2DkzH1`9S1_trPXMmK6@?i&nm?;ift2t3sv z3Q8beYj-4zTiZK$UH!nnK_1Itq$cMaPA8jlm_I*Rj$&+d$x}*DX9yNfZ$&l+pzak&$GybC~gMh;9!8xaRh*KF5f~ zG#L@B8!dBuf#JgmoKXC9=x#Jkzt45sW0|2DlZzH)=S-efIAhwBLceA0J0WDXhNeuL z?)O=zTcH)cgqR?gS?0K@)2y_$haF}GmRZNVx-})_>ELc`(B@NXqu!fgec8JNehV!kl`HMCeT028l zAcPymyea;`Z{}}azM^*8@)d_oTTwRM@3$^LF$C$JGdC3`jxSseixBCp`7ljc2GiP* zwQJi#D|4+cHHOvIac2I?c{2`~QBWw^yEgtN%tV>dYvb=>uCxxLG^%^sJ9gHh8X3J! za%;TYW$oOGU0z>NSXzy)J{T*#x+q&9#Q~79D&%0Nedv2tzt!-M<+}z;30=%UG*SuGTH#Sz| zx4N#XUeC1EAqry%2i}x*lqyC>g#1M;B*+3$3Q1dr5mymSEonr2*|Rao z&6b#wZ{+>)m79=3VdQyfm_-p@Q(0P3*=TI*8^)mv=~KeE*u2N-A7uAZ z17$+Ee{x5-zpuNiZ5LQzma=`IwMXjqs2N++5befYrBVF@U47`uw7UeC?Hxm+sY)uU zbFKOMmdcjOaDCa@aP#Jd$`Pf(>(86kR97{Jd7-`3Nl4d*xmjCJKLy1xPe-J1co-e# z62uW{P#hLw5?RsYR34|w!nGNWXk9voBm*T{l`$)HW22 zZe2XO^#Ui+Qqrhq;kNF+!H$uoHzHalwYjgktEa=*)-^CV)G#z?^p|#T9quU|7%IbV zlHp!&9*k09q~fvd4!Zqcd2L0wsj@lTSXoorT)m+ZgF{VaxT*TEN@EA7H1O#lB$XoW z?`YlOH2u*MR^QNqoId!vSE=mVqyy+J?`-YGw0F=zjh8u;GKv&9MlY6DlQcj%Y%XQW z>t;D-8PTGus4Qz)ZMYm+qt}HS*qEDInuwKXk`P8a;YOCJ!4a^cvc|wJwQA}>>utop ziuvgfZL7^3j-Uc?8~)e<)vdom4qvpl^tETt)+yhBq};H5-h<;X%n+ z>56#*dz~gqw7c~lG+gf{vc89hg?P=Pk_J3e9kBogpZc>l(zs8!9d-EIDM+k)gbv zw*KMes6$X@nAcMRdZ=@_XDe_U?6>tFQ<68>pVwZrh&J7QtwVYEG+jQ}w{2)!e<*MJ z_I7x!A9P;KSC?<^9fnwW+j?;1zIA&?9*&M}AL?9Q2&>+{ypEl19n!ZV#J0YkuAw|p zJ(t(tCo^AII!z^yRSVR%;odfO#d+xbi44YG(o2IaKA^ls3lH*z(dU!x?os+^oi2X3)UChlarcnKro{JKFGSwBhOo zV=EOPwn71cTeNQb#)Pk?$+cv8m)DkKo>IRWEx)`zHr9sP28=w>ensl>*07AXqX=JG zzCcEEX(Z0DTU#4uHI9tCl}-zEx>Kh~u(6JizoJE3kHe&LLuFlalt*L)Vq0nYJRhM( zrARMxC1;XsaPq*O)6)=Sj&`h*>x~`T5g8`Mo&w!ix?!~P=TO{R#!dr^Fxz_#M^%F} zqDT5=4(%N4F|!-u+#8L*HMX{3lH!i0QpE1X+w$Fq&%J{ugs3|WjhIaj^lj~s5m&}O zgX$H%$r{nD#k)+DbneCS7%^F`#N3e+NKUMpAo1{^Hy=tjVuT;1Iy9E*+)*lfZDmDu zY1k>sk;7<1P3h*c((-kq4zrjkBektEJ#spAjF>&hG<1plR;aG1$B4Yqnc>QiEwVTT zDRVhbnK7oCYJ5;=AJ43n83F4OiDqXw?r9xr>vVI?Z93(pH8m8|sA#I(iMbea&6)jS zi6ly5w#S@bP|R6kU++-gKsRUTZWUwKE7QBk9Ivep!(dxv^6N$$(;2f_Hf$rS-L}5i9=-Af5yxZY?S`1+zZmOxQZ19Z4oOj44M?XJEnmPuKiEUV=O;sbV zc9%-;l*~X!PivQa#nnRZN-IZhDZmKTj3o6A4a811>(MF8SYkA_lr>j3V`}4;X6&R6 zwapnrN2$=9$;LLr;a+Kon1Hq$+rzCGFpfdRWMRj&bf?71TB^b$K0{);p-ULv#)HqE zPAJk^8!MX{>g$>+-S0OkL3SU=^g;RlZ^5>2r+4VZr*20(9NIANcIqN#2-~)G?Tl@= zm>QK=m)1DZdFyJEbWSoYLlwfur!(75rn(%TN-ed{R5&`UIn7DCC{9yG z$wi|>W+PQtZf_kjM3u~%)^_P;+l{_$+ptLHl(aL?ai@P$$j(f#8M9uURIv{oIUjPC zA&hb^-iHTTj}3G9j(kXC7WMH{diFIGI}*y%Zb~;)YtzG^78%f-;Q?8%6E4&c_P^{J z>bu*G;lYjpN5gJp)RK8@bOp!B0f&vK7?y6Ky>FOfw^MQWdD<9_QifNjbq#vHo(;^V z+Yu%bk$e~R4M@f*e|{vN_}C%%;Ke8|V7cG1k&K{av9n}keBzLeFJox|mA(8w z0f)$uc}hJ|>%xu5u#QqLW@2pvLlwS%o1E_>9W;#Ix{i+a3Q@0R>~Q)Oj4`pZ-k5Q+ zs=5)`z!u{)EcZLelZPxZZ{BfeEiWx!gZ#t7WK8|4MdOK<4Z43cE;emP$`K?79yDGr zYkp8y)>mOB*NuS!<1*BhcQg%ejp+fY!yJjn-)J=wIVEk(GsHJyK{Xui=^s5nd42sl z`Mzw|Wi>KyAMV$+K6K{;h)-4!=)yGz8NZ|)nslv;pTnFT^s-OYSLWmk+NpXnX&KQwaEbNfcsmP9+EK8|N5+PhUM@uo zDIZmyT2|Yz+F+a;ItKXJtYd&X;9$~KSBa*Bg^Nlo(8}D4ADU6gH&iy3)i+4kKs40oj41>A5zHd0yPek+C?Sk<29ezxwGvwVGf9I z#|@a@)KreTCWghqa_R6{67?<3NO;#VY)3j@88q65G3+q4BXbv@l&mQ-pLV)!2!NzH z6&1r5CaunJD@%Pc=`PieHu>&w6D9FC)MMtd8SSas`7G;;`Ji+EQR489FqZrqVTUOQ z7NL+QRV{VpT<8ghBWo~`+1H4)u!=^$`MjCPUIL$?<>-Lw8;qfjZY*I+ZI2lbcgTzn zBP=sc`czM)VJ(!D4xzI}4mq^Lvwe7`ClLnxCLj4B=hhI6J=OL6Qj9F7Nhb0mtJ#}!OlnzynlX0bqZu6zMd!!e zApRfliL0O(vSL3{oy>NtrnABwnIjK6-y0FxJ^$*F8Hv$b+qx6AUH*kO=yZz54xqUk zz4(^$4(jgzxSVC$o}RGu>|w6h8SPzzZTNcLimw@b1|8!aI!ZTU6xDRPQejptyF@q> z(uxTsq4jS5jPj|BM0-ky%PrbAFg^R&v^m|MGn1@sX|CK9ok<$2%gdLBa=EhCjMe$U z5ca@?u!`n%p}C=l4>$E>iplxm#-q}d z)YJT@TW9!i%$B>G+@}VHvXsPAVc!&d_}&zw%$9w{t~)m(m8ImI7C1fejD$1e_r{&& zKim2ue3z!I@qH%+2N0sqgTgEKw1m^+&xkwIzt=ho{(P0el!=3@g*j zGAEjo%qeo>+QXYhBq8$njn_Vk5HR3h?OqbJvqj6bw73tz^H|vJz`fql$B&clP8`g4 zpJ#Iy{aDx$5$iwBmCOnUY4HB{b6U0AvPdX1aP3CQ|lSR}CCd)`0 z;U7n$Jdb6JH5Mq%{53&)9}71%#Kt$ys8kQ!W@pMx4$%-6blS!GPmgSvE=j(={Fj6POO%VYYoWOpEaFlDCfS@rs#ID-E~*@sOZj{Fm&o?)_NA7|9c zGH}k&^H?!Ah0uS<<4St^2s&p3JxkzWou#c@t?ul5#-hG|XN3EmBj{g@pq~a!{_LB^qW-@$!u|CT^m`*{39NQZXCj&D zLz?Q~+=sNYIo#QdITr1QoBiE~?z}VCeMr}hpn15!edxYp1brfCT5&J#ShTkDMz~+D zbQ0XtSc@XlQT*RHCw2-~i+@DRY%r(Y`>!kS~g=qx-_=&}T-~(KA_Z7$tG7?yp zg<{tOWoy0lW)Buzf|G-ag|wfGQzJy27lV`GJ5%xY9ZN5nSbLO%9eqT@UTc4C8KMxue;G7>{~Je-nvlyc|P(v!UEe zv0rgWaVHV!k@%}r5y)yM9YJiC#-9-ceGGApVeC<4J*WHE6*>0!s;ONFxE4pziHP@Z z#rufJtoxPD1P0N0)Dh9y4-mosWFYyRMg*U|MDRIBk?Hp3I-8?u&xbf)QOdz%|8ee8 zPVk4Um+;u%X!!D-Q*AyBR_y1AIK3;#;|abZ!f^(Dt0RN zDKcLf&vA;9UeM<%{T0Ql6mL?zTao=L!~a_G8O4_s-%<3Lj=y9@_J<5NLs1SJftEYO zfveQMMsbs(+!YRYsYgKe-{iAb@nXeqC|<94v*Py^?^L``@e#$}D*jpVRmBe!P_; zcPl=q_@v@uDD%sQ1K+iFDiar@g~I|Dc-O6gyI3k zHx%WLYREGe9ShUDNU=D!^ zT!KSSP@JZ?Mp5p7hW}QjyA_XD+@rWxk&1Q6{}RQ^6~C@Xg_Lyvt|B#FkiJ_{?t}*Y z8>OF9{75mF==fi$xK?q4Vz1(E#WNHyROF#y#`i77pDO-J@oB|p6kkyMtKu7q?7?V@*S%vcPfLPqjaI-V#O7Tm5McrO^R}-GQ!K9%D_&w?^8TR@dU-w6u+n_cPk^@ z*OZn!l|kRE^zDjwDgI3HA;l*YpH`H+mJ$9hO24X@5OmT#PBBk$sp4UZ+ZBftPf`4m zB2|(z|E^WMRq-c^k0}05k+%uZzlBd(Vya?El0#Q0u2tkYbo!$5|c$4C7ia%2PiQ@f=4=X;Q_*=yTiq9*)toXX(yNVwv zQkgB&nW;Eau~>1XVvXWv#qEka6;D(=P4R5S^A#^q+^2Z8;=4#m3_f2R1L z;^T@36#uIDzG4Cv2$-&P#c7K9iiaw$QEXA%s@SJ^yy97kmneQs@gBv86o0QMs$LNP zUzC1D@h!y<6iqCFP|gI!WW{lcS&CGXO@A{L=P4E`E>>KwSgu&DSg+Wkc(`J#;&#Oz z#X-egiYF3vP{L&ZRPc&p+) ziVrLPS@BiHzblTz2N~s@sklh7OtDV!FvV?(1Bz!WUZ{AN;sZp?-JVkVIbs2nFDbsK z_I`XQ(>_3i|CvO{Q%J;IVWrY*iSSpi^kGVem8P<9y7wu)Tj^7Y;B%?cS1Mko_O~m2 zkKzN0kE;6%M9A|uBF4-Q6~|#sg6k4lMA#P)bMU>V^cvzc$V)`{ZpG6SsWqPV`xLKL zyj$@>#U~YCQ%uH(KEq8?EK#gfY*ajwh`zO3>0^n=pW~H2Q|WV*{;J|P)&6>=Z&Uh* zO8-phUnu=+rA6O6#YD(OMLVR+iIAIW!iihdy+i3^ z6n7Jg4C8EdKTqk)l)g&wTZ-RP{E^~M6(3gojp6~tzbL*=gdFcE2C=q7dJ++G=P1rm z`{j!36}Ku565)TB(t8wns~6W>_AC7Z#m5!@toS++`ST$We5^@8$dO89zA9Zp1iw{E zZ&bQX>D@}7q4Xt+-&DMV2!6j%d{*&QMH6dIbRSDZ-#U{BdGZw(E3Q(kR%}$$mgKC1Y%y8n>~KCh_#TWTMVe*rKb#wcbI;Wmp1z6;d; zP^Ie?+Z0bwyg>1K#aoF;|J_9Np^qs2TO#EAqtgGS^ealguk=Ss2eI}^|6_>YH&N-S zO3zigK3Kx- zYlTWLC!${~Q@Wlw7aIGN?o&Kn@qER7#9AoHRr&_S?jp7zW z77C(h1eYqVP^?s}R%}w-sJKn>D8-`{hZRp!JXP^RBF4Q-6fal2O7U978x+4sMETsS z^v@L^R(wqHImH(hUr~HR@qNXAD8_M}5qzl`oAP8Sh7@xY=Mmv=iPFmy%hkR?>1M?? z#qEl{M1-g6Y{qkvA~k4}zEF`$n%Qs4dMfY+rEgM{^;FpZP-!Z0rvFD3pH%#v;&X~G zD!!)pmZGe$B7BhRlfX1Z-U359TQQ_KTXCKuwL;V13dJf#-up#+YKbNuso0^|r6}vN za6evYS(gR9SLyQE5X*>%5=`mFB&kbU#s% zs;NoK`Y-T5l)hY1)_-Att_g5ns(af;IwXDQ|@7AsPXHp5pc)+(-7+@jd7DC^4b->)>4ZZrI;ieFSbPm#K~ z>3+51w-mpvNEO|5zen*u6(3ThLT=ax~ z#Q1)wVwqxBiu6T_Usc?%_)SIL=|F$7UJtxm zX<4@i{h-paeh>Odr4J}Rr}(C#tmnfYwps{ZzhaVNsv>WcV!YXk)agySP?5^INtY{9 zXE*7Mid5ZAdWRyFb(20(ks7TzPrE?zGR1w0UsJqBQP%(A|4yavQ@mgC5yi(9|E%~I z#a9*IRQy0u{$BvT)R;|q#w%tkh7@Nj^137XTdr8D$ZHd6U#obeVyj}8Vvphpil-=^ zrFgF5<%(A+UZ;4Y;+=~3DBiF5pyK0-zft^y;-3`%qWD+EHx=Jil>b*CJr>vRfv7%) zFAv{cM6@}4-pc$UM8qU8mx%tTkce_#LPR(|v`0542=;)j7v4Mz4niV$@A>R>-TZoXSU9po0e%*@wM94j)DF5dJ{~hdS!1oFw z_}xhapCh$hQSCRw;m`0b)ck=TNeAd$+9Uibb*~_T&laU!dCpP#d?Mt!PwD%KWS&2` zt7oWnD{yGQ!A{qNlR`sAemnPn=WoS9=6qV@N4BJ3Q+H%X5H45mcAVYqIgxEU2Rq5m zov3wW(~IuQ8O23*zl>t@ zrD!akjZ7|AWLi-^?pPl^66|mCL=sif;;`)4I=tOEzmVT9=GLuSxiQ(}%3~=W6338x zw|AgOnD0)QgKfI0Btcyvo{{gKA@zikL-x49p|Lif2ax3q)*!0IXMM1oA^XeNp81qG z5c$RQRGw-0#Z(@91f6H3Kf3XmjThDWQ~k|{ABOS9i{=w~+<4c)#_Nymgs=Pf)vy|f zu1VSxt&+AlZI1Dv*-3vxxwhf;^4kl#2Cw_L{Inf%;(a0?8_gLWU6-_Ky-<#z-4 zv2Q0ot_`^So`#v}cKH>;Aqlkmxcpv%1><$=1^s#J3*GqeJKM`|75uq8=B53C0x^cg&H{K_X!JZUY zFkUR*MdIZ;h8u?AC!xbyLxVo98`nL&{2n~nnX8ar2(O!td*ROw18buV{7$ApAJ^?J z7`*)arx?cF@I!vn@w)te1%GZBSR2oR-^0jj_i^30-s0tV-zm6ox|h!{huG6&5aDycq7qMTI3| zza@B5Wlfc%=N1fwXKsWq=_gT_@x-Bs@#LbNk2CsL-(tJD=r{iWag%mS3HRI<(uy{WQkp$XUKSnJ6Oct(n z8-@jcVH1tK5&$NMu0chnSsZ^-xr`4E5NO0{=oCh(!gz~FYy5`CL{#D z2b)C0;=MnKQ`5mA$jH+ZXI>Am&!p?D?|_40{~no`IGfgqb|R!o%w=ek8IERrZ z+uWFyIFA=<^RmvHfe9><;9ZcvAs{zx+jSH$>C5;v?0oPHB%caN{Pr4z4Ww`>qXuDb zLs)AfqtDw4BdQZ4lROM29*SHI?1g_Lun`>0fPC-=%8>{wz)hb1z@QX)6H7JlEb=75 zu=sTvO#278CHDu%T@P<|F@;NaWceu~3ue;VZ(j>e!AU=aImynUeKzYrs(lk|f|Dsq zy4=4U4DEtB+b*D=851zvhU_kel|0L_hiE^C{^#3|p-h5vx%6IQbK_ue9?eVaFa!$D ze;2>YQlR?426@nY~;rt1H3Cz+ZJ8hd@GVNsex0CoEifsE*h+vOlc7`M>dn}{M1#7O5 z#{0=Bcq7u4n`ATb5@VN!A}0;k*e!cS5P&6#!w?NiA#TbL#Z(;Tpy4i;7> zlhnhN^5o3qX@7*3+@760okk-ChtQ4W88rLtE<~T4Lvy0s;GH~^<|MlS!X?i-1>RHB zxHUa_HqCx>YI@r7NN;kkkW(==jQ2A^)?@TEm#d}8sVT{G;K#lPC6SUsqhWu58jvzZ z0@)A3A|;jPL^}xAlr)-?z=_#!aYtOr*jK?R$kQWhQnJ|CBwtb{I{7jY_9>J83@_>S zc{FDiz{`C5Xb6!qh552f@+Boi*(wZ+yI@nMErq9eKUa9IiHIZ+;G$!^{~P!ndnGf)o${JJp$Gm*03q0M|t00#_rh_`t0w z_k=(@eiH-h@EZuEu_6b!9%~0KMyRBK1^eUxcRZ&AHbdw!fqlrb)IdLylonV55yu9& zuX9}B1cXfw%z{Yc1J59?2?6dS&5&D%GXodHoE6|~bYg((b&~=QgMW75Ht>u~Uy7_h z$r%3xoKus{3&Cl^`LN1Noo@1m(2O3|_Z)K&?nVZ0cTb%ugg?Vimsvt2X7C?Ssk4Ph z%9sn;Qgek!%?P2orOpv%=|<+a!BhIY)G_8^5GIffDU8622w?^;z^^ahMA!1 z7JiXu$cgyCa`;RLd>`Hu1G^DjAaEm677WaTs~yOIG)aN4!!PH60U~!UaO4XY694N`%PF z*h2;jO^)l?8Ap=AB5?_2l#o~~L{7#aNRyj!Kf@d%E}a?M7|&93AD}zqTzWfH zT>3L|k%ZJ`LJVba6LRWulT~e}F@|%8HMC)8w9**=5aU`QnQHAt&Zeyk!!?ULxeLv- z_a`8)>=R*=_V))6z_9C)gtQM|f-%uvhFnSehl!Yz>;bS(`{)4dQ*Dm?jyXN!ZQ7gW z72uPZaRf>#%`({{pUn6&UFMkIhRg3JUWbIG%{6}vBHNsJ86%lzl3U1}$p5aT%{L#0 zTaG!gmXYR(Tdp~=gQ7V_f{(*7OxY~>lhmPLd;sQ~Z2oUG?X3ea z<1Tl~z=WwBW8Q`})26vDG}9wA?{cSycz-kEO#C{8kh(s0`~t)lr~+*STJU2AI;5)a z#;+CN$E!b(0CQa6On8hBJb~0E1l|VU#K1)i6yQHbgMkYnzHM0Jz-a8!-@dh_kYL>SGhj1@zHfkGsxjqxNHON*t3Z6x#fT_#YqX0I!7}b+E=KfR5a?Wun2YP# zc=cg0av!1CV_&v&pPF+Y;s4adh{(0^-a8K`M5K~i@G_#e;PGAvnZGNH@F7l|C1Xz{+HZVa?lkaqu$x^@wdeM_uVM+ znJ)Z@xy@u$QJw#bxL1Y9vG2(GMH{B*2j8@Tsvo(D19d)n>jt`3tbS0_4R75*yNq76 zfxa+MJ&^_c_t%3MGS7)H- zglx_saQH;`?p>&nn@D1=)<87qYo(Q2<4hb*{alwgP*cn&-`P-FQ`S=Z`EPEZIv}WZ zEW`Z(A9oYO2&G1j-gozs1?I_r$DIiyBGIc6qKb1ys)?dz&hq+N=oN(IPT+$tLh#&m zaL}y<|LUCt2VFtn=-U~g?lXFOz$fbb|18S>dpyef_5Kf0+{g+mn)XMi(r@zoQ*}h{ z#;VmHCHGtvbY7kEu3r6rdPL#{0-hd$rB+Ri^U#$ZtGFPl$~PQSmY-X}{=cp&f21P( z{~;Cl|ANwc_88Q0AJJ{^)nh+WH$CrV`7FBUW0lR1u3Y~Aw8clX$A5Nh@wHU6U0vZ` z4HHxCV>H7@wY!%#HdCGTf3KEy(N0S};9-%-(U-igx~`&0wGocc$o@GdnWLy+AEoCR zsY_jSh@N^hgW zI9IU!%bISTw(-xasa;)J*I4<_=%F1YO&_mcR=$VAI!HQQ$e%m%7Bb}~lLG|i774@c{ zti{v~{&DI`*>Q{BSMB!yeRYiT^Sg8re*O*hbMikM(FaQ(o`?E4y$|(mdLK)6P(^Jl z{vXcsSTQ@-uojH}#f*hJ_n|mX)I{3yxHxpIbkuJ-9u8Q|k%h~#T-d~%#q(JQ4S!JP z=l$q}U#_-uZ!s3R4Ih5Er-n#gM68$^K8Vg$Q|>v=Rr@@}rHZABYZU7hn-yCXI~DsB zxhInG>`^>V@iN74DBh}gx8g4pf35h8;>(KfC}LSh_>WOcSClh!hXedNE69=58 zw48kdou~8y#X}TVDpo44Q(UjONwH0_Pw_a#GZp2m8{*lo^!F9-ReVJ8X+=512LCpf z%z<)74VaBAMbE6SNPxSyo-7Zop6{FihYX5DW0kLWySr9KT&*G@wbZ4 zE55Gy55*w5AIdXBu|QGIaKV1H(oKqC#Q{Y*a|M6bDSfM=oUMYroTUOjulDaL#-YQI z@>HCxSg3fY;%dbv#jxTIMLFXH{--H@v7($+g1wwg0zRhpayAL{AC#7}Nud8r=~ors zR{XmnE;JT-6BSbw(-kKwh7@Nh%9$nbFIIYq;tItI#kGnJiW?Mt!&JsXu~2c5 z;xffD#Wjj`ip`3LD|RXlEACM|Pm$YlD4(270^X+dy^6n7{Jr9TDZZoV$MQbIrzuWR zoTpf=*rK>au}5*2;^~SPC|<63o#JhZ_bUEU@ehhGE55JDEpn7=w&DuKwTc@Qw<`83 z?p8cQ@j}I?6<<<(P4OMY4;4j68R89KjAr~}6vr!0QkdHn!+(x-j4J@~2M_cJ#Tkh1e3x^;|n4fus%$?pt)b1+`UPuV=#YILJs!E59p zA3pfBX&KtaJDX$+{-^K3UsAGg(P!L)e^aSM5!r)3$Cza_O)+jwt+3`zd-9JjTCYwV z8}ym4uC&@-#k})fbJgy7-sbN$eh+-(Zz;=pY0o5z=b4_wn8y`*f+Q*C># zH;;#GN7M%%z3Qz^rC)pNi1PCPI%S%_#C+@IA{^+cN~|LPgeqTEu9%irJo$n#V_yHe zh$Fo!uIjRzFSz;an~j%FiNCeXeyrtP|D*l)bLgC=A+H`Yql`)Mz=T;q3sO|L4dS|Z#m31@`<^{`T-@N+lb19)G>+kpU5`Kqq*OJ5WD>u@YKW>O2{)Qi4 zX_wd}{1S~12RuF^m6(9dgPsE(n;Cu;T&yQg7k58rzjDB1GbD=l-$+|Cp6B-C{da&! zppDgrj!h4EbVCyVe9CC0!k530=Sr+FqKTVKp4M03H!f633?dfU z12|^RVgw1);>Q=5LeBwyp!fs%FvkU`00T!ksLwk-RYai5i}baz{p+=i-Z7;hAmqPgEaFf2RHd!KY-T+n@0C2$New| z?!=D~xEI`Ul;aynT%yfA)9NSrAN2E6c6fowU|k@;p=Hz1=D$H{35 zNqpk?8UzTGN~kO_FaxK;>ocBAJd6a`1Z4y`{ z!ENA!qa1XwA4ggONmqcSVZR28K=MvZ|NJ(;kpd~yQo*AfJhotQ`6Mxqr$plY6v92q zQHW>){FXKXOJOvrniDA<2eTEJ5B~l@fHfw-@AvosHPzrKM?N5!_6E4|D94ouZga8` zOm}3l;SkKEwcoys=1Dxbk!16mBbd!PkZSLu{bY)Q;{dP;hMY47JLqQyYh%d%E6tK; zIre_?pF{ui?MbLW!MQxnP-556ejd$B>{S$NKDX7oM>#0ZD*GAoDLMz{3OgQo6D(og zud#2Zc@eXq#^yXFxR}+Y)@Hc{m$LFU*iXYIxO@xD&Gsh7)kOI>*|*c&O!E;ox3va0 z(Y(dxna$v4#@lWuP@cnB(L3!ADZ>|N?y~pbH`q$^Q8w2Vf?YK4u+L-I9W-~_dnsEt z%{amrK9-cslSS$F*>Frsp*hq3 zJxe*2OtXb)k}%~_jtuy+UV%ixowQ=j&dx4#L2lrI!8H}cNSGuCC}wC2_EI3B6j;9;E|F-qhY@T z=aexL$bJf`O-ZFW(QZS4lr)-ol!MuCT?H{x#`2s-FcUvG%E87a`I0ix$(J4QmokYX zNV+|hv1I4K%Y3_;<|)jVWs)x`A4Ddyq_XHDfsg#!i;$t_ksw6{h`Db&a>9w|NV+p6PDDqNGyCGn?YO_iJn|Zlb3sn$i7qot z&s>O|J{<;g3*#LEIR#tx46~Ja6M$`SiG@~i~uhg@ zeUz6^AzpLeSX8dbxWnAscMIqP2t8S{c^{djqw>rI8dGo?xMA*R&^54|#ZikV zlYWILnCr*DE*o~#0RN`iF@@{RLM9W=GiJd#3}<>5ClnIhhGB1HzsGc6?~1kzR_4B6 zx}sgrdc!`&5$$@4_6?BJr3u~0+0GX_npq@_-{tb3FylqflWda?sQKR0Db=yQy$4bdh6hp- zm)eK&dzBe*Z~f-Q9ifKBA#U!C5?fws&Q z(`Y7?iLsHe)v?8Iq-(?tNqKk4+Po~GPbr`g`p_$98@Wz3`(wzM? zGMX_^>~@ha_|Zo=I{joe9BAp}$$HjSZh7!zWFyE8V%0#{EXGE{7sR*`zmbO$YTmID zd}7|Y9zHhjTo2K_V?3TH(3%lj3n7HpGKP$8Su%rzh5GB4a~kXYb;~)#L^mS?}3OkSv13b0cA{V@oI$V@wLi zh}C+66Q+gWWW{=d6DCp=%vc^yM?3^4Omoy@B%k4kMG^5x(5MMcfk+=GGczMbAScZF zXqXKQ1hFgRep4g_yLhOO^umaA&3wMGe zY_sbX4PAWc&;OcKQC_Bu^BcY z(Brc+%=E$)%h#x$M{L))yt+LU2>dv8^b>W7}x(Y)twT^D{uc$0*S?xKF;hsSl?AqOdeeJRlJbFHL z)Z?q}xs=G6D`NcyF=*yEdW?=;T^JHlaYvO#O`f@1zFy}u8e#*6WP^(BV8 zW!_1E^WXNOMMHgbtm)|8KGbQr=NKbLo!q_rv8SuNCsdsz#GJ5-dOqk3S>M*9kPFf5 z+ECe8hSS6bcegux{tY>^D_eger@0P35VSQcM}eJ0EGUjigl^4e_VxF5ceU-3WHr>+ zRF`iKH#AloJF2>HXL#AJp^n(7uYnUyElq3%m1qZp+rvj2ylP!SxQ7{d0Bv2k zv0}3kJtr+EWhX?set6iYV9qFC0ftjDolB5Dk~d2QgycW zws&_Ju_x(1#~7oM4oZo}wm&&O9X%ALr@Rh2S|rE(odRsGuB~j079jIW+V|%=;*GY; zV$N@ioSeZwNy&aobz_l%x6jHXRaTnYc7zMB|AG>+nwGbec3HPsbz=4}Z?c3O+4%P(uG3Xf26fvyNW zzVx+jzYR&ELmt?$^$s397x0Ys{Zvx#>J;&6EQn;99%UPB>+EQE2UB+%5!;-d^Vr7f z^75sjTsEoZzP|3k(6+vT5R}u0Dtoqew6}M(hj8G!tz&SoZ(wjvXkmUyeo=^DNri_L z9TLigGW?F#!Hzkh!x5yQLxbck%-ag(_-&mbh}$)YktbAKQdn}xq9ZYf<(kDuc(6o% zd`eQXtY5%N9)941=7Y~{kA1t?XPz2=THNXWGpsX5 zy7T+IG$q#E%a`Bdqw(c;{K#f zG?!zekA;8Gb8(X8S%-Hl%4>(xY@1_IUb~fMTO5n>IZJ8AI~L_}h0<(uW0C*g0lE+6 zy3Hkkr0*O--v^rUvrUdg`TRPDM*cekmCnI%9Mc`RB9m|D+bgI2U>8qMJmQPQVd73~ z_1i4gGQ!UJ^cg`$gT&65^>bB+pwS0Jj3bE38xBJx+aT`^T?!1MTGuGDe1d33+=)T^ z9Hl2H&3-pn;4Gd7Q3x#IV3D(48N@<5>m}VoM7VQpJvdL@^FGFXzVi=A9|DoTZ>F=~ zoX8qZWX<)Vx=kZODAotsgQXzTLAq3Njbg3hVTxhJZpA^x-HLk^&sF@2;@1>!QoLL7 z7mBP$jQ<(MmlfYp^qY=*K#_Y#>3^ys?}8vL_eBDWlwP7JXIx=lr}SpUc11bk3U@i< z3giwd^1D#+8;Uj?LYl)gdncE$S?We+v{J*zZ2F_D*QN0}cBQQSL1SU+#CLd~#nQ zuu$n`irj)l_h!ZIigLdo?B%{K;J4KNHpQPR%Kd810{L=h2XUrip&}Il z(|(O&v*H#-xz7*oyOcg#@%xHDSA1OYDPkP<4=VjSaT@N=R+_s~NplY|i#RW|D)1>Q+z}1KUCV6 z=CZ**JDcrY{ zhW}F(smq-7EsBp4!B@_bPBe@+NFyBU5HW!W_cWzLiZhAuFK0r5O-c_ao<~HuuPR=n zc#GmaiVrJ3t@yrTJO*UW6HqTBrL)#lsc1C{jZ_-TAN`p&JoIG=iKt z)1LDTB9bCFPZ3iJp%*DGRjek0iH!3=nb!d2dj!~~?%NfQRvcD5Uh!nbvlYLjc(LMt zD1J@x8pUrbepm56#rqW>QG8tS4~l`>&MaPr%!c!uKHirgelcb?KAa&tKG2Z}Op0e!#H zzgK)l@kK?R3t~9#CMSNRDDxW7LCzO|qQe6?UTK-npx?|T4HVrSzy(TkCpqn_6=i+{ zn&%Q|&#mReqZE%;99HCBbGnO851`yH4ZK9{<$h_jnUu~dud1&sC(Jf4YBFk$d1t%e~S-9PhvGeoKUI8C@t2AR zi0Cw*Q{*`@(yu7Kt@ys8iN2NYaf;kFPg>@2z^O{lP@Jonueeb05Jj2aA>98%-kZQz zRh@godpH?RPDnxmNq_(+2}2k%5)dK5j0XZC2}wW@Bnin0CuB4O1jp2oTH9i=y;e}D zwrX2zL9KPDN?UDhdv6rMS-WJqW)Lwo6|Fxcd&Pj~zz3=z?-uL_cz70G7 z^{n-*XFcm#<6dX)wfLr3sd7gOsU3qFA?KF77>AYM6~;rM6{O@BI?~bA|llh zA(t2ZQT-KSI;jxT5rs-V${14|m7bS~@pqn}N)PESB@I;RBK<^aWi)8uFDspp`yIXORA&hlxn<7NKt?Lil|`A15OH ze-(O^h(!M+^cf<{;eWD+NbvAty+rnr7axKLA7jNWEHG^SO>H{j9FGkD2}^Gu7p%|k z4U6ZziSV%jvM@JJVE9to%JDJ7gRn05vC*(X=bT(TVJT5X(I*W5!Tg94y9#~s+y4Q8KnCvDirg9ZabJR*OQs9bDhPQ zz_ZDm%L(h8z}E?5EQdVja`<+`5uV@Ym&1N&I82vo`Hua#UhTx8jAJOYW;*0@!uC#( zcdl5ErIg1$#gX^v3G$d&z!3&?SJr#$|O=E&o|7>wi8;g&g=`_NEwF^o5#kHh(HfFSk}&gJlZU-%sVbKv9G ziO%Jy`V{=a%cJB194QAT>X(Vz(Qg!d=u7*Y_{O6D-kw6EkT({VuHz(zRU>01rUMbKU3VFlM^aw`p)%hFU!?C?XS~`==a}+Gg*f!gYld0iTa6HMn%)mj+sB`H^~^O1Ixcc& zr1z~w-S}24b`3JJwv28F2-+&%+CUF(ip=YPRH}q&%~^&?AtM2R`qcTkiS2&zj7}24ZP;#W*MCOmdBR@ z3nV;f%zLf##EJX6?mqFC6Hk@}y0V^H<16r)6{*UyU+enZtgp50>9TzV6?@v2RXjDT zu;P!W*Pghy(+bS+eLk=X`SVs_?<2JX680YYmdkhDahC1!vGQ)~tGBK-vd)eE?lo)o zNY1{qhW+MmN4(6*NM>{Mr(bm&51$>e)}H-vS$enEcVO*ER;14yZ4N|@`2(@*?2JW9 zhpZENA0@-v+#EAzXWrfXQuF7}U6E&cJtw?_=9rb4IT&&L%oAIXx9r!>luv;MH~XSc zy2hC=jcf>9jPlKbZ9fR$E6p>vjjRq74$r#$@)i^Ieb)1j2d)GrJfDzZqz6=b1;h1K z+NJ+?1}tBP&l>9ttm??e{!_@4_tRsSb^iKX!pKTcC*O!R%H}+J)`*WCb*+fN36u77 z$>+=qj3w`$dOz^i%tK`@zxnyh*c0|r>r`jplCf3xD8 zH8;j8dwpug!gEPpbJ^RczaGfIK2uEbhmSvf+FbfQ#HxM0BDzHB8RaYUU6Mv?t~yZr zw+{wxD~lYZ>%-RD%BGC2@l6?fYz`&A{TuAK6>nOluP3BW9Wj^v_4JczW|5J3q%7rk zE0EHMi@ndA%icNNiPTC}YFfVDr|Q7z_;tx+@$07HPDFiXdPe4m??~AnRqW_8h>s;^_$0=2+h@lKM6Sb#A6=`!3%}?z$DYIPLRgQ-9tPcy`B!S)Nlh7q6S)d-RSp zYt_$Z=2tL{y8`CwyPB8yUOTP&3G_Csw*%jbyglj;?x>}I2%*Tu zmzejBa|zy0pG#`4AN%@Qmt`*A`utY{w*?~3#+Wyk8E+mLX$mw}xiW@lC|hS$jy`@i zB0l1*%ZNB-e|4p@PV=KhW9O%?+Oc7HmJ6x2{&+a4{KjFpGVMe5;rW3(178iqEX!<3 zs`ymd!1IAX_T|=DjIVE)acg(nan4ML$S!?7D&0CY^-OHMJ?6@$>twwDlyx@7ZVA{G zFG1sNqa#~g2S0soTC?k3tx5f<+Ogbqn{hXcxxPR@)8{(a_vlj9XB9tR^5)T`#ptsZ zuV0KlYq9FH3dS-5cLfdxt|`m?d0pV*C8-r-&2giV=s^?EgT|o;O+4j6-#G<+rw4tf z=af0-%8VLIy4+l1o|=JR?#UtV_}sowpf1Z_H*%hA1=usy&a|b;o|I3Yl#bfQ}dF}TooTXW(TRc z@fbyx%$BhD_|#L@SErm!G^qc<6V~Ae|5`FP;olS2Ck{t0j(OfZlu(5}e_hF&@ym=u zpX^B3plt7r8@sDHW?5?sQ?+0jJJUuQu}`@&Bhdeu-pJ#q(N8_7a;5(;Z{HT$o^77F z%CgUy+1HfSzJ6-zJE!{AVvMc55hF3{{v9KusO84|djij)exoKvK8c!mr&^*lP- zcq7_QI2V0(KGg}8T)o+;?FNiMO$C`;o|617wsr`^v-~}>dxl4vmX|i z`R3;?+A*^cnD@%*@4n%-QY&VtG!~q(A|HNWXQg$Dx$^j^Zl; z-?@RM;9I&r8+UH=rom%8|hdBiAX1LeG<}{t29BY^|O( zbNehS@7Z%kURFicn4OVUrTJ1H?F`JUipfZX-;6N7lqyd~H2jjqZv=eP;73iXe8aO< zJvkf!xs(}^x3VH~tj^~e(=!stKFzW2%ndZ*eX!F#xOoo1L%8|dB!8FWZ;_{P!!D(f zz8&6pSmstWezQi+lNwd>W?Fd$N;+eG21+_ZmGttl9a5w2ZvE_u>zw&ldZe$VZLx2x zb$;7d+NdFS!PNPjj@mUHwQV|T;B?eBxZ8k!_~UNHow-YDMPJ^dkD#sDr|#OrzTDgA zRpa)VPcSdMoO<#CbpP*M1ED&y!2H7Hs3QrgjCpE3(9QBEbCV`ms{-e>PB}XFmsxTUl?Se8(SE<>WKrv&t8_=lELd z{#oZ-eOu1Crr6*(A1$Iz#nD#E@+A~9#_^>;Ii;RU)VMV8E$cobGtO6W&Q%p* zeCe<(o`+7Dhi9F5=3G|(Gw0@+)VryvYS53fAA9@P`Umm0{`jk7u)l@BCG%N;<{R48 z)e;AqQ#MQ}?J9Q<9G&#hg99fN!qj=b!@`nr2t(?Ku5O&Rz=b~k@}=CcIXAp2YV z`}us43%@8}U~s6V5N@s?MKgS`t(DIn#h{sH_fSt;8Qgqt4zGq7T^)U`{H>b5Q-^Zm z>*#6o;ixvhvDS|-(eTk4o|mz>--s7J>++4p(rTk|TP2VDG#cwS7>(t%hCEOk^9NRW z=MQ*`fb)metn$v|-SlbawRhF+yNy zH^fvdLqdZsZ7qW>-rjaEKcVo>zjR2;`M6v;i7y=R;uxcW!TzCER0MC2A73=!2xu>k z1@z+dxksNS&$@DtKH)Z1a@ zssCV)+wWlM2bGWZ#JSu~Ew4XrMqLzMx&Wn*+R5qhwv)xXp9L+6rO|ng0)Po+y{W~lH zTW*^>6nOa$~wbgnY}*mv(J;5^~~lA7z2Mmm+GU`#JQKQSL7IV6)xFp-{}+5^8c1 z{L^CQ{~B&j2|{BQaO+mX(*`5PETng|$A(rhSVuBVANN z=QK8YH{`hA0Keg$gSKM2e~hYPxt~KOY&Ty%b-7o;Kf?VQ>=5aG95%sbyM>SzJ9RPQ zPU(wH`Za2e=QM@NX1iA+ud%80c6ojX%f-&(YrL_Z&oNpW>p+6%Fq`LWsxr;<6}r8A zRXojeJ9#q8P}p8iA>%5W&GV#C<|6Vh^(=%gv5UDiZJ}p1>TT>2x>tIJnaWbWdZaho zjm^0OX_a{9QhxqTa94N^Fm54jzs|FjGFQ+9)t*a`OZn_223_1v|wy(!ImOEmdQ;(%tN7f`wvbv)wOIj%>CYhcb_q&31oD9@%X7 z6{Hv|o9*6-dK4?0?S7GIg>SY?6DAx(8r*F6TI4t8lc)+yr$p0MO64i;8A#CZd;x!^ z#PCguY08{aJY-4rxWGRp_BKRK^K?LkDe*L?*TbZy1UK9LPoxv+qNJF|5G^at!_2D$ z;$n3Im%-+7Q}!eBG!I{pj*Fu^)$=}V8<#+#X-a5ZaI@VSaM@p_DZBe2Ins4MJf=R1 zKZz_N?7kbHo(6^0?z{0Bbn4xA<1^`YdH7ya{5-m&J-$rzD`6Jj(V6C%js}{L#`kHKdg`D= z!WA`y@K1?BM~{!Zl!;EC!Fy8y7UP1_4$S~J5& zJ)f2YXS5k>a>woIYERNQb0^%!bbj<~#+xRRbxj}Y?=Z|nvkf8Ww3Z?RW^Os-r^D$o zH>x<(d;10qv)1I>%}gr;QR69W0C(~+?a73uZAR`^nptKsCXp%JNzPnsR&zp`;(?mx zQj=S2x>8ob)Mk!(J&5QOzO!ZKDYn>@6jVa9KoJQk7NnVl=3%f+OKC-IGgp|8fk;j1 zA=@e?AT8xVvK5&xBgC7+H`mNHrVUl+r8HA?i8&iY)^vX_GErt0fLNMx6D?I?t_6{o z!qPTPN65^ijA4?9F(8v`3HMR;QN95~$%!D+aoXkhmo{I%SC zH_vvDK(5Qp_u3=ehf!pa?ih<#La~9pN5?og&?Qwu^Gl1Anxe=fJt$%Mcpl z{x$66abFH?V%<{_I>nubMB5>O}5o4q%Y+Om?*JG5rZkM&?m9jWK|nB z;yD)`L(US8!Eu2x(?`wMD5Kh&QOS}vyo1oRwQ%N{N$=f{lJ}ek2}yq+Lj)W_0Fx*E z7h7VqXFIHt^Z~1XtmhMmmGmJSOoHdD&_%narEG(MB-300d8sLWlvI*!a)5e1g&!;= zEi!!wd13Y~$XL>1a|?(xbM`&3Q_>QX(!A#ELDajXrDi+A=9#m3c34uj3d=HQ+f+># z$x_pZXU?8PiYj*~<=fysne=<69@o)kB>jOR4bRm~?M2mXd;Wzwy`(erI%U5?o>=U!PYL<|NLx%p0&t&x1Ap)JbW*do$KXEhsYF31{grVaG2*EMGbVb(r@%(co1!l; z6wUl78edDh$Pnr3hgXz#{s{?1{{=NzReL-2HUzmntcLCZ1Yv3(iK=O*T><~-0Gw7O z!t6PJB;O_YW6a>GhPMvShyr|Zct$7M_HDy6)+3?A!!t%8>B#Vm4^Sy?A2#gsFr?TE zzk+D?D)3F4{&zTI#_(s_wB$-i_q+toX{oGhhNproGj_t;<@p;_oHmn_;b_lW5ISwv zy)a6w=ieDOjdRZgLn%*o-K9SS8HqPhQlyKrRqz$TVC9nFYv3Cdt8CLoPDZ6nr;Gm# z+;cXz^cmBW?uRqxder8G>BVorjq)Bgrmy)kiqVMc^zS#OU+^y6F3)GtaHp40QncsG z$#9p_9cx6t3-y(?r%$f{r<ODy-z_-nhl66A7!FpYi8?s5c%XItN<0u7m&d`BZ1_OcWrOq2^wOb7efZuq~?AC0=U>T_ZujzQbvzMIczM2qaHq7cba_ixaV zD@}9j5Q~c{bH|X@U`PMHx$RJyi%xU-g(4PBy5_Edqidjh5YObu^7K_SpM4OX(^u0F zbGU|+JTo4t#c&tx9m(Z+$g1Zmuq9WpVj7;s$Wn48tF+6L1h-BrKLkeTNBHOnIFU*Pt7eDq)RFx>Mz z{3;@O@vq>{Qp*9!OK6{^p5ydis%`aes4L0Yv@JHB{v7pB;|LV!t)R~lS|m+MWT_r5JNR`>g_b$D!5bGTlG7h ziFASeETg%0I^oQe?l=^uFkR^cGoN9>sybq-&tRHzG#E@53wz4%Muwep###cZKgKCAwc9>Xdm zkFoTNXol7+Dlg_`h>-a%G6WB{r)*~B74sqRsVtukW%*NJWmK&tsf1&o5{xJc$>AQ5 z=EG|G17G!Kta;>b2+3tPhY_vaaIYfG58cdftC;kqja4!I)Uvj56&J9CB^_h%|DxUjzl$Rjz>U#IdjmCD!P%VQiIDR>Ini|*bkSNVtk zOcl+KXvuA~r)(Bo%xXmN~6^z!vXj{OzoK|aKNuB_; z>~pbHw(5ny8td~?%0}t9c^x_hl&dy8J>m(Cnc3xr9?}0K3qM4n)IN9;4Xv4v1e_^y` zFqDdRmeH;Ul}96s8YqDctj^Dws>vW=o)Yp?2+38Zd`?fI5ij?B_CL>=>@Q-`tDi?@ zex>TuVvsA-v{>XCQhQk^w^>e(>Xg(bys4|DZnLOcBch!@a48wt5%epeZOaU6yT!VD z5cH}Z)HrL0^-HjGnP4?jVJ$4mf#Viv`5JS zsW#CukK7ehXpd5e$7h{~A0-T{OKIT6;J$_xk0}}jsj#5+fn#n1b-_H+il66nDSf~! zZ9@!Idp=-w`4E&clkR{@p~D62Z3YlY9}&Xt%$N=`SYNcit}!dR5IR2?YOJ^w^zsm0 z{4nSvOI7R$8|?{V>LYCaikojXh$NW6j#&(4Rd2+k6OMr)-iV^^8L=E&_Nmrt-w%&W z)x7tqhL^1tq4udNkge9BG^3I83=CrBSxy7o0)gg{YtasM`Q|Be@=LGtDFU1{4j)8S zdFzOMAWM|B3zW5uRU@p=%dB=ZrY|FK)x}JuP^Iz`>FucX)^bJ9K(Uk#L*Ft8#vp)Z zZVe#)vM<4}7J+4-hhrBVpM--4R+m?kZHP8fM<^9%fxbssPaWc8axqJmd8}J zH0C(fau;=e8%n8axr^0uFGi7NXHf3eE@t%+kd+lQeH(j$H%Y32#?OI3<1_gu8hwS(P#Y;Q>1y1A~X0^X;f;#tYx70iK|$GTl&6xAT~ zHwcYGMCM+FM%Wj@+bCxKUqPS_zyz7TmEbJwFsvK?2I3;!;IL94$V5sC%|8lvJ{rI9-y_4k$eW$vmRu6|4LG6 z^AO1?;8>T3SZ$D4c>=mwqxXQ^2>%s62)O|MAvJ~_HHxYc#_G@@;lnUuJN%dp@>A8K zKj9BOhC@t6v?UwCt>VX=;T$QvuT{^Uv#4GK-xmxwR`NHDkP>?xC5#oYm+_J?DnpIy z&Ds(ARxs39Nu4NWA5hJLXn}-dq%eWv1S@A52R0x{IOoCK zCsohz2q@jHd{=iXs~02I8D!DO;rA|9jba0-J|{4b-NvG>0uM=u;qd2T-Td`!?t*_Dso$%X({Vu@aGl>Y3h0pz4|CqtU9KsTPjB1z>~O z?JJB(*xkOug`Tq*bwrJD<0nmOtV0c1|9gs_<@8mQU>@m5I?L9va?N5>-UZnkhCy)5 zHaD?Krm?&a!r$;-kKWYI=7N;{8N}0b=WI0*sODsv7hhH}kIYMlvaRs?01-F#GOXrO z{MDt)AFhp`Lrfi6g48DRg|Yd(U~9&m0!PhF2t*%I%szr0U9dU@Ym9Xnf_w+?ht2Um zFCyKpB2{ntDkXnQOQ_-y`X)GEY^>s`u8-0bN^vu4?O5;8t@IFT`lu ztI_N3piasPU9{9HqsWUSY0hUQ_+|vamJh<49LtQN0G+Vqiw;M>5)d$o4XESBrX5Gb7MK(A`08HbelI!N=0$J(pt z%Ry^Swe$mC<@lX0&EQys(V$XV-nH;}6s z?9hO==1jq;=EE|tENFsefgZ7J1H4ASS9T#B-=q9pWUA@LUtQUK>^M*2ei74W#pXf* zsEFBc)H=ZpjJO*Au=Z*?l- zG26wMGbo3n&z5^JY+28!DLsPlqc0P97?o2`Ep*5zr0NIXXKW6le|twCd1r8WZ7^d8VfJ0OVlryatW>%Evlw!`mo zq4i7_zC~Tac`(FTjJcm$l|oDz53zK%$=VNZ-;Magd3rmtq}BY3QuC8kL#cH}sdW$4 zxCM;tZQc(GhnxtTuY<{uaj$U9hB&RL;^52F!tov9xL`a7U$fR6E1m^Mt%MGtxJN;= z!=b!=5sv07eefo6GmNu>RpRDg9F-<~l*D|ar~poODPIweK}dZae(YE%^$NHfpF)>y zO?UCZkJlJ(^9bja=i^OF1aOh;=deO#r<(@47)-W6+sA_`b{c= z)AK;mI+1=%q{E;^EN?PDC4Q=3(47^7+Yro-;P>Id@|eW%&mf%B$hRaM)?9f9{HQ$K zdc0=_6OMuM7*RB0t z)%>i4>_tt#6n;)kM`|db6A0z43Mj^ix(lX>qLHF*Lhwa@rlqb?_R9fn9@&K2pzU{! zvR^eat?hS>vfm!ix|(07=mVfvJ>r3s>s7hk4Z7l_8}tpzXeh^3%2WrGsh&abYM4yr zH@NtyM=MsS?d%+^>9jl^u#6o)!012q0uILUe8288Xs2?5pBdjk>84EYgiriKV7?iS zi;W#q!8LW{dJsFiQ9`_@7B*cy9Xoa5-uVfn%{w))^Em+T88dgTM%(6{dT~DK zD-3h0IX^DRW5>;k+nh4hOhBcy<{2K(8hBv}l^i_e7IMTt8g(-sEHD+J3WkVUU=X5R zI9iuJJh_%9$-1);pJ&L%B&iU`-!mmBcO^n&U1oT!DM=t5);O@rNw{RiOKVk06rGTc zBy-_2b-KB527MAEloClH*&fdnPppU0rUlcFAnSAy8c~(R&}7O&nMq9XO{HL+*bJ2q z*m0&91T1*7C@i~5Y??|X!cl#8m^>8H9K@BJV0lYPM3wM{jvIE4BTmO}a6!fl36BUJ zSDYzF4=W|+;kQpRD`GxR->ph2oRNmYTi`L7SLZZe#gOGeg}~5aK+2%<$*Su$++}b_#JpxDmz)SQb)?0t!Ql zk;MCEj-^Ph(Bhfv^Fn4}$Vwf8jy|L}{a4Xo+Il2cgU!%~Bp2xz`Kq!NI|SD6H4C!9 zqm(aTd>WiPPL+hxWSuXaZR#VTw|cgz>VbD}^@-5sTHK*Bc1d9xfUFc!4u1COv!j`# zwIZD%U|h$8o9SQ0Wa>dU$*d2Smxpy`oH;jy84Nw?Q7^@LbcBE)pbEN;uVSs@d$j4M>q7$1~Bk#XrC zeqkt}LIm{Pq7YpahyVh${pfkZVOgPY&EO=1m`siHj*rH~^b>mI9VPxVDPRd+VOC9p zCr)=I%vR1i0w>uzIl8>Kp$CEU9Iz9zaDP-KEqW8d5wc0066}SKoG0$Y9Di!Ewo^Sp zE44YK)TVHy>ciyKQ><2MbGTCD&s%0O&`!kvjl44|Vg@f=E%e#MEGO8;G1Ej=POT73 zV8*n)=%Ne+5HLHP#E=$zowEH{DU3QXNj%(8G9gso=$fVj2;=J+D;HuAytqGFsNuSN zta!?#F2sK-5i@WmNzmoqNWf1{^d-zw&PMzVUOIGBD0E9O2o9%=|G6BtC!|8CQ_hb| zMTc$%Pw)!QWSE@wV3=@5@?vq5S39J<2Fm;AZ_=!(HDW@1hG z$0hTzQk?n<3BfC@yU2nkjv6OWNu3B?g*t$+Di}aljSe8tb~*-K0Ubc-2?o&hhz=k; zrkr(z?`dBGG?*lNaVkKa7_KEW627B-3C}8LBf*g{zE`0ISu2VE_wr0tsRc1C(@%r^ zXqoB;p$3d~U_j9jSQ#}hUG*xUj=)wh;iPyiVH3fLw&`dn+9oF&<&8I&j;T6f@|(tm zp=k9^H25M+U959qsVxMFliF7THfU!kdN5TmAWU6OblQep=o;Lnv85Wo%7v=$#3u)Q z)D<3!iEGxzaMqBjtd{C3!(R#x)1F?Ur((gMkQc=TuV}cS4MAsD^;cni;pK{3UcP5MBrd(Di2>K;UmBItE>QMG83} zG%M$Ygk!irsw;arjMW77gNw15uvJCdM7T*gHxnEQihgPR#LR zHLHPB9d!1$FvB+~fm;X%aofh;tB%s~koG4$qW#UIv;%*tK~3Vmnd+wnV~dkIg){kP z%>iC@W#O+*uaW89ha2TWQ7>xej$oW%mAVumCsb%X8Q;*(5F=}0@JfV>X%qk7DPrbo zeC|{kUk~r`Wqd7ExE{A{^j&q7j@xjfa!~V~xZ#9{x^BT=9oi7eSFrukO3Zm!4Q7>O zNl+2us0tnzgN20xG>?XrnsxN2@coryt|R3c)^yi82+YKn#mCfs-Xyt@6{@ z44@&xG))A#KzTLrO{=B2DU!HGdovPGMnI3X2XMJ|La4eJiP9}2|0p@R$0glFCPiTm zFEwGCq#yCGxM5|2R`5c;vcW1!4ocI?1~dH+1nByGAwkR95{kbC38u}thZYU5;h~OQqDR;k@h8&;2vMQ7#j=5h}J;J!;R9u@+cjJiWs`;C>>gM zuxh|ZzeQ=(CBQikH&P}nz^&7uYq1Kb!{6YQ1efzLM>pA=hdn2X6CR8N2Zi%t&q;(2 zbq+uM!!O~*0-cr*URM~eE1Wk51PhYLyuiNyG)v*tB^hy;4tg*Gey4-8p1>5DGC*A+ z&dnjtO<|l4qnh(;LE}tE6`>x%iyK|S6-Vh{LLV*H@QlTQBiiUU`i!(pmwKj73-hs$ zlJbW6xRl%s__z@Z5Atc5=24PNDoVV9Ee3w!rW;eWw2|D5sA7as+~cE@KRml(@eU#g@ooV;pq=;|yjap0JqgpaGZ+L1)Kpc2 zNb%9I-+;0pDMoU9?4ynd$Hz6!I$C%gp-$U@;0(8QlkjdLufq@;usI^FR}!|t6}-sI zN=ekg;~s@r{0t_N1(hho*$5AH(LN91Mop%fqg66nVgQBOskrdxM%>zKyW=$i$Mv|; zkr2oc32O}-8Ns|g2|xCyus7jt+_rIvd6W)#(w{C~WPKVpya+m09pP`dQSZ&8bm$sW z2ba2>Sj;J-{S>!p>>#jy*~aBp9i`)(_9xg#SNX#oyyn0K3lY4ybycGaZ)z}rz$wV& zbr3qUc6fzh(^0x~%uV4jLuKkzCfJU(h(&B0{a32l$21V=P6)}$*+8IbiUkTRDGR-noDX4n+Z!)v`vK9m2)$}kr2wp1m4Fq?~{N(YA61x zi;R=2#W-3mC)#)=mm+)}Ze)m1qnwQdR(JRkbi$1UN?~nqq@g2#PwC2-jbF7;3)VqoQpl+=!c*tv^cF3p#)>hFb^F z^=I7Bli+9)nv=)Em$ipkCWhre$7_VAy4Ys6;zs@mOcKvpgoE0b@Ur&BUv+K4U(Fco zdgw*vnIN$bcNE@6>N7GjEfRwvkNh+e`@{n;31-5Q`l6U&+!RB+8aFf}9KenCer2#1 znV6*3DA?PP)W+L32B*$KP+x|Q;$}ib%~>}FCpqa22wu@}K@UQ;a;m4@F*wIpOL)k0 z;wFwf_u_^%31hg)JH7_!;i*2vyE&XURGx?|avR7yhMU3~#wi(I;5p!c!h}NQY$UAK zz67mWqmvwX;TgQb+QSNXPG}Fw%BzlWt#V#OIDq@3*3i7FC(*oH!+As2NI-1zPPDwX zkXjhkc?uRVn8R(YL$}bES2S)NSCzyhp==XOji`fzCZHvCLsk#;T)afri@)l68-FA6LgY{6B_BL%gM)x_#YGh5}_9hvIphGi4?C<4Qgzs zlk)~%jvnl+d9jz{#jbV`=FDB$%}?@t`RtH!|gm$I{ssDFr8-Hd;oGTKE3df zh5meAaV{3~9Ne^lqd&vm#C;C;d$>Qq9e(})!MQgg z=kCI0{keFZhiK$>_YCH?=U&*`-bZfRG?u9jUbx%-EDdIz#Q zdIz)nByiVIM^{^JYj1ZSHVy30ZRzjM?&TDD&`Iq7e)Yp=Yi+~W4in8NC%{%$CC6E`vbQfo8`(VA(KUcDkWFp= z_LiZp!KT6eeSZ4ccpGrA4Zd)r1vi1fK8GM8l-x#P!@$}Zj1xtot7Cv{7_T~;dUx#! zb2bh5gCT=01D#G#_Yn4&bcg{zwN+HC8 zdvN^)8#mT%#A}Pl>H0`}>wU}Z`9Io1U>DFMooGUNp@0 z^J^|jFPoDwH@&!e+qO%$%%cW160 zv$azUqWtgAPq#nBSLeCu`T6Nq{5T1i2ExFqT&n2G;Gd=QCRCcYTxQO^8s`4GJBwc# z-h)gWxoj&kaoV-@f@_L1mgZehkjtnhD#XyXyyB%Ktc>jesicv`+csQa*R8jg z%{o+w`ML~I6H1G-Ywh72+g&sv{a4uT@|67bk>4!NJ6)Ztul)2m>9{h|ugUoJn!IbS z+lXozUO~Qqa-WwwJAXmmvf?~@AjgiISk})(nYnXwGfL;CSAX-O>UC=tTw{CIUsR1! z*}Vb(?6|EPF0wBvvXgR(nSQP{eS8AZW=_VOHMv3MHt2TYjm%gxCnF;bs^xAMWpnd% zsdyvT)L7neVxEk4^vumrs`ljANfT9lAsR1;=Ggu1cG6b#7~3!2u;KAyyKlXnm@~no z*SO8hC7Da+569IA3oWT<`Dh-Rs}nGqAs>HPGMN({X9bU`KC{Gazj6@5h+nH_ECv zH2JD(sv6cA+~K)tPyt6qqpi0U6O6D3+x#v4Mrn1~=8f3Q2;*22M>eCc9J7YjhCY95 zM|(#r2b#$g+g!7uruM=bqoS&zyk_&pGHiv6U8}o-V^{;!9B66xS7AKbjZ`r`>2E0; zYOif?$53iuWN#Cv&UJn1g-sQujipT$4ULjs(5tkuzO>wE9qMoD8R|Ct9lHaAW%&GM zLuGw^Q%$Xkrqbzf+4)m5VA4`D*H5&}?j`+IuB( z-hM-Ekj^48g5?t0`W>6tk5f6MnVxSMeg2-drry2@jjOb4*HCw9|DduF=7vVkP*+zI zH{R*$#TM(((&+Ye^bZXBxI4R4?U3y1-d5dQvDZ7>v97#THCsxnsw;F2?kU~78`YYd z(NAXXx;pGT4vQ&cIUBjdkWLSx;ME;ODLa+jJ=g-iHGm$(DI(n-H&<6T)iu_u8eCOf z+E|61(#tEhqVcV5s;=BpS*=TUlD=*ucHW=VyI^wfWjaq#Mm;DrwRWLxDJ>g&Sv#OQ z%A;;@!02;I4;_#()Dy1!zJ4^FaXrzv{?}KHCIG zGf*eh!;DeckH;ftqkTPgpw6pKX+kH40S2A+?tDWPWAG#d2;^xGXhwcN#}Fe_bQP6l zo7ZZYjsC8#phB#N8=C4WYbu%=Ha8F}E5h0(MEC2VsiLyl=&A1=8syqSTd&>+KR@Uy zFm_$i)IG4fNu}GZ>Yb|aJwB{zs643g6Q3Y8vUhN@i3}>XP=jmi7(~~wufL_Q37t{T zV7tNRaV0?;mIEc*Up4I7kB$?qPW9O=Kx1HNKy?GE&t&)so zmRQ+?CoR9~Rq_0)4k0ks)@=5c=jVIXT85WPF?r|bpYP4W3C@1#waB}@w0>i*A43hE zTiddiXYcB7!8q^j_wVf(z%cJED9kH7f5pY#?C#dSp<=8JV7$)mE(ARozz79whktwD z-oosGzU;RA74+%qZ5hnQ`cLsdZ~I_-pErB=?l!R24(JlhM-nCN^JxB692h#2j?-5rD3s&CEi>xD`E{qWRnDVsG2RQpg*D?85Y0jyU6 z8I65#I1RB_rRv7irL(=I3oUk%Heb7Tt!_F!s8|~-F)gU7*g6>_hxLZW(wd6W`U+zg zN7I&FDEE%Wp&tLhI*z^=2zz;KhEZDIScSQTGi%sbiCGVOiJq{|K^5*U)|lSOQY$TA zW+?8uj@E`t)le|N2BOEVjT@U-se*%-s;O)lttjJuqeIxEJh9)c^={vK@5mu1VX6EeqrK=KK|$a4757S7K(fMfVV3MI))_xD;x< z%Em^wYSI(>EEITMeXYTPPK`T8x1;&TcFaa6jxE8!ApL;MYgjPYd9&%`rdCF`j0&p8 zuMCzu@-0KT?(i@Obt8kEdck;A6}3&ld8?Yj^bX=I7`6dz+J?sZ%F>NhHNIK{b$S_Q zO8&ii66Va1>oF5UU&P_MyJfI7fNH{=V!}3P1P1(F?b`CeX$Q__=|O9A=4*yy4|UK8 z4U6GgSEPo@jiG9!I=dbofy36wS^@iGI>tHQq_gCTh6)3HkDk;*7pDktJUd1srx6*Q zdP0inc~e7mWo2F1fWQG%O;nwsM%RF^2}p3j7>kbXN^*Y3agFf1K;9 zIRg|&KPe-wdJI%Upt1}~!pZYwJ%iEh7(Fy0G5l(5^x@Z$mO-aPEBW-NB%$XtV7a{o z>ss1WA+%xehhC~|Rl+Dsm=K+UDpu`m#9U6sf9kFI_+v{GCb~_kbvX}Q7{`Nt!G1Z| zClBH23u~n9y+-IcT(_sTUhEm{bmfgz8!PKWZAEqRYF;IU>m1P(e5@Tm7tvf*&eW_! zOs-qTfHJ*)%$nHe;Q?bKk~T2SYlAYu5^dRo5|y-3vG4@l*t?REFXZ%f;6^hR-zAf&RLc4Hgdw1!wz_OvQ$^LBjo7=`0oy(EP$0GM@Rd)UZJoG|;W7k_QI+Bd6=C z!hVSp^x`st^8^zd1L#?xAJPr=pj*lLh+j49kHlXFlhs#jGyJ7oB*MesfNluAL;ZM~ zmk!nFsln-66&?NT^zk^!R*gv@5)oLkmujC$aa&x@D?LO%haITf4y+Xbqqnlg7}i*M_8-47T8D8@mWU`rE=Xp(YjTu|;)odZ19_txhypC%J4ii5^Pu77hn6 zyzV-keiO@V7(X{O;qk4q5lvdniP-H9;6V|y70wGdRS(*uX-j2&85XcmfNG>uJr)eH zwz8(a(&*iXG6he5DbT=%wJ`81QU6iAB&)|wejC7M|jD^8+)jhGlMn|Ux zV)Qaxf7+{Nk61KBtn%LfxrB@!;&u$*EGguC}_Wd>a}^Rj6tPSHr@3w4fixKs=Bm zY2W6WaxSYhH3gqjgVTWtC9S6J7%(|^7`IT?Kgc=EAfMrN-5l4UV^N?0vt}+~4jMf) zr0TDf{#-tUuO4nOq2pQ@dZtO9`qkLfZ|ssatv>zi;S^`6zSm&ps}?wF8H#c9hRuHl^!!m(KUKaxSb#-+sb}^=5<|y3N}r)td5j(=#9AB1ipGX+gcG;!?k3eU zHgSExXbUYfI1Bl3j*FsSrG7|7PR6Cy|;g0(f{K$1oZ*dhq&;=)8%ooQ`8%Y z&I>+#xu-l1=XI6GJRN70dMfajZ!kI+U#beH`Eu1fClHv0yEHE0TGw^<_10(1jWIv{ z!81w_{KMn%9kX!S3)*?H2u>w4Opd{D=i-Z6WpP>8MqL+qeZ&o}1NJA8Qd?YFxIDhZ z#ka|V7hWPLjZ3}Oz7D+Bt~m0Bhw~*bgkKYRZNzo1>+KuBH7?B&6+SH#3I#5Wn>H@* z$B;%@T-tcO%Hr~@lW}R`c`H$}f;I}i5C(7cZ3gue`_;z8C6qZPggt^UlaZG%Y?a1E zUrSwScjw{@Xk~Hpu617*eSOpokq06^>AKPW6u8RbN~}M}%?r;rUs4kpW%y<8!Z^G& z5N^k52oIOdm&=f)kIJ^b9LIVr~}SpL!1MO`0xL&O2sC+!=NhP5}&Yj~Z+fn&IlscR#zi@4r(gMENJb#bZV%6$cR z!tA{bCLUL&2b7FS@_XfYJM!hcFgcqkrO8M;5EVcgv z9CF#%HF!LKym2SO$j?6(!fC%}NvjK+D}`M)-pm~zf2w?vqd$zc)SJ7Z2sRGy7|)+1 zpOYKL2-qxyGrh}$QTRDo=(3IJ6XH)deuQA^drav&UB$miWq|Rf^H1>N;j-h?PcdFW zDA`MJTL@?RMY#2q;y4uAI9Ymp{22ybbx`s7BCn-Ro(@H^v8Td#e!hVmx@@$D@!@F( zznP^z?1wFcQ=eDqp{`KPB5C9G+wt+|7#R|O6yX-a8UGP7tBbGehA!LiPKfUf1;S+; z=@Y`!ja-q>zT84M<@3uYeWg1Ng*LvD8LwaFgz)(j!sjb_e+qK(ley4k8w)4!FAN33 zW#h}o@#!s+kF#J;rSBpopD%vvi=Rk_E*oFQjE}!W@!uO{SrT;7VH?>K__HU3=S~RE zRr0P4a^(hHbl67z1pfR9;meE`u{HZA(^xhkyl?`&T%{NCTy6|V{NpPA@=y?5wsD?u zm4tIlH9?18Ha;4?S;9Hao9ZLd5QlA?A0ptg@zv^G!mr=qUo|0owec+p*E_?ko)8{t zi43sS76l;|8>i%jU$#*iPJ+LD0$n+QUaRawTk5OIaVWHniS)V&;Twz}ivA3soL@|! zU!Fj}GlBkK0^=YNUlpVm$YubDu% zPoOW8_^cm^hSxYGbe5tsjW0Pg5=q3jvkwT(IZPtj?@6J@6urdwSD`uGNkn`4rO*cy zool=b+PRp&w8L?rb*>8yIIr#3}=G+ivts1Wu1uUw&D6Py*zpc27FV2LvMOI$&D z_^l?SROm4_ER_N$@ku4Amk<;;Gzc!;hp!prtXxr$$CnPU0+zG#ycLueT2VkKen}Yb zbKdfaJ}Z_lo5)j;Kkmo&wG-FJCV9)jS%=ZI|xc zGWf)0Z?JI{(pjO^kM*G+mnL1mhpb7L&~>$Q9^W02PU%i~$08Vvg;)Ld1st+kied<^ z9XEuzC3yS~BWMq)xMT40H|HWTnCj*UT_&^($g@TtAdT<#o&{RFaiWIcsNi#g&kMdF zI41bA;H$)8!{B($ZH~`~|J%fi4fPQa<5q6!kH`;_9H%qMjJNV}AfM*4RMK1944 z&kT%z4d#DD4AR_;!NW8czz|qo}8ai zPcQjK1d(nQY1nHW5p+Ef{0E35hVdC9!jBUX{xA{tK1m$G!97Iqv)+*Y4H4xsMl6zg zf&8d?f&8d?fqb4rxD|lCh@h#rvKJ8(foNg^#y%pZND0JgSRWv!$XpJUDvdPK@e*gD zei0!LtV++Bf;mKFYAbO>ZBnet%5rQ1A<+G z!-AI!9uT}q@K(Vig7*sECm2cW7Jd8mcc~VTPnpCS#6AO0MZimB4Fdk0c0r{-Xe6opQ56&_z2OfNEBZFUy98CeK=?_aekKluXPYM1+@Fl^&2)-{EW$Scj3i2pu>QO9ME?6a4E4W#3yI_l8K=3lb8w3vt z9u@q$;I{?8FZeUT-wK`A&RriQu0F-xG|C(BarVTk*{i#D>s{ULja3xLt6!;DF#&f}aw+L-03(e-wOE z@B_gq=s2ifrr-*}GQk?biv+s`hXt<{JSccX@Ls`(1ivfzbHR57T~Rvy^93seFBQB& zkSD!U&o2qyFZj6Nvx2`Ad{OW(f`1ouM@xPM^8_2++P_V3kDxlX1L^XVJ*I!T;MIZ$ z1V1Bqi{Nd7#{};dyif1}K_1sfxlal{E%;->QNdpcz99Ib;H!dv7JOUqJwYzpQhuah ztYD&Gs^DC~C4wsjD+TKXn*_TA_Y2-2c&p$Yf?pQ=n&5Fkbz%ne`i{`w6MR%LPjXYXr9o1_bvCUMr|hvqk#Hgnml!SAs7J{#o!n!N@6E zpG3hqf{O*O6jXpBMbS;Az1(1^-LXjR`32IbASa zFkA3^!EXxwLh$#3e-iw=AU;Y|@}dRf1g8nk6!Z!%5L_x)Ah=R+jbNqVX2G3;y9N6M z_X}Pv_$k3#1aB9-TkxBLj|hHO@W+C`5PU)K6~VsT5zji zn_!>d<$?zUZxK8uc%R@Sf=>&M3ceuts^Hs#{P~6Y@!)%6s^C1qe8D0?p4>*hX2CAO zVZmz!4+f3XA5QuE)vWY zTrOB7ID49of1Y52V4L7&g4YP%A^0^x4<`6bf0p0^!PSD*g6iaD@a+})X2Byw%muzK z^uxp+obe>|4}|`S(7zJ;w?xF_*;P#6O2&<`bSe>a8WD2R#Xnd4&nKc!Tqkrr5q!;p z?c(1@L>sy?qripr~L$(u9UU*g~olb<@T*2ky ze*qDBs}XvugtrTJO89;Wzm|xHtV2TIL4@49guY+sCj`Go1pm(@{5Qlj!+1^Tw~2^v zr)o?ROeI3^Orf)gh?g%|DOe-eOay-?aV|E=5&AkJQ=BIpHzD+J3WyjpNG5%KpEq3?}?-xPd|xB%x13jG`r z`n)XomS6-HRv15li16t`dxf4abe_=X6Crn_&|3tX1luHhQ1Ch;06O6~25dFPG*ePG=)kMgzBxYmNBcXQ^A-_xT67j!G=n?MM4ztC3- zeL(0>6A|xAf{ziA&c8|cZv=l&gq+ue{le~AA}LZ1`#c){O<@tp`cYXr-P(7T?9bhnCsKN0*_ z2z^BG8-h;?{!s9FBFg0tg0B;y-#H@Ei%!=#Rd7BL`OhP+!4X12uO~u}TA_Ce4vYU4 zLf=5#gLQl&;@>0qBoTW2Na&XY&xrrKg6sJbNDQDqlJFbF|B&GSV((4htE#TP;dAb}N$$BPGdFMp0d58$K*#_AqN0!y z0-4Cb;6M_RKmwV}061&4YL!}XZpBv|TCLR<$Eww8oolUA)l#(;hgMsMI=0S!|NlPg z+w@nP z!S}hOPnZE3<+kB=+{DDN<_ZhLSHBJjY9ui=;sArBqINtlKu}teU>dJ zmWcETMCdn7;6huBJ|lW@ox+L zzR+I?&5hET&mw|o-RA0oEld?xg}lKvqP za=wsw|6H4%COBWPO0ZsVqu_~x=L`Odh0Np zUj+Xq_<`Ujf?o>abr)6NI6+G=RdAxYc|l5YwAUGO8puLO17=2OpK!LOdb0;frQmf##g^*jsd3xz&Juv~Dtpn9&Ai1Qt3 zAm70w(mMsQS8z~pyWk0e>Nywqz9Y1H-Ua$1q1AIQ&{qnro_m4jyK$6%kKp}+j|)C2 z_`KjB1m70?yWl?rKNr+Jwp_kzM)~oA$%2ywrwPswRL{@AUnq33V3puX!A8L~g2xE9 z3w8_k3JwdZ=V_3Cn$X`Cyjbx2f_%q}djC-HM!~&;_Y+g`+*?pRX9N9|(0>s8qu?8Y zeAkTmJ{SC2FxIf+{emfiX@b)QvjrCr)8L^~@Cd;&!5YB^!L@?wc^&d~3Ed+&C^#Z` zrrp7$aCJ)u7o{7mpG z!FWte%%3c%o(F=SBJ>==e8EM6hYFSoRth!~aSggoP(3dM-68al;5I=z=pg@Tg69if zB)D5}kDz*<2>zc4eYfB*1)mgrR`4&x>DWA9@B_h51iuk9Fh`TmFPJD863h|I6D$;5 zEO?k;rC_z-dSVvVAA+5N-GT#x>bWKOP7?Yw!LtQ-3SJ`k1Hr2Ve<=7f!CM9I7W}2) zV}efz{!Z}sf`1ZxP4KUR?+Sh+XvEs}@(U&krU|ACh6Hm2=L!}GmI_u1)(Wl`Tqk(6 zV29vl!D9tS1y2+_Rq!0a^8_yz{J!8dg4YTDRPYwTdj#(nd{mHLw%M+~6a2m4p9EhM z{Fpcs*WZHQ2pVyAJl}C+dZJ)ZFhej~aF*aA!9xX01uF&X1)Btq6x<*Xqa}qUocTHD3~FbEjUZCNN}Ox z62WDHD+TKWTLjk&wh5~D1fZ9CF93MF#GfYkZNZ&_=L=pcc)8%!g6jPM$WiYD0Pm6b z2L&G$d`9qj!IuU13w|j055cbmJ$~C>reK0#Kv2C0fO0d1o*|egSSWai;NgN5f-3~; z1)Btq5>)REKz_f_LxSr40mPpq^cjL@3w}@VLcyyAeuOEx1qcWx>}4-xO5uT|oYaLaX;LKz}WCLV{hd6v1@C zse(Czxq=0P3j_}pJVLNmkdC%#uXTb)3w8)@7TiX}bBp5zPZRvM;7-Bw1uqp;?|~rS z)k3THK|tRrw0a)|^n*e_D)_YEbAo>qd{yv$;zGR7Eck`sH-fRawqm+pFd!HdoFzC{ zaIxTFg3AS03APG$2yPV|5Zo?!g5bG=-xIu4@N&Ux1g{hPx!^AZ9~694@JYdE1^*!U zN5MA*-xmBr@EgHc+*nce5KIwF6PzJ9Td+{DSnx2xrGoW>O@bSVcuurQuut$)k z!8wBYg6cgO$f*#TPSN;WXRY7{!Ht4Ff_;Lg2%au@uHg3sFA@BK;8lV@6ugaChH)c! zpWuUn&kDXE_$R^F1m71_@6|w#C&iW*E0{s7z;j%|S%Px~=L;?pRPWhb)h%NfbI&FhejTI7@J@;NgO$f-3}T z1X~2x3$_V%3T_n~5ImK*5>CDZ&lUWh;3a}T5WGt8hk`c>?iGAM@Dah^3O*yaPw-{I z*9G4cd{6L0K`-C4f_^^1B*6)SlLe;<&J!#WED>BHST49+aJ68I;AX+CfyI_~#cEJ+_ zPZvB(@I1i_1ivqMh2XV<*9-nk@K!1h)wG3yumNCwR8tPQeQWFBSZe;0=Pe z2;L$1u;8x+pB8*hFqY59dT~FVh?Y^J5PM!bwMnro)A^0>A?fRVH zJ|gV)B@;1yKQZP6dpb6a3C<16I5 zS)lqJafN*M3k)$G{X~8D3(O^r{*otHNJPIW7Azs6|11$)MnpfV5L`iAXFIzjHjqZY zY8F)Am7)ET@ngFc5Yb-CiD;*dM6}O#BHHC5BHH67BHH08BJBSL5q8hOz@)v`5@F|W z6Jg&R-j{}5Yl*O@x}OgFT}K-B%R$++o4TJ2JE{A|u+Lu^54*(kekANsNrXK%6JZB+ z{}c6pmo)0FzE46um-s=Wo&mPYh|CYru~=vnt?G*+R62@K^==j#B2@iP6#7&m?4bHF zseXj~svp5#s$YOs{Q`DV?F?GA^K5A^(5k&) z#~o}p&?gdMPxZVV^kt-B*ZU>?AtEZdPw1D4u(SGpAN=b3eb_t4gLu%nMA%(D{{~$` z8pdgp^iCp-v_t3UG5b69wORjztHM=G}_6_=cz28i1x}6I+qBetLMl_ zSI?0-|K$%K*EiC>5jZkrW49B6%ZCvyUu|HSzi}8oC>fDo?(BW6_DhM1Q@fYgaglvR zuqVZsy;H_Xw>Qs_5+XU(CMwbFYAcoKNR**ARLO^j3n#6oa{;2&)+$lawwf+hXQvJD zS666J+-l>Ms5oZ}7I(Buh+UuXW-M+lXmt=D4C9rH4O;B#!0hAhLgE(fC=l7m#hr?R zZLQ}=M2@=|NmQnTXsfn2U^HIiZ%C|vFhZ4QzX8{3SZ!VPYm6pXc^ z{Gu=!RXFy&aTv#@EXMvfsy>}v8%HhxoWj&mEa(8HHg#m;FZnQq zsUwMsVa^gRr~Yy`V$*Ifo^;-gYIMpk8SVD#6 zZHbci1OzM-Sp-MkrYLzAx$29{->|%0QS#!k{+r{H=h*MNQSx@Xm!6|oXl)T-LN4+U88$UKpUxMz#IeM1TO?!y+P{vuf&zlf6e1mp#gLV5G> zbL2f6C6CXL&sGxbKaPG+M#*~v@|J;x@(S>CsSwl;Vk;#?7qyn>=dMIqNww6JJl;etiQ#l@je zp^CTv78MpQUc5LIDk@%3G+)KH`A?~=snT^v5_QZC>xtsEkUfZFib)`ChL)w}-VAfD z(sF(G1*-;^>njGAYxglV`F3v=(}FjD^Utrp(XzAKlJ6b7J)vr1mA7heOngyQZWVb# zZCYXQK0P#eFaG`8oD*&jgqYh~)qPj;swq2S=T6?&mYr0sU6`~oY1Manl2$M^d4C{F z%Va2iRYzO=s%!9j9lz{$Ei1k{WS3v%*_(VXb?4y>?KGPl^OtS2`1Y78+@L=nH{Y`L z3!85ab^BCJZCUzdxovt#+aIbj_7>jjT#(y0JR-ZF(p|CRK`iXOY{u}Rp&7%WCBPY@ zM;scOa#V5QVGD}->IOpCY*l$7iIS5!<*33w3^~4TX!kI$elPToZ$(qy>FOh_gh!9X z$kPng!84UT*Q5L9gWKbY=~EDQ6_$1$Zw$ZUF+RZ`Pbn|fJl;6<9f&XH0aCuWPta|> z-Wd3fVOjw)8HXXo*mt~2_j(W$<3$p;C5!c5%~(A)4Y9G_pMi*DjPV9&Z;&e-<9%G? zdJCHvoQM$+6RJeeu8a+n@85gP*$Bs&XR$ENScl}; zWL3gX8I!_JX`H}dstR&Xv)D;0_)i8WtKfE&8$0DT@cOqR))(g+f*`955pgDi_CDfq zeullMNG;G925X$fV7yY%=i802`BVJS%*&yit{It-?6Xdf@FYA-9xjtK(}Doqd=Y;= z=8q9!nEPlJ&ET8ez6sYwa;1fk!7Ng_UIwRbwt~fDu2Z@G$z0}q$cZs0BOGgTnI2~@ zh5(;ttU@;5^gn{|Ux8r2H)8}~aXT~L%pHi0B31uc6!`8L<>b#lQk8ln0o`MvE zPbYly<{}vDr4qIc{XX8MHPcY8W|p(@O{`o|(+q?Slb?!s&AT8i#@vONShEd(<1~Zc z2KiGSg|vi0|AY~+S^Vt8AFy>vMS?%bXs`8MhNql?aJ zC5dpXr}^`#&oZly{6(iBTw(nMR`D0p_A9JQSk@w%pvD?Ss(&%NORdFr^B?+W=+I!j zh#3Fj#VDoON@ZD1)PJ3IKEurnA8qln!@rKsfB6#ZM6YN3x@LTU)5Z28MgA zGw|2n&hQp%4@&X(Fuc{OX5Os~_gbr&x0m5QEs5f2!ep$U(a&SOndH11M4siNt(3}^ zc?T5HtWi|R@^_=60cB3hB1_QPK*o4>(+rDWfmum3XGj%grLw48h&~5gv0h5@=R@b* zc#D-+1;oeO1%TG#6DA^az-j>zpU7~~x)fRClPNSq35{1mb0F?*q!`@U&)=7Xl4HHA zkS%2m{-mldlMqbEybDw!-0f-!Sqy54a7V8tWHaowUPa!79EN??JP1pe!En5l4`~TA zPXl}Mgj4W0VHU$)eR^O5A9N(-Dsnnj*RfvK(8yry9G0AroS0xhti{z|Vj_bu55q|+ zlf^G25|bJBSyPcAF@@oHNTSXLAFw5+K8^%GzpYx4n9k9s43#+9HdG(@5~th;mVm|Q z9*G$ySn{kb3{Rt>mMB9dhNxSGW?+4)C1wV|6ze?{*^FPI8<^&CD&iFUP5B=Fq^duh z0>H*G?OP62)vTBtV1agQs`LUJ-IW!haL*2^z-()gLBUdtpNT%+o{`6zK>rAkn}2a%Ir zgcf=|Qw}9#toL3-1fM_!^ioCy@Y8qyEu6EIIfwDHbCxsbE|BSo_`|&nwX_#m!dzC5 z#~l3h-Tg4BUG!WE_yv9w|IU=Ttl1j~P3Dg6`drpA0i{p6lBx4(+eIXmp7|`T4`g~O z;#t}h-nKagfegLDGm5{fJ=+kIb~1HH8>_>IVI9(_1J59~4rw$Kt8D9#b{a^oEo>bo zQ3rk%YwHlC4lN+Z>d<|F4pSeX4(rLzV+Vfv?rTg(&QVM_7cA4blk2UBj{#y|9cZZy zLmEGzL9O%(ZTd_^os6hnJ1WND!Y@N_jN!+I2sGJQGdw3@?bD`B=k>{Vz<-9~U#%B{ z7jmYoL_Ax9#u~xj)iG+R#Z+WT;=+fDsOfT&7Dr{LE<+J|pJPd`h4kGMA$xNB&}Jmi zk}seZA=N+wtdwV0cjz1P4KmTQt2dq9FWg!T*b3n0R|ZB$(o!xKk~r}eAx)Kzq#Iz4 z)HHrJlk{VxFGm2&ITJ+|ASNYEpOwxSoMEZNgotd*O0eQBW($zX&X|@DRVa`VDSB$`vf!m< zO2EXJuvUD5Y*O(UG8oA>CRh4*zNKp^dJ&4A3<07m1x!H-?8}slbHL;Y?kYnsn3}II zO+SbV(^zKpgyM7$7^ge=S-X%+8>D4M$ffB^u*%NLL~CZ}Yoz)UZSizuUJQw*>6k=}46=Ph2tB7wlJY9}BNKJ4yQcqQ?mQkd1F6>{PV9~{h(Oi(ka)*Mbk zfKw!M5M7+?v-()hcoo$^$hA`mbL?3B4IjL;*R|CIo3SOrxH`;*fX&z#VO$eoY;-en zhNY6kEc{R%0=YfdP2b5CU2}|3m5q3C?W1!YoD;=DQuwgd{~udVA1cDS6QhiZmZwb% z@I##mtT2pQ8+ot`@oA;|IVNL|{sks8+`hqC70Tk&(Az$;2_D}I`Z`7nb_|s)E-Dxv>L}=-i}oD_ zgY83byIw#)sRelHLHSE9=<6RT*i^7~U}$SWVNpK(n{I(W>w?m?1*N?`8{0Rw7aTX* zKQKJ6XJBOBpd@Y_?dk0-z=Q8WID;Q5XdmjE*AL(E^XOr5-on=6dGia479BfZLE)nL z^9yQv`bT#xDjv&O(9_@1JKEV*(AVD4&8O=Bt*W4M1)DoMqEuR#Uz}g$DE8m0&=@N^ zDijPKhxZW*@(cdshP7MNHs@IO_Mwh$){(z$i~CfS`-;J{wQH!qtG8eXmLEV<4xvX# z{{Ply7`N71^bImNq(bj*?d;mrKH59dO4rB3`Q03Z!y}!DMHr(2-jzFXX9@%c2wuCP zuVk`eeFFc|Cr!`uoz1J3i+MtsnjL8hOX&Ko>SMN42 z)=O#zGJdP+fvlX(9xrAAC+=68o>f>_;DJlhaNJxyR5ZV^plFq`Eo3CE^ysd%G&`-Z zW}LJ?K-Lk3xFe^>u8h5ERRNBpun>s|epk~&IR#UTW){vrywDiQHxfb~57L#h=8l0; z+$mhu+E7^sN9wkJsfcUxipsK<kq&oLtd)cL`!`zs;*# zDqAWeew(Egw2fgG+idQzH}r9QUiEivw;Mq@jD|aPZL96>ekAOVymz2~b4dN+?atwz z&HY`SA?0-5)}g7gHlo46NO#we)2<`^8gKKq4&wbFszk@?Bd&@%KG`Ea;={#=TWXi& zjjeE^J`i?dE>5}ssEFTQRa|;_}|E z_Mx#J$A?FURW0n6wwr0eeAE^_wzL;7SXB)T^tCisLCPr3J@yM(TU({Uz^9rF4L+`; z`|jbc-c7b+dNwH7Rbg}tK5Xm3ksQ{qv1?>>s2>##$H3DUD-!J_TpC4HyQ;dbx@m=` zT40Rx^@H8PcdVggqh4$gO?4hy$bpjBp`}~(jCA#lanp_)gRRFZkMfGk(G-&$*cvO= zqbd=vaPY+rKj+=_cfYC?owl;MvcfLf_Q$SfsaDE}wtoo=t*NYRaCwODZtw5x?b0^4 zws(y5Y=d=~@4qnniw5v*KZJI#Sl1dE;?3(DDwUN>%jz4OsY4|+KENs6NPADObb#jS z+RDbrxS~V0Z zRBBMEfytSI(W(n0i_-)hTeVHS1MP^Q(MOzGs_Ib7`W9A*Q=7}Cb_>{wQ5Lffb@jFP z^dsEY)jEvf)`ec&zO}V~v~P^7zHsMLbIiD2@>D*1sBPWU-aFi-ZQTez_1bVZ99oAs zO{oIapola=bzN1xUHZttNPDk)uvb+hsdJzMs{yT5W(dq)%j;S~njF5(mmR@5x5EQn+CTP&l?_`*IBfPF*No(EFljc9@sRpX)rWz^X5*l z)(_i1oDUD*+&>Dn=56Zh8Xj)n+%*q3w>FP-A6|&4{(*U2J36`sM|uYOkz><9U(d)q z^(N-L!2xwIL&qTs9aN7*Bt5R;l^hpZe_p39Bj{a z1FA||Utg;_fwpq?lfhEk(ppv$X1Gp1bS^HJuB zuoA7JyZC~n=<2#Pr8U(RnsZLJJH4v3twLE#Rcq8Kdh5v0D9*@O6fqa3O{!&|eV`Y| z->}v(iZP8*VGmw4AGX>ZeB-gLoG8amk;C>#9`5Mw>J(9$b3Ph70{hxWI=XSMtGxWs zP%b-nGe*s@dajC>bVHSW8@sTK>&oPtzwEK|NictWVLWd9!%U zIrebJc(o6Bvod^OF&YjtV8|u36KQY-?>Y=D;&G44b$GRjpeTiP@gSYo^AQ{J22$iO za2IH7{#f-68uK%cN8KfI^ZTOsecB;NCO_NYK>zjrD1N_sXC%V!j|fM|K72J8F@`z}>ZVLed_(y4&VX=dGN`VT^f0oBBj7M=ttyZbP-sj5E7;mGOX5Ceu5P4mWAFzL zJ2$GXG>sZJ=mzZB{u%b&ULszd!-MfCdX{~A%s<<{6X%~}+mZVrSCJpxlYO826*m*- z$+xhOe>7=yzcwQ1P9pd*g{yDldWpCNKS;zy{0I?z)Gw?L^y0lc`ZDFn@`UVrvqbiE zBC;qSotZ>*PtGTdhbjvB=#F%$;0nQdL8Uj++l5y8fo8vDzLNyMCHOtT%LIQYc)Q^J zg1-^mC-_IfcLhHZ{7jG&Ez65RXC=l9P7>r)&G>nOC4%LGb%JV7Uhwffck&-6c)s8z zf>#LMCV02t{eq7O^4TBd(2qOuZ-Va&{zLFfLEJb{eCiuP@Fxl#5LA2VB0f{-Ji!vd z3c&`!qXoMKPZiuLc$wg}f%v_t-*1-k{$737N+Out6(XM(>Fd{ppvg6{}^D(H=|^Q&)xAtyuVTtT&WE8^9+ zz`)Zb{(FLa-HPSkAb7u^+B+5TuL%9N;6DY`-l#}dd!hm}F=0^7Ji$W*)!wIwS9_iU zH%R<|;3a}T61-9HR>5Bi{#x*PLB2)8^4=BXOIf62FsBjKUZB9^NkixFNc@Gw419Yl z^wmNuACutc(_0T7GCwHkzm@c7g??G+SBcf}wA#ZHbe_Z) zN&I0#FB5vD&}#2a$m91Llsh2lYOl^DymKw|1;VfP=tQ|!3B6bFcHz5E=!b-UQt00i zA@60uk0kvoBA)uGZ@3|MBI?TeOd*1vLBx4x4iS8Z3#z>|adKEo8rUX$eUh&B$^`#q zl71Hv@*X7O|)f~N?cE_jaMd4d`a_9qNKg*cTegb5X@^CHrx3Y{aEE2z$kNIyd8D#4Y4s|8yG zTLn7=hl#MocEOVcza_X!@M6I$1g{djUhqaizV^cYb)VoPf{zP6Blx`F8-i~M{#{U= zS0U$fp}!X7i!+p;A{Z3R5X=^wCCE2x$bYC{nP8P*t>9|G^@7I;@>Lw>KUPqkhe4kt z^cjL@3w}@VLcwc@XiUCCO5{ta#9IXK66Cv8j92Gr;L}3?UhpNs*989}s9q2T|7Swu z;z-Hy3R;3mf_%$|x46`+I_y3SKUFwcw8gZxXy&@J>Osw*cfkC^X+VrCh$K!||-v3&2-}{<9#abqg@%2#{ILv8_S$XP{%oXte!JDUi8RZf$BSOwP zp^qj)-iXjUh>&}}&=(O|-~Y^>3$9HH>`Sf4^_Th-TkXljsxdF*Hs7mJi4g1Oi>sE=)9DljURdVBVB`^TOR2Ee$L_4 zV+A7I@`_#ZINlw3od~<-wSk_4pL00!+7RKEw*>MyzNsJea^&$`>6Uj6=<(zok9fDd z8kao3OWvhX@@@mo@#!2am&b+px#b-Vd4q_fei-Uu{q928tzRAp+TJ;w`reKRx4b7@ z`teQ|%VoWv!;kWueCt5C+m~rP-ouaK`6O@*f~dEH>MzsO4}XZxaUTfo)$JTk+E<8h z>-RGBqbYvrYL#0LLNUTaSlgbeU!W}A&+C4@;IkB^7vg7^DvFaI@}EA-u}+PH2$1#`5nHu zr`h*oqWQLqJSYBW-2b`WA%Kpm!{BqMxBbg+1YiEXDlPnt;6jYT^6txfg3HUhU&7xKd>43X&$64#yN~L=Y?M`u3^HJNceBL7oBfI_9$ece_`u~`=>wNveb=b(@5EPzTFBUY%%|QjoOX7Q{b3b; zocku+u6I{&NxvDL=AyHgD{B|>dq>vy)SmdNWj9;1^{3-!8_$$?YgtoZ*SvNuRC4dU zzrMdbw(3;W=BTY_?zL{dY`MXfD#Wkok-y^ZV$Q6p8C83`Jr^4n-}GtnpwaEm^8A=B zXJqwOd#?9>;Pi- zY8I)oymQO`c7O6m&-mWfyjfMRPui<2d+nPg-KkTKZ`0G2 ze)x*>x1VZz%BxmZEz7hy^z4t?wcJ%t-`bg2uM9ncQ3(AsmuYuA>c3k)T{@E&& zRW%p(qYjjM>07ZHtrv;q_od@$=DX|tww(R9_MCfjn$pa-@A~YC=pPeTPDKBhsATU| zBSXu+wLM;V9>`7rPr}Ls@FXf8PxeP`Y|+@g_rCT1S?n)dS9}XU9uMR9PZf&%xfns- zN5&pxoeQ?jR~u#x;d$xCF0Fk#-bOkOZx9^pGgieGs;*d|UR~)L3h^O(sAo8YN6VqE z{(;fW-TC(Stl&AQ6uhw-8f1Z?O?X|Tt5dR!4)tg~eS^L8j@a6DTu=X|0e(vBKd2P@ z)u_+_UL+DZcFCoi@a|ox1CMrw26}r#Sd@3Q_vHccQcGxXU^oP^ouKhPiVFHQr3?2~ zn|B0WL$H%=MK(db)`3l%@Vtf52UiBBtcN1a-6*V6sgs%MeAyc@{l5&U7OC#<8PT@P za?V^{{R2z|@!!HZ-j|?hr@DmTJKlfd>}ed91?Kd{Nb&e$p2x|==s~o1GEXl?CC;1P zjAjJodtc-32?$T!M0Ri7BoO#|I1`te2Ddu*hE8FEC+>2b6piDtxbbG~VvO#41*Q1W4-CLa=5$jGVq?C7Rd% zz7+0YWIP6Y_)=AHC*=7iso+mwG2dhrtcQ(!Q|L+2zY4YXnZD-{wsvNsM3X_ydXQm1 z!~EiSFVLXJD12#)V7yWhb1cHdn+a{%r)~}w61P? z!S69kRh6>PMexP(%V-n-1V1<>SK8^!WuWq!xe&F{&EYKOdK}^nvx*(T{2AIJ#{3A& z-B|N<{Eah1h`|@fPoZ}H>B}I&e6RZvji|2>Pn)MGfiIs3a42};4 zk=IIm8DV~L{2KBaeilEEJLAN9sf1nAc;9BoF)J}jG_wlpSKU+&8O*oXjZJ$NE0wXOlnAItl$gehzQ@ z6+0nbL7jXkDegnfjRwk?%-_GzBiw+m#dl=qom7yQSZ)LdGx*-$cUWW0- zF~!k@$zBBb#W9!L{&aMOyaXR@rBqHZ(;-Z=w%|{KpB~@>%A5%nS%TI!6q^wL17yvx zE`^#2Ni=6j6_t?6qWHz}yC?^f1|<3UASO4_V&zo>z_3~JUx=(@>CDw9cIDKi)Nx^rH~=PUK3*NY(lI zD5lx@7*9q%&zk;h1VhGBK6bOFK^db|^{GUap^TEe7X6_Z<&5`PH)H4;6@!S6w=RGs zjLJL|kZk=4W5TFvMmUIbD-#M@z?60yf?lImXO4*jgTtCpukS>EfUG?brOrmie3oPG z%0#TmJsxyZ-9|TWrJm+Gw4K+CgE6s71&kPLa%22BlkQu5Xt|px>}_OItzuk#1S%Q0 z8y#5J1OE#=9zF0dL>PMDMRW+S9ykd$i_rtzb}d#9TpofjJ#Z6L@aciGp_Hiye#S1V z2fk&)nJ6`+2kt;YnR;L&w93*0zd@JC)&mx-n4@dHYDlvc_#Lern8n&82R0#EPY%3+ zRgEV(@G%=MIdB5cg2{nqGujGn`nkL<739tvMxIXpao*qx^f)75e;9-> zcp{t#I6Cfc$t1Ih`it>tbK)kCWyiyCkvxW5ye`1HPoff zsjM9;#=4~M!5{vd$QxklSnn<-k)JhDNopEQWk*NyjZ9{kHE?p$GoH%Bn%H?L!Ja>3 zz5AVHdbYEZRY*;1vEFHjjbks1_0lz-62!XNob3Opl!oNlh*im~eIywv^m&(lADCzg zm4e8PjAVYA#EIOFFqWUNgRcV3u3v`qw0Xy%KbvPGsF}-QVBJ(N5}2bjw#kQeUb7l3 zG3HBXvRLzW497UL6Muc?T>Lf73n9yIE`e~%Z-iHp6U?w0Y(cFyTlVm=Co}Fwa zq6egyuVcuhnsl)+!8{LhR=|7;6KR@xF3Or{j-c~TGA{#v(EKI%(@hA|CYxuW_f9c4 zp`kL&Md*DolglsxoT5!R8QIbj^d9J#_8y{wX<7QiAcEEG!Z~^~s}|gXp^!F15#AtQ zO-P%m2w!k6x>(vQMZ^bhf^KQKibxLLfnJq1TL}wj88e+pB`rxWLY2+8QLtvVfk`*# ztM%&>?7HRx&IRVH2*;RL!$PrUGi(rNP5{5pq|05?d;vA{n-)?n^HFGnmjRHPU~a%@ zPSny4MOo*k<>^TvgIiE3e9V0T%&7%WgkjPOq#k?)BCSyBaU$%LRwVV{nImn!)Z;;# zcYz`T!RAa5#fk_9-=~0u`X+E^1Z!xbMJgo}%qFo|5jnvC?44Gkw8;(LjQ*2$h)U@W zj4684JMH7FqJYGya6UkTcU`OpbtYb?Qor4ZHJb$3z?>)W@_EERGYG%njfKz z>dS?$s$=R?^H7P&FSC(N`qj|PnJ}7e&OtZC)er1om?@C!HQS+4jQJURw^s8c;^4@zrXO95c1gBoLkInsJj4eruEz8Y z=aNAJ`=US<3gLcN}B@T8BY5GRyEwuiLn(7^lFR^YUe<$@>q8TT{ zxcVxtKc)^MSg7ltUkzQX$I*EDm&}X{pAf=du>hZSIZUH}%{eRH@*$W04TVBcbRat% z2-5W$J|X@o zI?L1lgtQ!eS`j**K2N3P>eIGUHG3T4VNhBoW*whCpwooGJ80iybrYu5f+Of4`j9@2 zCsxg<#r&Yx@wP--8G=6j9Q`Y1QD)t#&jqu+7}3A0uS5}E&G&tjt4`ng@kOXpfIh^r zlKUitzk1*UR2j>N39tf|5r04haRT-!KW?Xx?~Feqk9h{=n%6@~-K;<>c+56PHB2A= zdd;_>Z;bgi^oTW|MNFLeYxGZ_S%qAfYB0L|=6sA4%hVAQZ|+5z3Fg}nnP^r)nC)_kis=8yJIS1jaL_yprKATwfMrbRoJ{R@ zQRmxd!r~@3U=*)*b z_v^2)2PF1%YWk~eR?Y6IudyR}?VkO*-KlOy&GkQ%C*InD+S=h{>vL$S|Ai{i%Phig z9)Z8tTYT?L|2Plfz1DONx=)z*PA!Q#uyctXNoOLO{Y$1RMYB&S8gKM_$q?)P8=~SE zOs97^{_N8y{R;>5$U{Y%c_NP=XK@gdQaM}|vc#b}*Uk5_1j`Z#N{RnG8I_cAo z)Axh$20w%V{RBn$f&(1Dc>e}Ajt|a9JL{+Dbfspmwe-{U0uX`VksM;D>lGk^!D~50 zzO9Ii;7+u!ewHFa_VR*7rYhxW4QClcrPS)<#c(o1(4CWXtoIQo{qt}-LrCG|73=+r zlTPhy@iaVAViBPXLRD1?B14R1`Bd47T!?V2HwTh@hjA3Lt4$2{A=ZplUF}ckKe*dO zj)@r!i1*QCo<8zTyNi6kf$_A--$O^eY{%rA(euB*WAa*1KiDyOJx1Q;J0{OSC)&MZ z@)U@=Vuxn@34LectR2W}^rK5nN_z@H|JM)`m^3lSAw>_YlY%_|Y1Rsenv`CIXs@*y zDo&bA|7t!f-$Z!I_2^^q)<2PVQU?92C2Q$ap6vEAlk0=jXCMgGq->Q;k8O6c3Q6)B z#Kw&%PP%+cXI9E|+N&Mm>AYbj(ZTYop`i5^j1sJ1(VF#ZBnB(lnZ4Eq z%qzhv7T{Bxlm(YF9B*9#TLf1ei7}dNWuub@YZxD}-ecZ6dIAetXJXC>){lTE!+I+P z;RbrR3R!Kab8z)^gmbJ$*fQ8i{#@&5hMO49vwlSR&3^C{TJtc|1Y2%GxY(kv?%-Ok z+DojbSk^k~xx^aae7l}HEVJ(CJai<(71k@62p`4p3X7v8cr=$|HP$oK;h6UkZm?=G zdj&TnL)&JH*9^hd83?b_j7_Y@l$&55KW$qQoVF1$))Lq)IGsT)u@B#t1w(9UuXQcU z&16|Vi$2zaS#O}kcwR>}U`{i_0qYm!pK04_H|sd-Qpn7(+RzHY+&d5s zSvN9%_VWnmSd*ypoKFzWRX1RRb7`ME>jB2+*|ut7UFOlY_#n!=4O*)X!$O>H(2a09 zd;TsbGL<2)KZM9w?@3NNHL!b`;uI!&Yg5^&lq*^FomlTZOlH05bEPgG3#;Wz(u<{f3)K-w=qhA z5e}IF{&Z$y4|IffF_J3=cEyN#js=cqGHb%#W+zL%zRwh@$o^xeP>eG)Z*`K{rR-!b zv+NMFq3X|)hvNJ^nX23jA%(25Ufvc?XOP`br6Y18!v1TJX;1nwpCW8+!$?hU-+7DU9l(U5sajtb8CcgBo3>RATTAkj@`#vSs0>q^EG4DF7j^Tdtv{{#6CP*Kk zKHchOQu-k8EA?6{p>6tw95I(^1}l?((OlFebO_H*59vBr6q#!f4}zzNEVa&iRK;iG zOTYcX_H8;&Bla`bY z3zJf3>QpU`>@yZ%kQ+Cub)fMe^K+s>1nV*jPwO<}CY{&lnfox<5LTD!GpbNu;|@F5 zXW?9Ts9c%qcGVp^EtB~$8Go)AwHaL)qq>w;01ISt?@UCnU1oLkVNqizW^RDK2(zbX zxzlN@>pe6&53EzL4clf!&Q{s4_b@vTbsKQ5-e6#FE9AQovy8qsD&Ljie0x>C%mebx zNI`w{+w8jiI;`bwww8liN0_Ki_j%i3BhPD&Vp$NS?_P`Z#yl$Kd5wCNAu^wtJg+fN z8_0s8E|709+Yj(7Y(^QLKQkYhi>M*`qxyONT8rrU_aJJ)Ui`TS@x^FgZLYo=wOoyD zw9$MTZHfNCo;9NfVf`Lk`?HYQw(&hK?e7uoUqNel-W#iZ8s-;U`}e5*Q;=(G{~on} zmt`sK-)Agl-A?J8FLb6%DzSGl;(zLt_!Rh&YY#lxg*Wq*4;b@dPj{-cOGym}*?K*tPY(EOu z;VahRF_4RT&^%vLinf^c2v;Xbe-Gq>`S`=9cME1B5QqL<;&2>t1Y*Y4a7+Vn!7<}X zR`fEvqPdug?22AyS2P1nh(rz@Ew>6C*b}hF4rsf7J%}u-<0FODn+yeJGaG1T<9Akv zsy3Raa-ItMFxAB->8v677k>>s9i=@}^t&K)KDU!xkc2`LrWmC%-K#eCrjsW}M@sw92)BFsg$;vuE@?P0~Tlwy2A z`*79{s0O(wM_=$9=zlUo%Y75W(vu&y21kLu`}nX>Y|{s8_|d2B+7>FIrJ#@CWrZ3d zxwnQO<0Qq2IQ5>IzIz%XXLHs=3THij(KVYl06?>XSA(`KdWtIj!EhO;sxp2LdMV4` z`>=c_i7CkA+oLT-pVKVF#f9v+5e{3uaIW6S4gKRR&a-E!l8hzD zX-~*!Db=*n(oE>JC~SrKkfHD12R7TZi|o2#EpJ<)L}`fYZf!BE2@~3;U8`RNazO$9 z@KL?CFdlz6rz)kVL#tbm#JLojX-jjFItP)DI+2&@+Om8^F0}Q>j+&UgYAGHE34M1h zG+nR(QQX15d%#kp`LxR#*={AJf(r3%BwovT2=iuwW+ z%5xSEEFnmtP?K$z+9BpK-;t3KAY8-g>Ey#kIc_px5 zA_5}}_z^f8fnuu3iWLH9%wb{s)u1*ip@ZEK_S+p{#%v4#W4ql=P6(%NSE;kq^QrAR z6`G|UPuYyxj3IP7V?ZqqjPHkY4T#KFFm8=2ZJAGnQ?ImTvdYFDm8j0o2&?n+@`)i- zeo`}3=E2R9_1!1TgjK%4EaRC(oAKj6d(u_cv5$f*5_WjR4E@9Myyz3vNJE z3yZh|fj$QQ7Xsg9S=+#+WSy)6jh<~G$Y2lHi3pbKIew5bb|PvIa%dG3VYyon*gpfW z9A=^icrN5{f>Jf?1N>5|jr2uNGT#G;x>c2pz-tWLfWW6Lo9{EDY{Q?8yNZp*w*AOe zL=pUWDxM-PNk$n;goVafa0k+Oc2pwj5Gby5MC4EeRDc8;TfH_y370RW1RA@Y8ENcM z259W>AfPSf+vS`U)_@VVjWHfZthRKcME5z-|77&SUPRN*3%@0SrI#SE2KjGNjH^Aq zWz^(2$ZGQJ(9~RTJi*H;Vd*cy&C*_Q#9j=k$Y6InT`{Mno_D5&l#j%iK?AAcE-HSdBL{-#b|(}r^R)34Q=f9%yX z{-$PV|6keZSZ%o+gPAG3<#G)751uIdD92P!;D*R}S&5q^2W7KliA|4elRR0;!4>Zm z*#jCZu(WH3XC-#3Dka9*t$CVD9(0(Fys{xOPNK4ba?GssklGy@XH2zEv67r68yYJj z8wB$yB8O*}8IjD@6YT6WnS*;JXW$GtOSWECIda|1V>WA^9Vya-YwftH~nhr6X>p==iq zvrxUIwpK-I+wElzp=qTD%DTkfNV_awDjMEeyPV2-aQ3aR)n%b@2T(MDb*gfe4s!W{ z#uvFOBKBmyYQNooEPm>{GLnWfE(vEF?HY=2)LSMwX0NqCP=h+KIs0`(xB7?(s@fnV zO$TyyB-P$=yD<~fWmAM{soJ!=IkMw62L(>wkqFG@5rW+t0(cOydockGiA6g1LNYdU{1Qz#!fmk2p{2^IelVG%?_fP2iei(2;?}h)BcAH zOvKWF67V;C(29s6;Jl7t6M>6wTRuaW?U{hT;p4xU&DCC92kTl3p%6ch#@FW}hpo(N z0+YwK9ocAJ_df1a5;F`dL$Jj+JL02i?b1VC$45pl8w6_|ioxIT5kd%2ge?4Q+c1=i z9});yvqm)vudJ&Mh(&}mh}nmd!wd}J(v0#5m;$5ZAwuPJx|oyQ>0*umC*6_aq%*&B zxJ(d29Gb#OA>#CmsMUm!3O3_!_~1PRU27zS@S`;v!?PH#w`oOp<3!??jN!A38DnYo z0vLesKF%__wuZoKTifV}4TwUCd%%M3;n@3y1ESrC{hxri0`VeTf>sNg!n zC3Y;~NB9}qj%`=+GVW;=zmD*-3XZYLzpFXkHhjvJ3_NGYmT9gasx{k4af1l9Q^9t35uQ#uFBG#c}@p;<;z9as% zn*rHY+;J4}Z|y;jdtckI*TuhdVPJo77j{DMuQO~^UM&7^_$>Gz;yU90id&6w{W%=y z#p1uDpoYXK;FHL3?Lc z2YfON{Tro?vnBt(pUWOZ|AnpD3;xf#YQe*n&K>rrzqTI=km}=8iR<9pj<_cE^(9=oVB6w5a!j#JbgkDdl0rx_uocO>smJ-VKw z(FaLB{(52yqICQUva^qvmUZ}1^QX+nR=!XoneRZNF*#IFP+QnkDrrju7@Me$71 z4RZ+!f5>3zx1N}f@NZT4o)brm!Sb>qUa{FdPz2IS%8%|=D z-+O4?8%|ncOkP<~G!-#lsYW^LWD5UGA3^F_CyyAbm(WVX{DbFCUeD}Vh3k#kq3H!j z>?vGugjCTmDRP$*|K3R2ov-xz@T5)ak6aJ(Rh>HRIeFds zJ%w3Cj&qn@hZ{jQ4;{tqDk#9diMrlCiauK1-`TaJ7Nb(@Z)(Sf`dy9Ki(V^jsi>}R zEw8VrEN`u%kCZe6~3{vjL_+vfARA~%{B-yHnt9-G9wKC6d27V!%q&I{}<=sEVa zLAuH5Xd3M5=-JfM!4LGuE3Bn%RbBnsI_0mTp|r6HPA6(>n^rW#9avLyV`XV=Yk6%& zYfD2#X>(=k+Qw@9wf!j2RRzYXJ>!**U|eq;MSZx9azj(2%GFfa+`6Koyg|xsEM40Q zF{P~)P0i6!rOl0{`iAh1_$bhxQ!BT1^^a6)tKiL|wXR-?b#hm( zX>P4Rm1^osD_ZGrhgxD|hK*axH*Frz1Z!(c;i{vlX}q-BhRWrwrKRPqHRW18d|XtO zR@byNR%-TXOcRUM)i+mHt#7R@T^C(1m^W-)vC48-MNHe&T-w~y#Kzb98r!!wp)uVJ zv35;u*i!+#aD*%0)YY{$x}b{YarJM6?pozKSl2c^8@6>tWoboaBfK!ceVJ`exAPPi zcWFaIeRUn2tF%J;_U^9!4(R8!dHvvU(^!Wg^dJ&elII6FR;hGXsIhiUnbu#W+|~?_ z4Ar#vZyxLa0DDPn9%}DXLuK6NsA#%7Bd-~Xf;)qqbs(;$wU=KZFzl5U9;QRRW)dO zc@SCrN*uVf1jOvH(h@?m1n#NkP*z7D3XZf;f4m~;C( zm7&n%G5EMoNh$o2tZ9eS8J9;a#}kNZNO+SOX|lT|gU#-7SXNOBeGc>yR8d*mvJNwv znopE>k=91qsxt`X1MKv8PH)G z@S)dWY466-zS5zQGJK{TcAnA!e=_5htGc4?+r)KJ;pw5VvIYZkO(jloHI=PR)kjvs z4NqeiJ7G)z@NxYe-9rQYJ;%4xEzO$wt;Gj8IdZuI8s}76-_U{<7-MF&v%kxI7CIqb zv+pm57brLa9A>A)oQQQ~wRoY^!rqahdI-+db`Nt+?3iPkDp$AGwba^9Zs`gtQegjxj9Ia`0@z~~5lM0O6S6YrX$aA-cfZs8>1_7LL|Q@fz$ zkTJ^A_>p<3rnCtsrOMUR+tori6|`b?f)k%in@XZomuG0T6u>3tP(M7gsqvu(bbl3g z{!wQ$wer!pAdrPdE6&(s+&$S&%-rsDY}ZzsI_;f`Za!ootXG3?6{_)o(dq{@qr za<}8EX;sY{ZKQ9oioPdY7ZMFaU7Nb-{l@W(W4n*)t90qEH0IVx=nm0qC!Az(sliFx zG0>QGMr6UE+-v>+j-$ekq+E`pY*$_54b$bzBfh{~ZCP5|INo%^1sj%5brq$Ja#plY z66K{;V;eoPEOm8iE(NL^RG+M0rETP5s-3>6;4ikLt7&9l&}m>Ud^t?u7O$qsiDavn z*Op^JSic;zS9v{Ea8{+x$^#3f0l47YY>zlwxrW+?F$1uox~a5`b6!hXb9Hk~WG!1; zS*!KCrdQP~#rxqnwks`PFs@v6!i{z-SXo4 zQFaTjpWG7xZNv$rsd9NOoq1ulUoH!!DDPhXg*!J|A0|4i^|eY>cXgp;Ungmtq=;3}y~c;SN4 z(a>0LZ_B2ZNNQ0sHuu`<=JFM?wCQuqchD2BCOgWhF(XdTTqgzFrJ3}UF|(rWYcRY) z9ChOIo*irKlB2egmxZ`C9IGZe*7}yZ`li;F7M!EU%;y}o%>(w?x_xAHcuYoF?blS+ zEpJ|7r^qsLEtZYr45G$TtxuM~YUxJ*(B*1r)Cq^q8g6(FwWF&EOKoLEb!n^Jx1$$J z4K=0f%Sy{vjk|_Jr$qNsiy1lTyZsTCqLTGk?#WfM8lXy7+qCLj+oK*MxH<;QtEt8X zHP5j)SM&7hsN0C^D_Q2+p5I)fnS-{xw5Eo-Y86eDtRaVoedVDfI3x_uE~LEr$(ZBW zP>qZ5wM54NmQNkF-%IEEzp)b9m1Bxioz-c5&FOFB^%&K1SO*tip zwRAUeMJc?Ps+EEAN66;1d)>GV&+96eRdB8C&#B}1g z@Pm(Edu@TWqcd?w{OGF7Y&3VY+flBEl^fJSUX(X=4Q;~>bXUukHbtGy)b**x?IWF= zHtKEWQ1yGHAn{*C@C zcUKnuG~bXs`Ym)1znZgrNx)x)?V#LfX@rfT;{lWTCgdQ|;Loo$;MYH*?1&{9@YU5;A> zEw%PFpXj0%xZ{`^7wgi*k!u#MOD$Zh28J-;H8okRXzx^)3b-l38A4oE%Gx^OCe^i6 zuExzt)oY|BBI}!GwM4KNB#wJems#v?sg-wiwhy)9n!L4>Hyy*)?b5<=u1uwCs-=lswk?z);I$N4)WjuTNyFLY zq+-uJ<9OiJ`Zytc0;#XSBxVCR`yn z^PjrAI$p`RByx=&o(3bg7VPzwE4j9QO}KrXsgRd7jg`2CiA#Rh4Si)a>1PLB;2d~^ zn>WavTTo$BFmk?qPg(m97eu(JMR(9G!&(^lN(kc2JlQ9uzFqN1Xrf~cUPFzUGBD5#A4h$AjDZgCqO z*Qle2D1stJarFN@w@!6B5oX@+{eNHmQhCm~_uO;OJ$J9FuBse43GQH*kM=GrtwpEc z^5mpRW85!8qLoDZlyMbh6(itR+$)JzgBvQ*ET+due~(<2KnOkH7Do#-v&X#5gey8T z%drQZHgYJ|xUIV7uJQTBU&^}?+#9j+f)_f zv3+jw(cu;gbc|b*_2|}e-R^CXWpH%AmF|sx&1u|gj%osix~RVFRyjT-j>QFEF|Bl4 zaHlD*b(;M;i6VLdxP(n*!kjbRdv3}b&yKUgL4$1WPSeUJjT<>`__5s7dOy~x@@HWj z))zy6_nFbwg9a`bju;yC;qqg3`Js`E>zKjWeZ6Z3S{OS~-VQlN!ZAoUvn;AWdJCbi zed?=RZvW70&{k&guF>%ny4S5`SYyA``#Wtz^vE+|xMC*WV&9!pz4;w$Y^)$h&%`y& zVYt1dKUKd3yTWGQPaQU?eEj5LNcYXZH4p zJ|59m>Di7mxi^oXOX+=s&MvZzi+ou2^TkI~8%QDgR#uZERN^<#}MYwzO`_I37AGlvapXtdc zs$H{ZXzwk@EColqt6Oa4aF>9aypymq7&jD6TAB+t9^;TLx@^VxapQ(nOi?v*8%?h= z^y})Q^rP)g?cJQ1*V9^T!7xC14H7f<$nhLwkG;#`y+%Eu9siyZrt_#)&8tw}t?8D7 zHeb%KM^hFU^YzSSi{|4RTFYxVEzE@Gma%RXdAYgw{~|jB`GS3v%OVrAURSoX#=S7J zWNh_H^wdc#esn9=Wr)5rig7`=$C+oJiJ?v{!BvCkWtX>?cJG8OUOcmU5w6k9d$9q0M#sws} zEvhZ>(ysJ+Ea)+kc;B1A;;>ms)YA^`B3xIxJKl~xY09wa&ATc!yrN=YsDO7TrYv2$ zXjur?yhCM^#)gKWjI(FYnH@sEn>Ak{f$*tf7(sCQAX{zWJDIx$p$FU`%t zmby#mR3zy+N0RjD*Q4glYTT#`!9(-8zzX&5Th#Z&{-^7oEB}VWQ093_PNL?$2Y>mn zFCGtz9OS(Rl^K|evn(lav9-o`iFvj0|0FIte?C+k&GUiiXnXi@b+kRH;Ju?ah_a;E zHMBE2oDX1EBn7TE<|SFtx=2G8;>)Zq% zec&OpR`P!N>5V7PU+gs>cpG9X`4}|-(bUg2WJVsRchmUr$a_nBV%69d@@xsshYvrI zYAGMDW+J*-KYuvYeE86cTFN`>97H$EN4GNm@*t^|e3H6K zY_ldGgp4|T_ytN!d)lZc5Knv9rcK1Z0%Ut}5B_4M`S9VlRxQ(~$ggAYBU^}>`nOoo z@@cK))6{2(W`67sCTgGgO~>gTYz@ta4_{+znO}xFf@r2^KQZ;!+M?xyt>lAv*|{qp z((=*0i~ercO1_=yDDv!ICgN!ye~aWE?L0!G4_{;JDRJ(v(D*)r=EK+GXB5n@z1G(( z(O&U;0zC3@8v5(Pn$`b&_)!}3Z$3_FCPDdBj+!9p+42nV(T6Yca}hVxr*mnR@Trc? z3@D$%TM(iSpXw6DF}_YrX;ABDmS47QuR%m*48a33s*C zLQXHvo7eBmxDdA$mMyP4b7@^+pH{0ix!=&d+Kpbz7Z&#~?$vr}(@LOMFYj6gE?Z!a z*J?kZ9|dW73&7iA=r7SCV|NcB7d_5Cv$}33c3?}E&sD$Mo^$opyb0CM3VF}OMQTsG zDQY#QuP1ZNisu_Sj)R=*Iv}1HKiqBi@gv+HP{xmR$6vFb`-9(jbZoY>_)+c{5kDI5 zQy@Ra{TXWfSohba@#EZa)Etjjup!=@pxn32^uOzhSTE z5d6}OKXxgIPGq|vE)`rQc!}V8!J7r|6x=DOzr~L9e-r)# z!S4ls5>%#}PljN7!92k(f;|QM3yu|>EU3SwUXCB!2(Q1T4t}BVX9%7xc)s8oLH#ZD za=d;^_`3uj5`0?lCBe@GzZEV5_?ut`It=sA6YMEiA~;-dvfxZXzL1XT_{&-1#e&xe z-Yj^R;6s8>3%(?HK=5b582+Ra`q~P15bP@0UvRwOse#=LhwAniv>3d^0jExw_Whhg8K!J2%1<#Q7%)klVESb zQo+%JQw7fuyikx|2B4m+1o=&5@;3@@75t;%cEO#3e-V6D@NK~l1V0n}O7M_ili+WH zF>!9biGrzuS%Uo3AnocRSR^<=aERb|!Ks3$3(gi?D0rseIfC_qmkC}ic)j2j!N&xj z7kpE&QSckVp9O8q<+M9hFh{UZutac#V2$8X!HxK3Ak*J2c#j}IPQ~zDf=>uOBlv>g zD}rwd{!@_eQ)K$j1@{Xc68ur{--13Y3@9Hbm@Jqkm?fAa*jccLU?0H~1&0U@6C5o# zQE-~z48aA0XA7@GM+ zuv~D2;5fl4f~N>p3CR`5hG6afRS|!5aj(2;L@mx8VJP4+}mnxLfdf z!IuT!5PVmVpO#?0`eSRruY^A+ctr3QK{MHv=f`cBE=e#|FjFuj*h#R5V6or?!P5k5 z1?vUZ32qkTcL|u^4#5`%-xB;p@N2=J1pQc;GhG|O_JUmm`w5l{ju$*laDm`T!Ak|N z6TDq;hu~9!FA4rb@H4?*1^Ie^+Lb1lEtn_RRj{{UxgbB_$8_TarwE=RSS2`DaFO7d zf-40t5L_d8h2VO@8w9rq-X?gr;QfLR3qCHmTkv_omj&Mtd{^)z!7l`l2>v2ywsp%H zE0`phDwru45*#2nPVf}Lxq@d3ULbgd;0=Pe33kL@j{fZ}I7D!a;K_n>1(yld3tlC7 zliwSh9SM;6;L0 z3*IDnr{IHve-Zq<;6DUE734QlsP|{V80>|~w-xLl*j2E0u!5kbCLk@oS!eZ)*beu<5IZ^2T*QG!ziJ7I%Qx&DHK1j_|S z2#yn+B6zA`cWmsLzF2Um-~_?b1ZxGC3)Ty+6WlC#hu{vuCk0;=d`qxV@N2=J1g%Wh z9)2-_cC{Ded#uTi6PziyP;i;x)q;F$9pxVp+#~q5;3tCL2>vXXfXmxVmnT>xSVF{} zXO!?K5lfXiMZ#+(d>IjI1y$w-cfNalyX{zAEwW3jYZa>3$OaSK%GL zm>c2gMC8|5u#eyv!Kp-yt#gFu*JqgTa^Wu!{wl$1C47tUJBUcXNBCC+-;nS|!EXhB zBf=hj-;S6q$S?Mh?@Pp(R3ZF0VkutSA^Z&Cmk2JC@C$^$jEMBxg#V-Pj|=W0BA>Sf zKNb9jh(7#_@HVc+F<*xW`3&JhM938hKS1L7s&W%;cB1g7Nqn{Ni-bRe2)T15{8A!( za=q}|h{*Sk!tW4%x8QRU-XQ$jM5Ny*7#ni=6Ns?8NN}*=2x5*>(}b@fBENaUpDFyg zM95tu{Pn`$D)^A#i-I2#^KqS(2z^J1y_LQ|$?!JN#qe|@_>M%xcNM;$U^%fbt^*JY z&@PEcw?yz93Ex6Q`R*ik!~RY9r-_h%Rq#y-|4{f(h5uUkABaeA<+|w-1=9tyh=?yB zmg9AFf|Y{P1kV$^O7JGZ?Sjt=HVD2)EJVEt|2+|Q^O`WxCYHl*!lw(LE7)DadkH^) z2z`TuA1*kNI1uBa#Lpmx@y2-~@>wSQ8sRqx-c5wQCxqW4_=?~g68|s3qeRqO4C3BK&keu^(RdFZ`>5{}TLG@E77_ z+|TXort=Frf&sxa!FGZn!F<7Pf;|QM3l0=46C5TuMsR}QG{I8^X9>;|R5&>ATY{Ks zHIHqbMs#OA-@}a>y9*Wx!XL-30pz77Ox&O^oNO zC5ifeN*mnABM+P>xKL2vPeC~EyD?thR{>rw{6@h|g8Kdn;_nsyA;Cuk^*cim|FZCJ z3ce%Ad*96GE5Sp8KM3;UOpH$u~_@kh{PXjrA{fO&_B*8Sn zc7h?ne8ED&-h%vE64MV893wbEaGIdLp98r$!p|4vmy@{OI8X3mL4AJ*;a3ZPtsuXw zMEN@f|0K9mP~Y!C{0qXrBdG8DApBF|zYzRJP=7@j@f--aPO$_N1d|2%=_AJH3icxA zVe)0=_2U?+bn`xL@#FL4JLR>l0frNl@Q^LO8y}pyN9Vb{8xX?}H&cRrsLb34(co{GcGy7YYs#ED_ZA%@AKHJik^%`I7~!1ZN8_6kIBJ zq2MKg>jd@vHR#zS{LO;=Y7zB6Aoyp&#|57id_nLZg6|1_D)@!qH-d)*4cpc47vxut zm|r`=T)|F)Jp_9RmJ3!2@)JhdAAKglCs9z+Zm8ex`3+?LU(?Ld6C@F78{)bV=V zgm_e`=CxhmQ54N^jDImQ!V|8gSi*5d>0-9a99 zHV|Q-?$@v@h3y-5==|*t1CZ zIwI^^EBrbll;0uz-9*o?$j|fZ9*KXE2)i4F{}&PVHwpg>F(3WI03v-V5k~Tb-Q;^E2T$jD=q$H=+)VanqqjVjQwZP!l@9w+gCDfH5s zInDl??Edt%^>dfUu-W^$Wpk#@&tVpxxs-KN*y?dhJdf$cpdxz}l0{8u(Ws{$`5TmD zM(D>OJq@jN<55mG&77JQ^YCjn{A9Cmwx+9VYWNGAs95|8Cz{1eF7P%JFHfE3_aTe; zZZy#d9iVdS^t%g~VjX9q4nD>QvQpD+dcZ}frff!YwGa3MGi0DVXfo_f!_#*P0`Lfk z3QI&b1|r(q2PgGfILTjtGuply@W;}(3gOZE@}u-okEidZR{GW)N8b&t^mUKYheuB$ z`u@^N-<`+N_joIPeWUa_QTjOVM3?tP@U-7MJpcWKfM~e}@EF!3hbQ*~LZbaLC@NoG zTk-so31!jx8WC57(>o}~BOOA~`i4SZ2;rcQ!;nDtk8TPS`X&-LQMeKV*^m!^AD4;*Q1JU|;TjV_7o&dR%yhuop z(R51N2n40qwPUMyGB< z|3zBw@Z^Y5!RlS;3!|-az8;Q~`LZ9-wrG99C5Vek=;_1ic5GcA`61BKo}pg-di5*n z)4y-u0ijS)h==>XS5eV`0SFr~pr~(O9bOf`df1ps!|*h*ECrGBAbYhZq~rYVTB1OW zQ=^ry_U78)aQvUo|5*!tJA=c=AN;KdXRo^96)Vk^SyshY=B&T2tY4W@QSp@u)eSG+ zWmVSKCRYCTwP;fubLW)pEB~g)r>z}KWkyl_5~FO zV|?3d-r84?r$W!|v-0*dZC1rksrhwqlD(_$vG~zhD`UDj?~XtG*sR!*YGdrExY3u- zj~l@}j7AkQ8mZH_H|#cf;S6Oin^RNu!dHPg@=D%QG6y*Xakl@vWwHMJsCB58czW zZh}_|mG@bdZ^yx>qRT*fj09W(+>A2?ey)2gsB`qm}2YHu5T}zpm}OQ{|W;=FIcR-rUOY zcm6h3qskvzom)Bn@Wp4`t$m)?Xx=;i@I`01wV?`<-b>cyOo2zIRV1REiK7xx&O}|# z*hcf_yei+#v#P9{Z?E#-{8^RB_RtqyJU>)v?TTTn?KsZdhmj$D;P+Y?65sxtEaSd~|;a+-bTJ4Rk!Co4vN^|ij* zQx`gw2|G6r_vtpvp5)70)6~6aSJTR(S+n*p{cP6TP4z{2vuDkowP4RaWf*<#uANo$ zcGLQzyc(pQ4?D1|XhiNRlviC^oSY@;_|K-W_|ZQ)TW6u6pv2N;Vr* z{>x1&oW67X;q|loRt-&@?N9wdop8bHyT5;Cn9*ZZ!|w0PMm!41rJ#I$Rre(g$=yj` z*xA=}VZ+BwfltgHGnzWrnXhj;`ooV_WBbR(AGDG-9sTvkM-TaucYhx)v=05&H~6}* zhx2sbvHixL)RaGgIU5bgcS8A*#@TZ!u0Q(Y5B~j0)*Z^Jy7Zy(hj*XRtEzjWuSfFh zj~+6Uou@x;x^F$tlbiCxsxaIDTjxLDq}G=-e%!Q0hgR-<^pF9`@rMKFc(KeS)ZpvE z@@2oCF?LbKbw__ZynHAf^?0e_IpB+X#~)O&2OAl`t@ed|@p){+^_8mNyVqZ6vKs#ML;d2! zL$L|}tXx^`ukpbjCzqL(36<~ci#IMRzp`xicTd&(dSrfZ?tIlPeDIO_ zu6Xmr^vdUcsGm!{y(<&;`=vapApHGZ^?fpfPaApXR{Mwkv+}jQBlH+kGJj=FpPd8e zpEkNYoD!y<8|v*tVV~-xHX1kBhX(oVL&c_jsIRed0kvYfI(SJvENFY~!oLN}4=MA7 zeKBoI7M0Yvo_l9sOx_Fo%t*dVUbo=Kp~CwLtPgyJZ#}JYjGW$$2-l!`v`;iPJhfx_s%{O-g~T?p^wc^ZbXi4pIFpv%ejjOE-?CM z4oxW>So84fs&W29ur|-=&zdM%u<_`TBkwe=E!xwxrYdD;X63mHQmW2fxCbF`HLX!k zEc|TV(S>(l3{a01FVf}B%@|GOIsTv#yQisMy@R?+#JHhif7+{c_hwDAKI0A6BNLpktg_?j@=E@x|wO1JE2IVxFsDtFJmbmOxsmBUt( zu;G<`fxK7twbgA0G(78$M}dQuFX8pf%664*O?(KsJx%v1mrK}=(e&Q7+f|M`xA-fo z9y2lfd81~rZs(s@aRwj!Y2em2TN7@+eeS%uo#w99qx0Haf2BL-Uh?H`)w}Zh3|Fs> z)*C*Ub&-|p+~bD$(AH8ea_*+b(zY8L)u!a_N%tn+lej5qQ`#o&Q?w!TqPI~Wi8s2v zguUaIrd~xU2V;|yZ}v49-FyxC{jEl$o6(TgUo|H7Z_bhVeRg5X{NBItmZqYj>D`iF zr#cv>_chM41uYl@PG_PBm#?^57NQCS`NG~1tw3eESu*bF3Qc$}t?A`uNDIN3r zQ>(oGc%fUiz(FH9=1{C%Uzyi1u<~eBitAdXSSKlJcc$I+@c8`>3zY zn;G=TbLcTKha5Y2&`eHkrEPI5ZF5AMHK3q6c+g5tX(fNsg|l+~hq9%|)u1-UTxjGy zhuJLV&{bx1$-)=W*_%kMO~d>pw)> z+gAGudZAmF;e&71Te*o*wf|CmqON`8dUbub%H(^K?(u5R<6E(q~5-HioHue^rz{5O)@yk2)& zN@L9aGGBUA8+GbRuIUpRYgcw#sm$SAF=qDYKSiPG3H1Ip*}`THm{kaou8SjdxY3p|Pm;j9Sbt zKHszWAJ_Zx%=(d)zTAacP8EF9@IsT*fblz?GcGW}-2TEoYx}u1?JF@y)MQs;HeUY@ zT1XXU-L}EeWs`<2+n?au{oNnxecii!P_lsY`S#i$~^>f~BvKlk@TjuEf z|5`SwUB&*-m#Lf^hMlqB=lct0-M{Zo@|V<{yWr8Mdt_PrP2J_EdIVit~X`vf!;`<|%ciUIb8d_`r4uoA{@kaqQW?}-K91t+fz=$zH8 zhLygG^}4McozEFL2Q%{fk44+?MD01Xqja6E+^N?9n0KF;&sBg?;^z0a*Xc>DiW^m4 zv~8-}ki%0SFhU3qy#w#h&YNCAddhL;$z7=b=y|fozPgI^${ydCS6Y~HY8(9XRgbw1 z4VaBp!JLM-(95`bJ_oJ#^!H!KdY#_c`c2O(>;voLM^=U_=N(vk1;fa%J#g2mT?clr zI&>iLbLW3PeBeb*b$svN2VPi}abW$xPlF{pU%vp7nKj*-4$NB>`+?E;@i*;PwOt)^ zV13u719KOU&s-35ATaRJ1J5rw>%iUx0}fQK`r^Q4t1QH%tYUoIRaTC1Lr@pMam}}k_%h9WF)K}mvVSh-k zyk^G?~ z&^nL(k5?ujjn(Y3{f3eJp1wX|B)m^4cih;suV9|RD-lI1bX8;0pH$wkrqsdA!Hri1 zYq7%YA|to2VE>2im~#7)`mcOuIa;vq3Hx5-+L(LQwf=iz?`0c!bSKhZ&Qj!~C%gjDx;hBsgW1hAA=R-<|5i(w%?BP-#C@K1RJHi9A%!j zZZE7riym^g!vo{LYCK0BwZD%!8rNsr(HNuM(Bx4{&zH10u-}X=+wY4*s>`u1jX!xW zcQMI&>|~3HK0ZTV(reAOs}#rT=<8R_H$0UJL975LuL~CA4C9=DvkE6K#?|B8hI2R0 zMw|+I!_;lz*`}y#V(x9umSOs~C`3az|4;STBafl?)jy%mkIuz?Lk%^CVPQQi+fA#C zkP$YjjCy06u^Z_^$gLO>6ChI;B^8DQV+)QO|NFG-k;e|ng;%TtxJ&VS4V3!Nu|w~J zFQ0(W`7M2bNX42-tB&>A+CdseOw|`t(|itBoOk5i(R)&97F3B9#`Ji%&Gp znVpk$YSH5O|8a8fTLvvt&#Ij>YvHmLi($g-Wu$$+QSduNOP7ag=0FKwA~YYblJwk#*Kn%E@T@qM z(c+PKHMJZsLTw$nY{e|TIPv(-VLj<)`M)xl??Ya`v}L74H+cQj16s|0Jz^mdz!}6D z!uj9&^m@o_!x_RE##x249_KckyKy$+R0iU4hH+NmtjD$=Zeo37O-1&x!60~x4`|2c=aEdYKz1)A znG2I*i%ulDx;@BZ-Y>SkL8)U0@fNXv2J{h9?a?tTKJA5=WFpVo@76i?X$K^n>`hUUbC zbDCumc|*#6JDoBg;-6ucvQ5}a@f?`XK7sYGtUjpD#FSf_Q>CT*AkX)pDErFJOm#OK zl>IWw;Im6nMV5UVT9e<-7qM$yuu?;yeSNC1fd$$4)>KIG)VPs!S6 z`OdlUeR3DdcXtL+rfV+fzD^&?bR%8t3}<-vN09rVWW1(ZB^T13urmntklZT<^ibz6 z=GB+JAK~zqA<6yef-%l_c=RNB0GrELhxL{`@O{`Y!Py0WClBT`)Ki?F;N#@Uw12vD zl=e>{eVW5xWG7E2UFF<{I!r#9`ObFuW|ibq*wAa8t7+#9((|3|q^n6UaQ5JT@_f<@ zotQk(3rR0>Zh|Gri%2h40ji@5+spxBpbGhnK*Htt*F6wRUulyAardDp%2}EV2glDx z^`~lg1`;R|bb3HOp#Nt(^HHEcGTj-{Sq0iMs{*L5hisfhO$pf$Drl3y!s`s$IBo_H zp%1l5bdWgJ*#eu}B#{m}*I*E66QI&;t+b6+nh$lKK&f>Zo5*5629r3e3_9D6!M_yU zWRikybND=XGC#V~HkYJIp0i}OYMV#ea%#~0+UApvbzX&@wjD`3&KaMF7Ynmqw84UV%|LhBP`-?l4rP70(X{zN7FJHm`4lFGRa%^)RNCvy5? z#7PN|j&)|D8K<-%?LZT4wr+)dO55E?mT)Po7?IMBy-zzTCDV1(yj+N6u?eL*&rv-4 zH_+XkPcXQqbfBXKX-B1mXxmU_ZNyldlEb$gS__eTn-n@O&MHDG>vniODUPK=C9!Pl z@h?UH<7j|TcJG98KH0qOln zTiWFG{Fd`7G0~18Es!b()N&c;3<&Z~TB>OK(1m|Nd^6UbIJy z+vdO-X(yr13?uDLG#S%KdmNLm&q(_ln}d<|C0ye-(q^Lx#TaR|5VwuAd*P^9BkdsC zY@CsHBhm$ov;puCU=*q(WTf$h4>?9!Pl)6iX}wUSJR|Kx_$%K~e*Jb1R*CWE-)X>O zNCS5^{nu!fwf;70@LNP}L z==3bOw;e|)IFA*QeiVij8wJMahzM?{d{^TzXceqM{TkhkWGJ+P(_x`eXcU5p4Guw3 zj3O=O1RtTddut{T`~~GP`Wh7wOAUU_HqhUw0TT@VKy3rHg6!b0j2UFCLrf@m6s|Q& zjqPCagHti=Vt4u+n1YNsOHtY7#(QA82VbFrp~kmhih>*hj7rV)RhcXwZBAk7P%xGy zGY*10y}*?yZJhNpqbSdk=qNq7rLYYmIt`(jq*($lWnTs|4NKytA?J>Yan?vL`oc)G zru1MHXI+NSc(&6xtJafbSzSc~sfgMTeXSSG(lb}tyv*2YZ$ z{4(H64r=yzwLgb18o=DG2U@p&2LYnA+g4C~L+* zV+r&u3SLarB|4@q*bQaQ7^Ioy!Lh8P!3LY!N)_0Q?6@1ytDzw(>yi*+OSMz|9bmX_ zn8Ehf!!a?b7!6k2i&6Q?p3#}E{xgSedoC)#ct{s>-gWAk-jZSA{}V~Twj z!c*-8XiKv{Lu*L4`7(?QJB%EI_GPH7cJ@I?X4-t2VwU}P{Li+Zf_!^BkjvK@^CeIx z*iXStA$tKd`|<~*CPY{<8}z_yNd7E0GiS!=0!)HWEAa6ZUzaOy}nT(7Hw z)Ad@*`2=MT>Lse>oQ+ZkPhm%lb=t#^!Ba^)$#wVweekrM@Ib)1kus-qGzdCeV+ChW zCgi*T%Y!pX=R15EU$CkMbb<3>2Iy*rcTcXHbtXo8rY&;nQHtO!h8H`}p`^jtv}cgA zmcYW`=qdQ$z6$?}jNs1+@Vdia-vobQV&(AHy1{=lgIH&K2=uSdGuUZB1q6Si(tvXz z9OTleLH;T-Xc#k5SHa*6w2Gk5V0P~YV^DFyF2+*Ce9+-8maVJ79a*-~fj>zOcGGiO z$mqZqf(E-A*B~z6=#Y=r8SJ6s3XBf@X}{Z#x?|%x1l1ZGn+~JX_o4K$!KFqim>_>E z9z4T%04h{)81p*Q*aHT;&WCcqjT^}%k3(5(@CxG$Cea>VXIzH{uJn>5c$IMwj<8h1 zYpCcza3Nnn=5Uq{E@ruv!$~^0gtX;wjt(v*9qVw44%U%&l>ZhuP_JZyt3HK-v|b^E z8)+371+k!c7*4^0W=saUg^`9m9u_pp{}Mdlrdh+ch1vH*xw7BmNM=`{Q=9g`k%iBG z31f$4zl?(U?XB>2jQtA*?P`R?+N+UWoXyvl#@nlqD#88@Q(aTYE5ymSWFDPf4{efJ~a5ffkZ(XCpSl9?sTiKZv&1F6~m}8wZ;? zPwR0uxa%F-{0~^7Y`)~xum`{r)5Zu1o6my0W%K2RetRSOb&Q>Zk-=604lR3;QV%b% zHWow3?%>C4Ny+%|Xz&v{6pL22_)pm&Ew_h!<~Ghb$UpeGcDi#VlDc%j`J7GU3!0Sb z{D_=_d&eN{-Og~7Ie3_je24Qlwy__W_E8m}4Q!-hN8l<1vz^MYr+Kzb&HFGjQy}vm zgz$9dc$$P5w#7K>dIakQ2wPwZ*Dna%1v>p#ls`YX!WhdA$E8*9Y-28Hv{|GKo?~3f z1{k~n)et;aGqJ%mHoEhTI}zgqXTxW~)y8fx?m{hiq474D)ZiIV9lY513QRD#m*u-e zGugp(7!bTnGa+{k#4J;^@pOT=2BJ}ViBunnCdC4w@nJLO#czwmlR^wfzBp^07r#3a zPj4gUKE}{RG*xGez~?=2n(PI>4?52JJACEmLQA)=cEJ>s&2B`Qls$x@%36S?+b;VE zr2V(UICDAT+I5&oxf?N1shD@s>Nc;8nTno#+LxG5R#dlj{gCbN#iN*u2K2Yixh5eQB3OA(MKZDnv&>W5E_53mi&;C zOiG?;zoq$JN*4=)+EXoI1cH_q`Nx?ns?Te3qTh+NAf_Ql!LT=onj0xS<^U_6gyjU zKu@QggPhmU&a+OY4V=P3pF(=5(-s3!)~TdNIP4`^r-h+sjANj-vQD23dV({I;WO5N zp5k!Bku~#1(9_}YY?MYjoJM(W90xkiS^!tYSyY_KRzyoZfu*1`8N(uafonkf!)Q6W ziDnhib@pAzP1!HPcEetY1g6b*lKbphXteCdLHlvt5{1n=@kT0TBKp%)`cDRO@}y&R zV9I<(&%!@gcYx8XQ~J)0)-@bwB_cqZNN>0?RO2Mn@L zkU|Va;ULD7q%(C&1YQB!{|J^j+6&ovuY$MFMLSV8-}-FWub|AP%{Qm|>`iC`m|XdK z?6uh!^hED-?!&+A)zqq->2OyQEIT+~tW+Hea z=K`f0c=mN{;IYp4wDN{N40iaEsq9Up1J1)xm%W*Es&fKMd(&c=9CW%OBzp^0PH^~A z%IsTS0zJjyK$N|8AY!Mx9^A$muFAPS7vX=Pm9@?=7Wy_8sLtuid~fGSveMzpw6gD@ z%y~{CI%4)8r-80_Qc#HOJ4vr`su_M4W!5_Hph;)n&G2IhL5Gr+XbV{Gkt86xplavU@p^i{uMC@KgY+C=1)XH%~dYFHy8AI zK55;rLq*Om)QCCBqHzXS40*RmLXJ&G39aNnlQ~cpa$YG_>YFAdrb+KbMYy zdc-qKx3GYU-vGLkjV#Pi45w9N9|H0?wjqFZpQqoU9g3m9^7VTqa$}gQ)=pt2cJ<_Z z&CEaZYb!hAc3+KnvD(UHo@P@wL>gMs5_KND{M?IT&7`lEM;NHCPtcO1ucOuig;TUy6bBxNH zjeq8yhL&j)YNp@BkYe8DhWDe3Tsr?DG%WKey`{~6HwW~yE**znX};vr2F6$O4NVus z!+i5yV?S(gqMt0tf073M)7=4IiB@km>I7Z*+Qu}cNT#s`b&K)>+<7~u9<@vkVmJet% zRhMT_ih_61ZOq?{`2UI8ZP2nCXCqFk3*)TES@1cPSZ1=PqyX_nIFD!FHpK15*@)9q z{&jl^NzDB9@PpA0^4;t3J9#soBr+)h#Pf20&K`6ybE58i z#&U$_J;l117{ish?tu)XP>)q5NXkQZpKiP}<%^?XK3)JWTpc36m;yQ92axNZ^)}V_ z5|y&MBS3fek0A(pAO!PjG2$AJXhGF89gWpE+hq_LgVWg33(`e8`Pn)-I<4xZFS4Jl zx6%X1>+9@p%IV;HGie`Vg714{LLXxyU}7I*5?~U3WcWAcvb9w%n?hbLTXinyP%Fm( zJVL}V;3k|!90$O2@$dxr|Cq}&7{!g(bRi+CKNn?ec`fAS@|w=&18QNL5?it8+rm*m z-ylJ-VquLfO(CWF>FO(B^`#?!)w4@S2>z&B3s^M@r?KU<9Auz}#2@uAHUOf%>J~1i zWIa};{3UXKJxuGk%WgJci;lwsg()l0Ku8ei%vmtWL@siv7==19SK2Oecar zk#o#IB=q~JXy6+*YAxg|e@_k3?(_RFx5AH;T(LgJWFgG+l`sB9v{gMZQIYOB zY51xY=$;oKm6;kT;#**iEq9`cxNc0!yu zNZOyB!R+l{4rj)oib_#w+A9@Mhlb_%jX}WiJQMP(Vh)L{@X|pb*j=l2%1OR>RC(zl z1k%YXJ#|+gK$WbMV4cHeFvrWG1isgcfSh@#BI7dMpM30v?x=8??h0zi0gN#`&ckVJ zc?P8j|AwG;C{h{U0(~lo@(d8J6DrUdb^n;=N!|j1pv6|mAl#oqS!p{!)a^@W7rLkMo=XR6`lry-!}@+0&xgL8Q+dwiy~HVprMEE zW%iWYjZiK37742O0mM*GZWvRJItNxU740%VwZfN!PeC&;X7AiL1VkZ-!ys@}jzDd4 zm>P=H{Ko3v35rifSQn%S&jG;;X+u4U@-q-(Zm23hALv%O_9y298%a5?$6E$k9fVZL zSx_1o3ig0GUUQrVcdBAmEj6j(cS1dnI-JIqv7KSdBM6#9qn-u97pIlI2I4^wh>RY_v{C7F%c6fAoMzXU_vr?}P%2U; zu1AhnrY@)&RT__fzroiXU=Vf0?og$r2#XJ6G}2)cjUxIB5ha)jRSF^~xI74F8W;s~ z_|a8klfGZ=TZklX58dSU(44`@#%R=Yn(y*RY@-`Hx`vmU9*9gxryv7kOBcAn9Z4T> z*VQOwk$xBD1J*W-q+#&xNV-!`NV(w0=#lh6SJMDqu{0mDsAxGN$I>homxuXhD+%G@ z^AL7DBGo9?AKMot%-vR)cUzuj4Awlnq&l1neIDn*ofzo5AoW!UdIK`y8$pc3wkG^X z5O;wXw-bahIs6phYew+x@VtLN>}?Rk_)hp?-|!#b3SaKWn%Zp}!#aQ*))oK8vsde6 z+>vWVYe-P>Z6ui0ZW7Ggk2RMzXBG&Zw2T?O2Cj^m@uNaz%#in2%9trX|5L`?CShzX zV~%woqD-k+0|VHq#-gZY&q3g73Y36wZ7r{lkZJn-vx;N8Y_jhpje zPHg6Sz3=sHxNr4fZurd&KD~(Duw(V$@6m1|{KKmU^CCcme{{8;rSCybt{eL6t@fin zPVuEE^ME_iip-~UUT1^v@eD+t0O3y2fAQT9{&fl-f@S8@I^WO9uf;!ex8`H8haRBc z?)r@8i^%J*j{MbC48FhKK|Jf@<-jY!PpEJG)?#4O~=>fvrFm&+Ep(u95-~?Y3JEnLo zlx%`T=?x&btQZapyy-LM{DZMaVmqo*W~G-tjKtJ({+2kprmrfcQLiEWSvqC{V^nD) z0y&iY!4uy`ao_oO04Dp+zdMdIvhV!+;#A2I$+kJmMxCW9ZHt&LVWr-8HJj(^Iyndb zCvgHO?SOdN)Z}L3KUb|#xs1eQS6Ylnn&rTU9K^9>DXB#mtpabI|4VF)F3399vh}WI z*NJ7Di54}f(n9>>JhR+Y<@fOi%E-rU<*k;|*ZWk-<48bHdPVYYsG}1x#8mDs zxy`=fZB%art#22xS4`qn!Yy4P9ZxF2tteZA7LIfIFFYRK!u}QU=0KB|`;6CMuY28R zpvhq)uMkZJk<8n@J~PPVHK6>5*lGrw+`s9aEE7^uzP@xeRBOwh&BP;3W}g3X2=pku z6Q4X1gR=RInXg;?NjfZlHwHs&rORCJUFpnE5Pn z!}SHP%W**nR|E2Y#R|&2&g3=<-r&`;oR`_FzA|}f&)~f5mi8;Rw0Z#iDypft$#%n^=P=ver7y;`V0Line^S1qOaF+%nw@-n zpwjjI44+#cL-pv=8GD{#S`rsbI0cN*CW^;&`$>m6y8X0M-Sp?=y0Z{QZv?v+Ke}`G ztVX}Uny8H|RDxItW8pUdbBTV5eyv`p^g`Ql=aL0_VRAb70eWY>P{&^mev1Cc)*{{9 z)qoy}P`ko<9$D~q=EMoS4ttjF5xxy~uO9q%C&Ztvg?$^YTRm9U<4TF&vD)3(-eavn zdcDZus%1A$bD8x$il7%c`=D4=>P0FDxJscDx9NpgE_70`pi*PmZEgkeE@Q8@M+-6vOp{)(HF$UAj-oad>b~eE*k^lQG}c{9R$C=T2=+( zbhPsFMOtRd>hhJE*tWWSjVA6`UA|ruPp&T80-_GWWp{&EN8(`+TR{x#45H$%s3I;Q z;(H_N8H7BAsN$Cq$^oYI0}y%$L>W1n!6FPy7?w-;0=3sd44uGG$ld`pdi@Jk91&yJ zs?z(ZI*b`a!d6g=tb*67;wKTU^LZ5n^QqEuQTdd^-^-5IChtjk$_ln3jtwl=zj2IfjVZE!Mpw*&vq0$27Vh`*9}5yXBFQ{Q6z2lyAl!oBQE5dA<5%KxuI{{Pz zMzb}2m73NCVQvgBrWY`#gYVWzOofRKGX^3@5LEU(wD2jJN@@xs27k&N<9jtCU&%47 z0KoyqjP-qum~bbAZALOx97fD*AyB1UK(o$M`oZ&ZDIjLU!3*?S=4Gj@9*ktC(XEqf znRgLG1?4&h9cRmGRr&=2*_hAt#Mz3`n9+)j$*5D^mNi&UOjuW{4MS%k% zaG@uV3IRQY;QAQiXP{Ep0j`($v$znGFm?*$jH=_}WK#&lR1m$xAZCMLAxf8n;70V2 zC|FJl+zc>p=mBMi9#A-KsM0kM9}ETl0W2SP@2d0)gmDFz?S}b%e9eg}{uj*9SsVbt zG$%HvDLI0$$kl}{lvg=@Ifo5#ZwAbVY%Xq84AHj&@U{c^!5lOVV#dS8oKnH@Cc*TN z(wK+^oPHjfl8|>fAj35b&BYKQWIf3;?pvnEav?B=$xha*l9gJ6-a?(jTu#>4HP2^W z)AfGo0)|f4Tcy>SyqXr(Yf^8JF4W2N-r^!=(S?`irt2-yq-Q~%thYmRDW$qj*9*IQ zw1FH6&7Cpu!F2uIiwCvNyU__vm*jJQLcWp{A@%0xX1Gjl2( z=j0Pbz=v(oP%kt}vIL`Sb5--L3TWKNT-;CLl46uz3xU`HNppHkc*X!U4d@=lCV|07 z;o7D$%VWalfIo?udVysZA>?v|lwAelX%NFUgDATl|K4Zt{UG=lb=hMeOxRks2Sf)F zuYp(q;*|G5u+Wb(^m8y@kT?h;4{Ob`qaenTh=I!%k!S0@MHwjLqd7v>^A~%vd_J@aMf4G+WL)Qr3`Pe1?&Iagrn=Eet>W$k1Hc-8BJ9RH8^Ka(! z6vBb$HPK{G7=(7BH(7tvo2)qOn~L-^3V$>CjKXN}y>zE{`AfhLIGvlT*G%kn)e9i| z&&7Lg8Z?`Lkz(j`2j=Z)H5GiC%SiCz>m;f zYI`Z{(Tn+s`u+|-YUtWGQQxoGO;!3f*<^ixrvT-3@9#{}nppk;Lpnk8R6k43>)rzr za==g1>0c&qhQLqPd>+UFlgXc~`A0zNYe`SQ5Vs?`%l{|+9bn*K_I7iYpO0;r8%DTS ztuFL)^zd!)u1Z~`En!m~d=Fe|UM&2Ra#d@M=6SV5@2oEIbBBwoC6ynbH%(G}h8_Go z|22a#dhlzY-C%#X4f;SS2|oz_Oeg#kqSk;I>ZK`bkEq)aQq~2;-%0cXk)8{Esv_P{ z>RJ$EP_q8mRbJ`mu9COf2IvjYwSI2O27`AuNOx(6m>VV(ry^WWR&1r5!1u$l$Xv|j zL3c!OdGMQ#(96xz5eQ?4>WcgzO#iSsZwTvl4DSHlC3p#Mg>#`J^%B9`v5-iW$+)-JO_ANh%x%V)lSe)X!p@W3d zS43FjdYo^o;@J#Odxmx0?^TfXn^(qb=bVCB)l7-e4|-za!NPoPM_g7J@&Ysk_=`#f z8?_Qd)q!oFb`a?U`WZ+>%}}R*im@ZEJ^@Ui7yj|o3GgTZ-7xb$>@Kr=3`ekAw)h-R zwAK?f+ywf6lA5_GfD}E?X-+W)yn*WH$$#o@HV5O(CO73ld*@vnKie1B(}9Y(_g7x4B(klP<>|-Y7%Gi z!_@;(P@bXM8V7SO&+1{wjA#0*)fTeknVVUY1?4=epRrLPIJ0I~-vIM@roX0)Hb%#! zlmuT=R?rO~WOmGsIEKS=7f22nY@iR?^X!nmyp;%DT3fqo(;8{oJ z;O3eo?tlOqNR&IYw)R@N-!mOQjM2WMHe`+G`Mi>#2DSMJ%_)RukiyR_LfWfXL25yr zNgfy-&m8mnMll`9j7;g;ktJ&BdeN|srXQGu9iaEM+dq&&d#%LQ84sI&fb%76oc~&!3FVO}=$o9VcA_Fb( z`f{v|`do35q0Hchat!NzqwQ#+uV1q{RM5Y;|`_VZZ973su%vh;mIJWkmuC**1ph zhPotrDkCwsw--Ir6HeAmXLz$>ap4Fm&PJmg=^B7#S9BuG_P_VO*fa(ukb0+ru}n9~ zI4Q%+Y@D_k7LHFqDAsZWnmMsKA&WjqH&TR8_B@BnfX&{a?vvtSCH*;-1g;TKHFg6c zrJKpgqE$1e&;f2UIJIS)ZQ<6_niX(-IlY-3(ryr%&5Rrjc{8I!X>gSUpq&lTRbE}F zD8D30w?^1o(;NT=GjQ^vo2BA^^KAtaInWN2p(YSwa9ZknW5O{6CPoWPJchueXn{$` z5cq$Z1;%}rf`GAHN#mZpdSo1h0tA-69^Bxp@g?^{@(Sh5IC3MYM4sEXT zaQ|bOM-vFl5EVsm{WXD5>xL4}!}nf=F-kvXwbfJL#d})3cqEM+QAI6AprKA8lxuPVL52YW_g1i$ zz!n2p0vpWnT*#;S^>fS=n7FlB^+Zp#8$s=?ck}~-r?O?^cTJrXt#Xo9S*lY`B2W=k zPTERpj2l6?T9Z=<>v6g=q__=46CrrnHV^l)2z9g7&58E^U-1x!04g(8Nu-G|%+0G+ z5fQGBW`Ss9yjZrlBAl2K8|;YER@KG=qX1r-WBbU9CC1=Hl@q3G@+3kXPI|EA%!>UD zWG50lf#wQ~M`%V76dm7c6WWSBlX7Vwh=eXeTA+~jxzp~ zmI)Y(tsBiHwAJJk0|CW2sEO5uauKM_RQxEY zdkg}M#EEDL(U6H14T1=r^x}pdCdE58h#>Ilg6n!xykCb10-uI(BS`giBM7{e=SGk^ z&y9%A_CamQM8cnOy51%g(6SQ=o;?$_J$$MIP9k{PCvI)lJ`rgnM@Y*~B6vzBMJbsS ztz=S^l1UK_AXrvYZP3UPkd8hiUcXLG;w>+}F9=5)F_l0gklZ5DQ-+%AnZRI{qTCH8 zOv1@vqU*Y;(Ry74r%{0$d)n_~CpX7Vj*dO9V_c>G-7zfK|9RcH+W&jqQTzX;2I=|t zxUG;w;T1U1e+g`Ru^2<*zRmC5aVxnPI2e%x+SDBB^_4h|My`Bw#h&D492Lv@4ZGS1 ztk-63!R?kdZn~4B6 zSXb>7&&Wue*O9!$(4`NyyxBOh+#y69(L^tKIk-jDL7w8MAWyX}vKG>sMKL`G<;~@c zEUPYngd01fId+DZmb1z>oTwoJD{O#U@Lt)y^Yyhs{ZDFCpidW!XXz;(SExD8V)}R) z|KGz2&wdJs)uv6M{bO9RxvnB3^eDtIIBFV52TRx3u%~~=KA90e#j+-Jh|W&_aU#KZ zN+{Cg6at$kLJ6BqZ4!Yux7`+v@W>GYg`ttaq!>G;RP6*FKCgNgGredcr`f4<>U z{~vSj0UlME_Hmz?lY|f;)Kx^vB%wn(oq(jwKzdA}S%#De38a}!ASendB3MzxMbsDU zie*8?6@^z+bZx7k=(3hImQ_?#M9{eUuHXNDp8uIS8RG6<-+O)E^`0vyzjHt3etJD+ zEl~6;rcu-ej{jXbNd?`F)0!X4wWI|(+0=#**W&D9T)etFo>NHvaYSq&Y5U;l5>;eL zTZPBfb@JAKfbx8c(@5Sr7_8&9Buo_AR|5N?L(q4r#m^zJ%aHS71lW4)E5w^7- z!bri1@rw59SEzNoleP|pRmg0>)(k9F5)tWo#AO7Y(`+(gw_1}2IKK)pxCWaW76ZEc{nT8njm7M7tEQlGO<%_tB7kiIl7twMwu&O1WX15y{6U2G&nk#ZrWYPqyDh>t!nM@a}y!)Y4JR&AAwSZyvCZ<*WuX~!3LM9PIIN1K!y zBOW3Rm`5ygoVnHVP7~wIKn;}JA~)i+c46e2>1_{8FZaOkL!8JPD{P_I!$>dyyIbg4 z1@d6wsomKB7jFFsc(sm$KnfLgOR!B{776ZGmx19|;?Y+b6-jqAP+tmUpU0_Qw!j6B zSvcJ|ZHe9j@xO6K7;`s<&697Q=|jx#Re4`3j|zq%2ap+d+vrn`#Dl#ZOGF>WiSPtk zUzzALl;<*msJ4506t;(yr@4ZGC?r&!u&r_-!xXBNz-5`sMXWX#47beXB1@YK{@XH_ zi*JQ?{L*k-SALy9&!RRHN$QyyS4GdC8U?jI&Z6)<2;_x(1t4EgWCCEzCz|&%q ziBcd+utF6d;sOO{+1tucZ0r7|?R?lu$ylDH>$dBD4l@?97ruZKkrjwHC82lL z5ZvzRDm}j-PAUmWs<5-ZoD5SAVG#CFK6OC7Jl#UY%Cggm)6SjR^mA-hvwS`H7x zk$jlTmbJ6sqFTEWFrYQ7tb`-fHLEQDO>}`tgJqA)P}_mV4eL+~B7Yq;W%v!(=&P8h zUw<7G$j#q&Vb|57qa~1RM!rg*Q8;mFIg~Hzt<(+$@*Epli9nKKF96%fC{M%v6KAI? zV;PDcu{=wuI=cwa#Tpn~PJ_`vWm_~77_Bd70a-XBj1?F-hi;YYQf*~LR$NX}Mu8qo ztWKG(s5zm`q)A}D?OH#4 zs^dTM^L}SP6W~*-seNl$58C`WWpAkzT^TT!=m;e}(pL!g(fl>=9h{gQ z3l7s*sAC=IN1LH-8BV9sqA0P%#*m7wI1Q6&i|iw*LtKjsJpp_KR|jxn%oH5P*$v4L z^JC9kgfF!n{;R|CED`?tKg`*EX99y*j-9+I?lgoy$w!KnjMjH@e)o6K`4=s ztPzS1qoVt)!pSnipjE+Yif|sE9%42Ac?SicQaBj7mOIZRWv+~vzw8s4Mf<3Gu%jNLAJ}~ zK4ni2mey(*uEyENXf`%FnjJWuIL(dfw_L7)%~+H_Ru~bn8y&oegDdM2j;%}hv9w-@ zD|rFO*1L<9^aL;xe5@|jido7Wrh?`*#glZ4$j^xSGrD}EwFqB zw{&4hpT(9>biqMy#o|qeJ=Y#~aIq=v;%=NyV?%*(JI+Ys;r|AlFw4IQ+{SAl zaE!zivR9C#F4azf^yM%WjCRUpsX!j*J2lf?aAZvZpwGu? z$!)lP(N(~nWTEc}oEH{`z%?UVBLZ}OLv$6vX^2M~PT5(pj?3WpW1L^&Jcjc)&S==n{zyNZ18~ZYhjm2aLgF5cbFgIy{ufSJ zEn3TR%j!{>J&se>RkH4~7AKayyzn#)XBN&poYql<>$y13$5~}FmyRF>r{tGa4&vuR zoYrhkrf=3E=9l2ig4w+|@5lKN&PQ-Qic_W*`*7Ni|I78K8V~q5jh|7|oQ|KPdl?QK zh&xV4LSjN z{T-WLgsQ&jpD#}}e&J*h!{E5|x^BS9%CMG2(;P=gz_$vL0 z>GKlP@e^z173GO5S{oW$;%giI@lCX>XsxZQN~~^e-&)ewZgV^ zMU$@u5;|xYSNW=OjV{KEtMb-5zt>ND^o)UPJmTnuW2767AyihidFvV%Yb}-sm@f_Z zz}T_WTZe$*2Ftjm-o}b0wo7k|FJR*@Z&_+t)wlY6ZI*x^`>`&YYa15h9X?CEpw-vv z(*k-UxGQg}K?Q^f##^M(-OyT}*;rrS*Z?CWw8>wC-jI1<)g4%&gN18mR*ejH87S>C ze{G%Q5T4jV&LZT@#s+^m+Gn#i%&tlmXIg;&V1Cy~elPV}^#d-Dc4U;dw!Wzj5=K(V z#ztJ?85CTif6~{;x!QUr30*e=)#g~mLM0Z|KRO+!IZccKmcceN#NZ7W7|t+9L>m5w zMZ`G6;X2G|xgX`|f4Nc+H2Sf-!!_c}5sAvz{D{27vl3?%Bo0eV#q`?l^_?CL6F=7G z>}2?C566fYB~pO_eCn%kF&_jkMd3lbNbUtQu_b0iU}FHAExITA+2gv5SZ zPls#dj6{U`$+CCg?T+r%`H4egW6Y@eG0x~tQT+q*a3mc5;X)T2z8ao@>s{*lt#JR0 z8HqDu%{JGh*r72n|6oNL+}s_JHZx{ejM>W-lFj#;j>KUzVq)TA;%1x`bJn>@DM=Yg zg-NL>k(e1Vh2!(g-maP%$v4KBzspI!G4Z_In44w|NscKHgiFI;>5rLV9$A@S zZZ=)Qa~I4y>#lQ#BrjYrYi*Ia{=yvdM0mouk+@1QS4ZTSs}<8se}Z`pE-#HpGh1B9 zl%(;oOBR?*)679HnbisAAXnn_n4~LbOft{$n`8VlZ#3Uskzn2zkzjtbqT0MG;WCSU3u~oL(2Qh zZTnzP$BcMaOpLkOf84Bhnf+a{NvNylvIJ>gXZl}@H~(t5%!gJbJerulV8Sbj@o__w zR%Vz{4@LeiH)f(aXal0XZ^E{Ev-?x#q8!uZG8ZN2|8pjpBV4&q{btl<=4lB-hT}I( z>}B^-xMP8etn@lg`Df-ttcZ+Vc+SGOnM0Gtrq(2xO%Kg9`zA!@nZ7i0a9T)PI~e82 z3HB69)z(=_!xB}^n=bM^`Dfm+GOev9&y$#Mc26^xr!6j=mur4wUS|3d%-96w?K1RW zGvm$qY35khsJylX6J{>nIn(SBiAs^UA~*KtHA9n<#-?N^nvbkX^G41|GG8}bNzVRA z4(1olar!)OR(ff=H$T19$n}=wx!pxZ{rv2L+494)n0w?E z&i3ZH=eqN}*#(|LYmo6)Ha1jY2&lF6WEN(*GrgX|V(9UvXBB1U8;iZ=mHyggKCg^@ zordgU3{ysC)_iYKdU1)n*qc5VzZ=}L*n5GuOLh50?%Ce-^h|GFrgmoJ7o~d3k|%i) z=c1BgZ(**Hk(cXD&nPS|HSjaJ5`CRYPh)c_h8!IjTxQ~Dl}p^E-n{gZ(h~RVe0M>q z*OQk%+a8ODB@mxLHl=5#8j>E&6!5Fa%*0NK&qLb1=|;W$=yOT!g+2p6 zs^FcQ>`gYB%a<#IhJaNi2JC;KWv=0CtMz;8$`>1YzGgHv*ESlz`4iC?Y-K%^S<)%D z^)HKOs-SEItINMr)U!&mjGFR>syd(1xEwP9Z-cJ|KmDvSx2P~LJ9B~8lZ{GH-CSPp z^R`IhLgJEDR-9g%U0Bd5KMUs?cz4v>kDzp)wFPd`zcUE?~7hMM0*4*^G>@0LL1+(4gVdmRa7tWsDRUyvH zPxs~(mXz2!o_XoT1(H6@LiHk`)K^z$Frx#%h+Tp?DQD0~5%Nbn$VNUG^(DT=^}Yra z8hQumPSRU3LG?&ARxMJhMbiqSzT96~`(3W zNPfA!w8WdATIS72FUXpgomD!=XlZP1u4HNGesA8~eAEN#5&^(3cdO6d{ojp zpRY-h>062_rD`4|G*Df;ByPG`=4NNP3yq~%sL1LB{Ym3ubA5;DctbGuUDvhjGRL|PRhO!pb zi%TU!Z84hC>nd97)0_Pmt5~NIFT~iWpX+OGfd^F;)#6XqKj^UBhEJ6!8kz0_OLSh> zqbtZnkA@{Y!qAzno0zJN)`&Q_ysoyYvf z%uzYiFWIX`D_tHIQAOzRD(Wy^NN;KPD|I9@J#!9PVtT3Dz+z69l?)eU1-S)<^9qax zE5VYpsJrNs(V(FfJs6sR8>54&Kt<)1OMQM7y}&w6Dn7DWjR@Z5%_xqp(wCl>o@v-d z1wHWTURmiw6MI1IOah<|EfzoV6gdxy#6dEuU7E(0VmGVUMAuc5#ER z%2ioY-W=$GOWf{U>7AN<0xj)e1S1$Fc(M|Ft*fa~$^W8>$c8jXrMpGuXk}6A@e3iA;wUw%d)>{qGmcf|S zw6?%oX<=qp1(;r1oN2h{XO|k8vIC3pg~NcAuyW~G(dz6G#sn+VSejqnrYd-5jSrQz zN_956j|ff#-34>)9%Vo;Day+(^%j+7VEW;87nJ4e(M0Xe*s57zf`%$4El1`SlO~`u zl0L1nv9$qvK1Nles>K+vtt)hKGKaOdOI2d_Ie<5_u&kieSn6+X#TIjOV}%c0Bs!ht zwN?I_+=`|aqf?tRyt?|!j5N!gXJAK1KBHt0g2O|J`)n@~WK^*qG-LzCXl_(9OS}7$ zdD$4^y=p{4J;Thqu}L~ktE%J&>xmkU(I8b-S2}b%DatRhDyDmGsW%IGlvkLZh51}@ z3C5~yRF#kcPp0rbRcSDZ#>i+dm|}N6-oBFA8fJ4GbkscCHXZCd)4)K$PD)oc>90_U zf<^Din`@{(5^c^Jf;;7b45^4_$((GDp1)%vf3{>_a2}SIVi!_%zpHYyJTUfjG3_nJ z+9KElO3~H2jh5C*UyCkTUE(EbLRBpDDTyX(li%m_RH=$4>jccqm#J127%BCX+Kk)kIc&KqRi~sdGq>2CITusZJa+*n0%du5 z-lEcCrAGHIstU;XRhBZrS}9@0hxuBtCUiANt88kO)eSlozfswWMF3i}q=MtKx3I{~ zNq$qg%pC&V2*&XiOh8H-jiq`ffp#$m!zL%rOJ%1NvkXmZr59+pXzboOS;kVe)vnfz zUOlVvNw&HhWGh_ChU+OTjr4N1xV&L;r*()l;9_?k)**A7&r;7yIfPm-)&XWFc46+}Kcip_+i&N<#Os4H;OCF2$|7 z#SMCc0aFIA2TL3@b6>R&tAvo*Wav64G=dn&?D@=`lc_^s9+6#;U8+_&!9Fn~ONJKI zq+m5vOII0BtU&{fPGzXO!QZ?>&Dd7DxV*^^VIcHYwQbADu~Wv*J^ z$W+gZ=70%XroTZi{#8H?jsDu|71k6}v>UCJn6y`8_0$H}YEEL+S(y`b(f7)74|TR6 zE4>(VH*10CUWO)e%7`Qjw-j-NOv#pEJ5T{n>BcaP3Rv1kJDK>Yu?`D9S^lUp(apa& ze{M!#LgjDqs;P-;w|b@CRpezeNcVWBY%^t0G;nbh{0WQgM-;zLM68Q^sQ3rbS@iC8XkEHtx;9{7F~qYHLf%mTpM{ z)-BeW%bq1ziOfy0@=OZ&Y|u(^{HvZX*zBabloeqL;X~FMmnsdiRIqmEY(uKtp=`#&vfk$}ufm)_x)hmZd(d^FK$ljR*WoU9WnClw*Xs#G zbCwTVR;rN(dzauwgsqP#K;6c`m|0j*;La??{mh2NE~yi;Rj9HRIf1=ZuT~$jm*B>z ztEO8h5LIuGXKJe0MOmoTP@tz%1Erp8lyW*RwG4aOTv6*jNzEPfq)e58-p#dTqmEs6 zLzS;BSk%f@V{?fiw_Uwzn6U5V78Pep(+>>VYA&nx0|U_u?60WS!wyHyK}#{q$d|6W zv<#V+g$)cnNCmbzIIGI`cvSYI6XRx+j-+;;S>9A$S?gb+^Si3CwE}wo!wl*$v`Yv)mJ-=9W!YJBQY_FE9jagMxQj@GLC@FLu17#(< zWx-yExnRwrR3L`h66hkl!lJT}p}*6Rtd?uCOHe!)dl#xLs2vX5_M!~GDBV0vbEUn@ zgu2Tf0a}U*3rgJHIhg*ri@QpEW+CpB=v= zovXc0F56J%9NFYmOD$q&hZ{QT?ur^L?R8FOGIO#rt-+_$ zZHsh_HI2A2qVH>2Ex(9+Ojv)*grpKrbEp}jY94yqlO>~Ts_uu4Wfje}lE?63rHuEC z0&5D@_j-#kJO$cGVB4=Nvr^k$c9uJ%Y__3xNpw5lQycn*nhZeYb-K^t#;fd`6eB)* zbI@LRDomp?utMRj1vPO&XRf+ROd{0q)uJ1^zg#t;274cc-H_P@WvKfhbtMg$wG{$vKdU1ZD4|i4YC{0!T#Q2Kla!jCI&Aw%|vLEY8nUFML>ZJ2r z@z_Rhoq@-s@PJEv{RGJV8r+Nlt6^TnUMsehQ@ebTcuC!gTP01V|}eZUQMCm zn;KEjzGfKe0X<$CC#34u2HcQrY>02c+6okJGLG0~sKpa_c%~+_^xcJ?u9}q0UuA!| zn(I3I5RXYe@?R$AwYbBhDj!(aAy>_Y0@ zOWEUTK^E&yxl>1C^~2U|llN|w{(8>80_{{yelVsq%Qm_iEVE=Q>$g_8YU5rukdP_R zp*u~Es-*^QZ?~4$$vj4;|HulJD=L@RgGdjiSNQpuBB&XHZHSteKG!OwPIEh533xN! zwo#3)05{T+CN(9iMB8lEGs`JPy*#TWBZcZ&x~i!8dAP67&3i@{eS2U-fpsSj<3W8f z?^Ac~?bU7pcci@R?IqaS7OLx_$5-o1Rbl*cBek-{P`7FHd?#DB^3?)Bb$A$N0Ev7{}Rvm<{ ziE72xMLJ5d@u{(psALu9Wh}syadviTI=0^OFwS>Xodfk+m#P=5gHTP_R7bL`{VX+v za$OjxbER@CZdtM##ne>8HYj6)izM0Vl{LrF_t(3O6&AvFADQVM%wVuD--0Q%O_MCO zkhJs#vZb`Zu+I&ch)x4>k?a{uO~RPVy({(Xhb><+^E2@r2c9Xx24*3a{Fvyp`Ks7l zy2$zbqU5eZ$}dXk+Bg+0Lfu@{t-HRwP4E624IBq6WE-jx4JNq2(AAN3Av??VP>fo} zN++Pp1HFLS(|4DmdzHq{d4*)O%qCS3YT0BOtRZpV&Wla! z;GQ!!hPcngmXU{>1J*+({_Ub`Pk*> z){IpD&O?w)3iSOS*~n9!HG7VR0-vuctF~D-%|p{tR;;#dmvN@TR;2p}?zh|GhR0os z+?m6`LDZ*pru^JJ-66QQv}PH=cS%&uU<;$(aY| zNhNOEjXPs@X67{4Xqk$a;_i8i3va!+Fzt4^Iqh~~zAyKr8k<|j{I6%~`kkj=ap23@ zE2Hr-b$JKMI^=z!eID@$GhUO}iYi zv%lN*kh1HkW0w&<+Bqk>r`@l-7ndGAP}?u*Y$xw%+0*8NY#s8RTSoNYHQla=SQ~z2 z*gA8)q+Y+oTH;ujzh8p_3I6Hcv-MzdA*ku?Q(TmJzqymvipM-zq@(z>X5WnG>dUcpT z!HDS9Ex)@nDxzQ50LSkfr<(&EXE+8q&U6e7i0c9?kiytDgABNz7KUfP}$_V&ZX8zY_FM;VLY zK-^18@go=bA3RLFGt$|85A}@*ai4|L3Hxinq<|OsN_X%u@m5J^|GkXsVJ-fpT{~ef z{vXGw4^(#BVdBM+&i;EF+h8yLrOi8GFaB$A>H{yD*bWmPYU=F2FF#MI`v|A{kW4>A@I32d0Ps~j8?w!jw#rUjK{-lVrz+vKJC7s=;s?U?2FD<;9i#M|NW-84Dm zJ#~kATU?3ipJ_}C(Kj(@i7OKyLG2vg(k5c^Phc8GnB zk;n9kh3J_dBG-nd1w2y>YU$lpJC`*6W7xn-1PVxxjBxRX{^uBut$& zd19w0G83junmh>`^uZkyxS29Bu=faQLUO46)J{)zrihM-r?{Egd38TG8O|n64Rw|} zRUW9)ds^znkifH}O6_D@WF?rC&iFKvqc7J5= zA+Q(ZRS#{;i$lRD5xd-O;A4{Z{e@GXoV1sZy4A}E+4=NSC&lVHXjH^5-j4Bl)%wj_ zn^J|u>g%fd*1DjOQhOnu%22OX@l_hjU0qe}viioLzdE}P*x7Wn)?FBoS}lL}cOE*$ zm$ym-;iOc{6P~BO`B!h1w|XV(l&8?UxV>tB7E$G3;5`KCTcWU3k-dbdb$b2dT(8&n z&a3oT=d9A-%W~!z#w@rK*+uu`h43$9VicBiH8h7RrBC`I(f6bf`t}Inf1l8YF@f?+ zLVQE)HOdFbp`gTTwD4*jFX1eaF+dj!uhlNz!fO&DJiid1plqW&5{$Y| ze_ksJ3%M+jqo@yT78&7g7WdG1n-Kc838C+9A(Y)uS^C(h8}NM~A^gegAnHcLC=hPc z@e@7gLKZ!XgwTUO!#S7nhM?lT8166zIP2j~cp=P%uwqjF_%xUi@cihlaybgd`Jj?9}5xB zUufSGW4QR2uh2^QjTOS3d^=X^mwcsF^j;%`yX`{6>j~P+r(Pvrz9iGaAjAJcA=*b3 zxl(v5%3X-cy-s*L^a$~xsQsYiQ@h9r?@Qq}l)n(=@QV=oWW6iy#!7y}T$Ll@eXq!< zZy%FGCI67l&7`bzrJl(5tfZct5JFFf@I2_JY#?96JW2H2!FCE-?QD*i-^qRl`EX2R z#J3Op3stztC;3X5AXgWdUm%E?)M0x2VcZcl=ZR1bG#7oOcEkK6NJmq z?@>muhI2Icp0MDE6d6nSoatY$xKt1W)ySs#X zE|+;6{DHqCp~`SxYN)+C=ViK|6ng}z+{vm$WXUICJ~^MPB%8?PI)KPJB+|3OAXX#M@jp=2^yPp%-ZAa5YIkx!5>lW&tB zkzbQ?8$i<2gFKy#BNNH#eZd&WNeg9eJ(A-RaG zChN#%vW>iiTti+(ZXjcSGau@j=`3Ctu`33m{DL?ow>FPrc zA;*%F$t?10@_ceBxt`oa-a|e}?j-+6?j@fmUnbum-zMK9KO#RTzb3yYeA(bB%Ci@F2ANFGAalqvvVv?NFC^EJHEv)SnLLZkA?K0Jrm@-cEZ`3(62`6_vk zJVO3T_Q2Xtl_MEPCX&<1*<=a%DtVavJ9(V^mF$THr073`97(2g+c zC&)eIbL30p8{~WB=j8X~uVfEw2#DU($vAQrnM=+mtH>sDCAo#Xhulf-C0{1rCO;y- zCVwKs2kH3qC5Mn>$cbb+nNKbtedGn?#pHT&6L|;uFu9w2ll*}E2N`juj@M9fEIEnH zCKr;`WHWgQxt6?-e3INx9w6T*KPA5d6SWEy!X`679k9D)sc@mE3KM?Oyu!ec37pH3E&=abcBqi_|z z5l{Il@@Dcr@-gxm@-^}=Labpwqx_w474E0HH2adL3E?h9hUL!=Gznt=ALg=}X+)VSkgy@4FrM#E+e&WxSu6NdUA!R>kBFSgosxwc?G$N+#-a(dnj+G{3Q82&0nJYrqE{?f1&(0 zA;SBF@-blv_FIGqH#$zUzYy+*3laZ#a*`1GvS^=AR*)@1xL-keE#>RU%|iHpNO(E+ z5y{tu!wlmiA>#2Rc~W>bev^UrJ%(%hK0>%3E`c^_g#Kle zSCQ+;+sU2ei$a9`S0Vg;Ony!C0V7~O(lAB|VV^`!B{Rs`Lb#hxxtv@=Hq!o5@&=mU zOnEE$2l9E^zeXOU`4M58VSG(F5@AZXzY|uW{g9)Ca5q_q(=8l}_z97Ya&igTDx8b{ zM+kRU)BGm#7MedSgu5pwze4*rD8EmBAw+zRlRpdLug55@e;7H1ED&Cf&$W|v{Xxp_Q~rqZQOe&@{)Mswbxrj36e7IAWCG1+k_F`XWCPhIY{LCJA^cxU^Uaj+ zAa{~a(EeGO|G5z9?jXYwv^cJcvo7x^6dHu)KOoQz7;@jH!-CC8G}$@9osvX#7)yq3I$yocOL?j>I)-zGmG zPmrgh4NCr{k~4*qaX*u?{N|_Bry8=J=53TOrhFB-iRRlWKSjPm9uz|VyX4=5sDIxG z5l#oqdnRl7G$Gs%62jeBnx~ReXr4|vhb$p|WP=cTmQ%i)e3*QK+$V&)gOuN){2}=h z*&{`}8%E9~XOrdRGV*?MC;2LQi2Q~$QTHYMp5#b!I*C_;mHqkTV)9b*_vEcYWL59Cwi0rE5QJMtH@+Z3%o zmYhbq$s+O`vYNbrTuI(W-bWr5o@E$c2~mze2+uQ&pK0E6sy6Q{ggciI_9H0c<8ms0 zrjuE;pHKT5A@p8A`BKVjDQ~2F8@Zi)jNC)MK)z1CM}A6vP5wasN=8o8;r9}v9>)oh zjwB(Tvl=lml8OF<$4+s&Tw<&*0o*=_!=y(qxn+NgugH2zSdV zuO`=%8)$zog!Z?PcaqNv5#Ay4 zBjIA~%TRWp4vGEgGasWAy97e{GW61eWF?8BQuZr^k%nUl7@4|8aLrwOC=^Q^)c$|Fd5cTM)k zrjr?@hb$u(kc-GFvV~kuUQS*~-YOhmsCT}E_fp;NS zQ|nRBtHC^)^6$tqNEa#Z2uZk;NcH?0CrTC8~`;kM*STcc3A=UGDBQZXU44zGvk>`?&$i?JRvYA{)UQDXz z_n>bbleuIuIhU*<>&O;zIe7_r8M&Ukmb{7FOl~FbBDa%|ko(B}iNZy*bk!|Pfj4Gkm@-`xXYznOwJ{JLhQNKlIr&ydfPZwj%;^A7m|`4RaQ z`7QYa`42LzyY?SR_9pw2L&z9%G&!D}L{1~s^O}fHKIKw!K3PGk=QiQ4p7I6cMdT`S zEqN7r6SD{gOnd7pCI><`$_d2C)~e5`LE;$=_6~&3&>V-4Y`iIp4>>@N#097OseNekq`SQ?8Bb0or;{0^hb$sX$x3oD*+4dvE6LU5 zmE_grCh`_?8+kYR2>B?vhulZLLB2)4Oa6^KN**JBB!4Em_tg2-iyTb4$Wi1tavC|4 zbd%ZSJn|fJ3E4olk}Jq{Umt)-$?lu@^iJyg?}oLXr1La#5IKg7C#R7!$xUm%I|B~`I_d?Go$_Zo&IhCA2W|4Eqh2;69daf7xYAH97e)1x66}gtYioA{7M&3_8 zL_SVFNj^)e=YA3H>y+Ol-y=UHkB~>nUr2c|PwGK8vIp6ZJdGSe#*m}Q@#IW0gDfD6 z$%W+kd>&a&){u2%3%Q(JPhLyjL~bUxl6R52 z$lc^VazCk_zearCp!^Q`F8KxdHF<*UAl38P@NZ%-P}1F>Je^d}X~TRh<(Xs#nN8-C z=aP%aMzV#xoV=2}j=Yh)g}j}-o4lWVlH5Z+PyUHKKprCBBR?dMkVnbyN%h=0(kU+} zOFo2=J;>hVKyok{M~)(s$O+^OGM$`5=8@--`S`H;p8|nk(^0pkU3-lc|KV|E+HGpR&oWonp{I(LvA3q zk$00j$j8aO_zq^UF2|bA~}^z zC*5QoSwwotN^%L=K(>-A$ScV8iq=x z`iJVMMCo@P7Sw=1(7m@0H2gGkFGVgCL&;b&g`7lYkRGy_oJ%et8^~qkh2&-A zTJk#bM)DT&cJdzb0rE+554oRwi9A3aB0nR)Bu|hX2-{4?>sB{_f`NUHZtU>`?$ESW$~B&U+OWFa}1Tu4@tHDn{%LS8|xCpVBc zk++gt$$QD|+)o}L50UNUr{pp61c}cZs`!MF zeaHdiXmUK6N=_zck{M(+nNOCI^T{P-1G$V;?;9as)>FQg+(h0&-a+0&K1A*ypCtE? z>b)fBJwUmg{FFRFc97j%IzM`lapWj+GC7^hB@4-ONH4jRY$PutSCLnf*OObxyU0J1 zPm`~bZ;&67pOD{@-;=+RW{gf(Z&JPIgnSuCIgwQFIl(-GavoVkR*=*U|akI8Sy9Jr^@t7>(!lgxHIiBE%YNmJoByIYNvTMMBh}`9j>+UL+i;zlSCF<(fo} z=XdX*Y_-U#xZfLZOap>^WcdsB1q&%3E@0m$_ z#*;~;eAiFRX9-cRS>zltkDO1QORDFe;9fod1UAv!PqvXO$<^cY7>W1f$Q?r5yLp^^Qi%Gmo}&WyiHvrzpL|I;0QalO1H#ks+#z{bi1zV5*)ANW zzvm}BA~M>`QSz7&?dAm8Aw>K6g*2Ryr9F_5!X!NZPWBO^z78PO_r6dLgGB~iLgedk zQhiqr?QOispn6^l?M^+f1x7}|9Qmc5w?ICP6&d-HEku5tD@1;*5h5RM5hDG66e8W~ zc?G0%l-#FBI@R}+M&f-G-nUoJ1t2|NiHvl(5r)M7Qz7CzA__!&w+RuykAxn6zwQe5 zSBT$mslSj%3*mPHv(1dA<9nyJ%a)%J<9U$c=@ntzCQJx?~`tv9k36T#?l>I{FhpMOWuj(oCMb#_Fs$L;~ zRQW?z<&S(ylk$W-ONjh35?WT&`^zgpf3s$db%kG)^cxBCbB#tqMN5m3AO;EeKCA7M z%pqphr;bC6)#sVDne8LZ3HY(gfWyE?f&-sxKGiDt(dMqb20!22wY~a^bJte-d(MF% zbqY`Us`GF7Rt2_fH-#+RWjo4}`?lk-ybcc(3z&fomCOhYu` ztJG4@5-Rz#c>2@Szu_34sJ3iu!SYkpmW@h9;9J6$X-KeBrv@}>_xPPU`LW7UX%$R! z$j`UxFI`)fXfMeL$pN<^>hS?=m8~l80>!UBg>5MaB!Hj9KE+%pij~)>F9;iO2-elw(C0pLGr*lEPcmeVAnVLPkJ3B z`fz(cpf4K!?fUYN5d$q{pp`EvA?~VR@sQ1(6>b9D*~49paEsth!j<}9r9qdQS8??BkoQ3QQbw8p_aXYGpfZKQ%sMQ6 z--hT*dR4FIMIWZ70e$@t7x8Z8Us@lm-@AxkG|cSzHw*eUz@O-gvFRH}eU^$fsC4%J z#fqvs#NBOhHwAjE!%BxY#N8pdv+TsJb=?}`?l9cpFK}4lUJrLD(m?-w^f&a~8KO_z zVM!1;EPYbf^xDyFPF?%!(AuBrG{|L-ff#;jZ$fmhJ#`%!0e&ycSsD`ryD`uMVJI z&4Intt0cOU^13)A+$!kns=jOrad+EqxRY-=+w<=b++hhAIK)nlSLqH=$ER>7@v#mo z9e;uYdpZWb7Hn@5;aJ>>zYjygJqmr64$)~{cZ9e*26rgpz+uI&A3WI8VWM;FsyxPr zxEla>UFF{#y0ZexgS+fmmZZZHDGToG=^c&q%J*)h+@|A%wMuVkh`vqp@I0HXKef^! z@90?9@M_$4F2=1`xzdMaC*QudhnsX9``B?h+?jA{g)8s&Sbjvq(M#|>JFQCnx9sFy zAiF*TFYj%G3rWW;oR+?IA^L7x1^bZp0q5#B3+(!~L7$9;qA$ayZ%c^2_bz6&FhQ68dCPI-4pXu10ookkxRZMSDlU)>O}JFYovK`42 zlP4ynPMR<=$>qWy4*fqlDQWWL$u8HVsgqMvl=-5lRqi~G!(qit;y5}GPXuq=rls%+ zBEZgPR2WUW`sDX1Shpk0<9xtX^XrfQ{B_%I^VTzW#(13fZ=QYP^Koqv z@I*z6u`wd2Tkg){- zI*ulXZ*q+5>516XZByi??we*E8+xhZ5d(d*zw!10!Or){L;l^CY#>VW%oTfRmj$f5&Y+PU4 zkSp59Fw{dGza$&ujAPG1V^i!Qqzq{?#DxKog`P8u!Fjolw~Z0zDbUc}G~5qe>EQBKjE!V{Y+CG2pjx{<$$i*IVRw@&1lq z&PJ+=-}xde&dRy(Ys{TP7VS_W+=Q0m7#DWXHDvFR4jmF&nY16rxcD!=PY*|U(Fb!? z8Mhpsb^PYk>}+FWc1}+2`q~`THf!3Q<9Z!Dh@9)cJ5uG9BMzxZMry|S4;ZoMwj|oQxXpT(&6IYI7)2)J1>F z23Ly5_}P&>OXUjk=P_46b%=FldZX8jX=w9v}`CItIZ~Pw`l#{#DJ>I}4Bf znlfln|8}P*#$)-?K6H(+Qi&WcH+K$2i8*)dJ#s8v%G*{&)MW&^%6T~N!0CwVgWB*c z>g;M&0*8=unY-=fpzBPZKsoeBt?GB6cOYFoGkYOjy>gf|pOB_*4o{`#mDCMh~Gau8r zGfc{MnXbK(N5&}Bx#oeYCL)KMwT+Y-N>8=5FKe_8sUgmt{gG>?DfeIE zzc@C#@Whm+4LhXvgn62FdsI#s$1h!dZd?{}A}c2gIgzDe+SU%Y%|?v49T+<+JIp0l z-8`;EHM^%Me-($T;wHh@q@2m{HC6eV-#(?F_v_ss?)}2eN4gnd--Sh80d{|InkO~8 zNYyOq<1-G1o!qm+>2Y|_w%g_?^Dwl^v7Rv=UH`(68)MK$B}}U~FFdiYBzz;%dv=`1 zm18~-cJhSEiGhy|Q1OX8d}Ule#HU|Qf5hhu6(1d@4nb?yI(2^P(bJ_uFPAah%WLl%pRf1};!qcbF%9h|`mlJzD8dZT{mjr=Lyeh&X3HS2K0oSnBe7~>*75WiwRH0#BVy48+}M?Qz7 z##8CL9)pGi`tKR&%|@#_B=(sdBNjO(egi*k%1`Iit2TG3q|NDZW``vqtY>sS{5yF= z$5BVqY|PDfg=IT77&j=tznVMF>j?X~D}DPqPW0W^fiJGQnr5R;7G8mwjJfdu(iQDF z-IM#+0px%&Zo~(L$Bf9rV;g!rr_!|9{X3N>J${ZnISzR;U*$<;AWw#5z0gs&?S+n- zZIRzOBL5!98EGYcj72(sto;3r{6U@u^9Nx^;fCSQ-8%MnG@<{mE7mm#`kZaMSF!E9 zwtCj_QT3om*Rb+SZ@p4CZ#f}LrSEiV?@%KI~^1EW_wV1PwZ+Ljc^^C#^jF5SzkL=g*>SJ zjDzN_D2Ws)3Bxt5T}EzQlO;}wf3d{t3`w)9-{@JBMzuE#m3s8B(K7g+@4~`AJs{!E zUm&rNE62>tj_}pTq_(8|Cuw>8^Kj`Qu9tdkKWfkq+=5en-%0AU%wC4IUa@1eXT7Qg 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∾Zfl+;Ys0#!q0@C3oi<<2>&HCFb82e{D3bR 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

=Jv# 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
CEzC->ME@X+>#uX<>Qkxy3VKfV!@3nz-SI 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%0AJkJ#KY#5KP?gwKUzY!<- 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 CedarX <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n" + "tag : %s\n" + "branch: %s\n" + "commit: %s\n" + "date : %s\n" + "author: %s\n" + "----------------------------------------------------------------------\n", + REPO_TAG, REPO_BRANCH, REPO_COMMIT, REPO_DATE, RELEASE_AUTHOR); +} + +/* usage: TagVersionInfo(myLibTag) */ +#define TagVersionInfo(tag) \ + static void VersionInfo_##tag(void) __attribute__((constructor));\ + void VersionInfo_##tag(void) \ + { \ + logd("-------library tag: %s-------", #tag);\ + LogVersionInfo(); \ + } + +#ifdef __cplusplus +} +#endif + +#endif + + diff --git a/platform/mcu/xr871/src/cedarx/common/iniparser/iniparserapi.h b/platform/mcu/xr871/src/cedarx/common/iniparser/iniparserapi.h new file mode 100644 index 0000000000..c92839db17 --- /dev/null +++ b/platform/mcu/xr871/src/cedarx/common/iniparser/iniparserapi.h @@ -0,0 +1,170 @@ +/* + * Copyright (c) 2008-2016 Allwinner Technology Co. Ltd. + * All rights reserved. + * + * File : iniparserapi.h + * Description : iniparserapi + * History : + * + */ + +#ifndef INI_PARSER_API_H +#define INI_PARSER_API_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +/*-------------------------------------------------------------------------*/ +/** + @brief IniParser to load /system/etc/cedarx.conf config file + @return integer 1 parse success, 0 parse fail + + The dictionary will save int a global vale g_ini. + This function will be called automatically when other api funcion is called + firstly. + **/ +/*--------------------------------------------------------------------------*/ +static inline int IniParserInit() { + return 0; +} + +/*-------------------------------------------------------------------------*/ +/** + @brief Free all memory associated + @return void + + Free all memory associated to ini dictionary. + */ +/*--------------------------------------------------------------------------*/ +static inline void IniParserDestory() { + +} + +/*-------------------------------------------------------------------------*/ +/** + @brief Get the string associated to a key + @param key Key string to look for + @param def Default value to return if key not found. + @return pointer to statically allocated character string + + This function queries for a key. A key as read from an + ini file is given as "section:key". If the key cannot be found, + the pointer passed as 'def' is returned. + The returned char pointer is pointing to a string allocated in + the dictionary, do not free or modify it. + */ +/*--------------------------------------------------------------------------*/ +/*static inline const char * IniParserGetString(const char * key, const char * def) { + +}*/ + +/*-------------------------------------------------------------------------*/ +/** + @brief Get the string associated to a key, convert to an int + @param key Key string to look for + @param notfound Value to return in case of error + @return integer + + This function queries for a key. A key as read from an + ini file is given as "section:key". If the key cannot be found, + the notfound value is returned. + + Supported values for integers include the usual C notation + so decimal, octal (starting with 0) and hexadecimal (starting with 0x) + are supported. Examples: + + - "42" -> 42 + - "042" -> 34 (octal -> decimal) + - "0x42" -> 66 (hexa -> decimal) + + Warning: the conversion may overflow in various ways. Conversion is + totally outsourced to strtol(), see the associated man page for overflow + handling. + */ +/*--------------------------------------------------------------------------*/ +static inline int IniParserGetInt(const char * key, int notfound) { + return notfound; +} + +/*-------------------------------------------------------------------------*/ +/** + @brief Get the string associated to a key, convert to a double + @param key Key string to look for + @param notfound Value to return in case of error + @return double + + This function queries a dictionary for a key. A key as read from an + ini file is given as "section:key". If the key cannot be found, + the notfound value is returned. + */ +/*--------------------------------------------------------------------------*/ +static inline double IniparserGetDouble(const char * key, double notfound) { + return notfound; +} + +/*-------------------------------------------------------------------------*/ +/** + @brief Get the string associated to a key, convert to a boolean + @param key Key string to look for + @param notfound Value to return in case of error + @return integer + + This function queries for a key. A key as read from an + ini file is given as "section:key". If the key cannot be found, + the notfound value is returned. + + A true boolean is found if one of the following is matched: + + - A string starting with 'y' + - A string starting with 'Y' + - A string starting with 't' + - A string starting with 'T' + - A string starting with '1' + + A false boolean is found if one of the following is matched: + + - A string starting with 'n' + - A string starting with 'N' + - A string starting with 'f' + - A string starting with 'F' + - A string starting with '0' + + The notfound value returned if no boolean is identified, does not + necessarily have to be 0 or 1. + */ +/*--------------------------------------------------------------------------*/ +static inline int IniParserGetBoolean(const char * key, int notfound) { + return notfound; +} + +/*-------------------------------------------------------------------------*/ +/** + @brief Finds out if a given entry exists + @param entry Name of the entry to look for + @return integer 1 if entry exists, 0 otherwise + + Finds out if a given entry exists in the dictionary. Since sections + are stored as keys with NULL associated values, this is the only way + of querying for the presence of sections in a dictionary. + */ +/*--------------------------------------------------------------------------*/ +//int IniParserFindEntry(const char * entry); + +static inline int GetConfigParamterInt(const char * key, int notfound) { + return notfound; +} + +//const char * GetConfigParamterString(const char * key, const char * def); + +static inline double GetConfigParamterDouble(const char * key, double notfound) { + return notfound; +} +//int GetConfigParamterBoolean(const char * key, int notfound); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/platform/mcu/xr871/src/cedarx/external/include/adecoder/adecoder.h b/platform/mcu/xr871/src/cedarx/external/include/adecoder/adecoder.h new file mode 100644 index 0000000000..a26c8667c0 --- /dev/null +++ b/platform/mcu/xr871/src/cedarx/external/include/adecoder/adecoder.h @@ -0,0 +1,434 @@ +/* +* Copyright (c) 2008-2016 Allwinner Technology Co. Ltd. +* All rights reserved. +* +* File : adecoder.h +* Description : +* History : +* Author : gvc-al2 +* Date : long long ago +*/ + +#ifndef ADECODER_H +#define ADECODER_H + +#include "unistd.h" +//#include +//typedef signed long long int64_t; +//typedef int int32_t; + +#ifdef __cplusplus +extern "C" { +#endif + +#define AUDIO_BITSTREAM_BUFFER_SIZE (1024 * 6)//(1024*4096) +#define AUDIO_BITSTREAM_BUFFER_MAX_FRAME_NUM (6)//(4096) +#define AUDIO_PCM_BUFFER_SIZE (16 * 1024)//(512*1024) + +typedef enum __AUDIO_DEC_RESULT +{ + ERR_AUDIO_DEC_EXIT = -4, // exit + ERR_AUDIO_DEC_ENDINGCHKFAIL = -3, //big ending or little ending check failed + ERR_AUDIO_DEC_ABSEND = -2, //audio bitstream decode end + ERR_AUDIO_DEC_ONEFRMFAIL = -1, //decode one frame failed, can try again + + ERR_AUDIO_DEC_NONE = 0, //decode successed, no error + ERR_AUDIO_DEC_FFREVRETURN = 1, //return from fast-forward or fast-reverse + ERR_AUDIO_DEC_RETAPOINT = 2, //return from A point under A/B play mode + ERR_AUDIO_DEC_RETTAG = 3, //return from the first frame of tag play + ERR_AUDIO_DEC_VIDEOJUMP = 4, //0X88 maybe return 4 or 0 + ERR_AUDIO_DEC_NO_BITSTREAM = 5, //No enough bitstream ,try again + ERR_AUDIO_DEC_ + +} __audio_dec_result_t; + +enum EAUDIOCODECFORMAT +{ + AUDIO_CODEC_FORMAT_UNKNOWN = 0, + AUDIO_CODEC_FORMAT_MP1, + AUDIO_CODEC_FORMAT_MP2, + AUDIO_CODEC_FORMAT_MP3, + AUDIO_CODEC_FORMAT_MPEG_AAC_LC, + AUDIO_CODEC_FORMAT_AC3, //* not supported. + AUDIO_CODEC_FORMAT_DTS, //* not supported. + AUDIO_CODEC_FORMAT_LPCM_V, + AUDIO_CODEC_FORMAT_LPCM_A, + AUDIO_CODEC_FORMAT_ADPCM, + AUDIO_CODEC_FORMAT_PCM, + AUDIO_CODEC_FORMAT_WMA_STANDARD, + AUDIO_CODEC_FORMAT_FLAC, + AUDIO_CODEC_FORMAT_APE, + AUDIO_CODEC_FORMAT_OGG, + AUDIO_CODEC_FORMAT_RAAC, + AUDIO_CODEC_FORMAT_COOK, + AUDIO_CODEC_FORMAT_SIPR, + AUDIO_CODEC_FORMAT_ATRC, + AUDIO_CODEC_FORMAT_AMR, + AUDIO_CODEC_FORMAT_RA, + AUDIO_CODEC_FORMAT_PPCM, //* not supported. + AUDIO_CODEC_FORMAT_WMA_LOSS, //* not supported. + AUDIO_CODEC_FORMAT_WMA_PRO, //* not supported. + AUDIO_CODEC_FORMAT_MP3_PRO, //* not supported. + AUDIO_CODEC_FORMAT_ALAC, + AUDIO_CODEC_FORMAT_G729, + AUDIO_CODEC_FORMAT_DSD, + AUDIO_CODEC_FORMAT_OPUS, + AUDIO_CODEC_FORMAT_MLP = AUDIO_CODEC_FORMAT_AC3, + AUDIO_CODEC_FORMAT_ = -1, +}; + +//**************************************************************************// +//* Below are definition for sub codec types. +//**************************************************************************// +#define ABS_EDIAN_FLAG_MASK ((unsigned int)(1<<16)) +#define ABS_EDIAN_FLAG_BIG ((unsigned int)(1<<16)) + +/* Windows WAVE File Encoding Tags */ +#define WAVE_FORMAT_UNKNOWN 0x0000 /* Unknown Format */ +#define WAVE_FORMAT_PCM 0x0001 /* PCM */ +#define WAVE_FORMAT_ADPCM 0x0002 /* Microsoft ADPCM Format */ +#define WAVE_FORMAT_IEEE_FLOAT 0x0003 /* IEEE Float */ +#define WAVE_FORMAT_VSELP 0x0004 /* Compaq Computer's VSELP */ +#define WAVE_FORMAT_IBM_CVSD 0x0005 /* IBM CVSD */ +#define WAVE_FORMAT_ALAW 0x0006 /* ALAW */ +#define WAVE_FORMAT_MULAW 0x0007 /* MULAW */ +#define WAVE_FORMAT_DTS 0x0008 /* Microsoft Corporation */ +#define WAVE_FORMAT_DRM 0x0009 /* Microsoft Corporation */ +#define WAVE_FORMAT_WMAVOICE9 0x000A /* Microsoft Corporation */ +#define WAVE_FORMAT_WMAVOICE10 0x000B /* Microsoft Corporation */ +#define WAVE_FORMAT_OKI_ADPCM 0x0010 /* OKI ADPCM */ +#define WAVE_FORMAT_DVI_ADPCM 0x0011 /* Intel's DVI ADPCM */ +#define WAVE_FORMAT_IMA_ADPCM (WAVE_FORMAT_DVI_ADPCM) /* Intel Corporation */ +#define WAVE_FORMAT_MEDIASPACE_ADPCM 0x0012 /* Videologic's MediaSpace ADPCM*/ +#define WAVE_FORMAT_SIERRA_ADPCM 0x0013 /* Sierra ADPCM */ +#define WAVE_FORMAT_G723_ADPCM 0x0014 /* G.723 ADPCM */ +#define WAVE_FORMAT_DIGISTD 0x0015 /* DSP Solution's DIGISTD */ +#define WAVE_FORMAT_DIGIFIX 0x0016 /* DSP Solution's DIGIFIX */ +#define WAVE_FORMAT_DIALOGIC_OKI_ADPCM 0x0017 /* Dialogic OKI ADPCM */ +#define WAVE_FORMAT_MEDIAVISION_ADPCM 0x0018 /* MediaVision ADPCM */ +#define WAVE_FORMAT_CU_CODEC 0x0019 /* HP CU */ +#define WAVE_FORMAT_YAMAHA_ADPCM 0x0020 /* Yamaha ADPCM */ +#define WAVE_FORMAT_SONARC 0x0021 /* Speech Compression's Sonarc */ +#define WAVE_FORMAT_TRUESPEECH 0x0022 /* DSP Group's True Speech */ +#define WAVE_FORMAT_ECHOSC1 0x0023 /* Echo Speech's EchoSC1 */ +#define WAVE_FORMAT_AUDIOFILE_AF36 0x0024 /* Audiofile AF36 */ +#define WAVE_FORMAT_APTX 0x0025 /* APTX */ +#define WAVE_FORMAT_AUDIOFILE_AF10 0x0026 /* AudioFile AF10 */ +#define WAVE_FORMAT_PROSODY_1612 0x0027 /* Prosody 1612 */ +#define WAVE_FORMAT_LRC 0x0028 /* LRC */ +#define WAVE_FORMAT_AC2 0x0030 /* Dolby AC2 */ +#define WAVE_FORMAT_GSM610 0x0031 /* GSM610 */ +#define WAVE_FORMAT_MSNAUDIO 0x0032 /* MSNAudio */ +#define WAVE_FORMAT_ANTEX_ADPCME 0x0033 /* Antex ADPCME */ +#define WAVE_FORMAT_CONTROL_RES_VQLPC 0x0034 /* Control Res VQLPC */ +#define WAVE_FORMAT_DIGIREAL 0x0035 /* Digireal */ +#define WAVE_FORMAT_DIGIADPCM 0x0036 /* DigiADPCM */ +#define WAVE_FORMAT_CONTROL_RES_CR10 0x0037 /* Control Res CR10 */ +#define WAVE_FORMAT_VBXADPCM 0x0038 /* NMS VBXADPCM */ +#define WAVE_FORMAT_ROLAND_RDAC 0x0039 /* Roland RDAC */ +#define WAVE_FORMAT_ECHOSC3 0x003A /* EchoSC3 */ +#define WAVE_FORMAT_ROCKWELL_ADPCM 0x003B /* Rockwell ADPCM */ +#define WAVE_FORMAT_ROCKWELL_DIGITALK 0x003C /* Rockwell Digit LK */ +#define WAVE_FORMAT_XEBEC 0x003D /* Xebec */ +#define WAVE_FORMAT_G721_ADPCM 0x0040 /* Antex Electronics G.721 */ +#define WAVE_FORMAT_G728_CELP 0x0041 /* G.728 CELP */ +#define WAVE_FORMAT_MSG723 0x0042 /* MSG723 */ +#define WAVE_FORMAT_MPEG 0x0050 /* MPEG Layer 1,2 */ +#define WAVE_FORMAT_RT24 0x0051 /* RT24 */ +#define WAVE_FORMAT_PAC 0x0051 /* PAC */ +#define WAVE_FORMAT_MPEGLAYER3 0x0055 /* MPEG Layer 3 */ +//#define WAVE_FORMAT_CIRRUS 0x0059 /* Cirrus */ +#define WAVE_FORMAT_LUCENT_G723 0x0059 /* Lucent Technologies */ +#define WAVE_FORMAT_CIRRUS 0x0060 /* Cirrus Logic */ +#define WAVE_FORMAT_ESPCM 0x0061 /* ESPCM */ +#define WAVE_FORMAT_VOXWARE 0x0062 /* Voxware (obsolete) */ +#define WAVE_FORMAT_CANOPUS_ATRAC 0x0063 /* Canopus Atrac */ +#define WAVE_FORMAT_G726_ADPCM 0x0064 /* G.726 ADPCM */ +#define WAVE_FORMAT_G722_ADPCM 0x0065 /* G.722 ADPCM */ +#define WAVE_FORMAT_DSAT 0x0066 /* DSAT */ +#define WAVE_FORMAT_DSAT_DISPLAY 0x0067 /* DSAT Display */ +#define WAVE_FORMAT_VOXWARE_BYTE_ALIGNED 0x0069 /* Voxware Byte Aligned (obsolete) */ +#define WAVE_FORMAT_VOXWARE_AC8 0x0070 /* Voxware AC8 (obsolete) */ +#define WAVE_FORMAT_VOXWARE_AC10 0x0071 /* Voxware AC10 (obsolete) */ +#define WAVE_FORMAT_VOXWARE_AC16 0x0072 /* Voxware AC16 (obsolete) */ +#define WAVE_FORMAT_VOXWARE_AC20 0x0073 /* Voxware AC20 (obsolete) */ +#define WAVE_FORMAT_VOXWARE_RT24 0x0074 /* Voxware MetaVoice (obsolete) */ +#define WAVE_FORMAT_VOXWARE_RT29 0x0075 /* Voxware MetaSound (obsolete) */ +#define WAVE_FORMAT_VOXWARE_RT29HW 0x0076 /* Voxware RT29HW (obsolete) */ +#define WAVE_FORMAT_VOXWARE_VR12 0x0077 /* Voxware VR12 (obsolete) */ +#define WAVE_FORMAT_VOXWARE_VR18 0x0078 /* Voxware VR18 (obsolete) */ +#define WAVE_FORMAT_VOXWARE_TQ40 0x0079 /* Voxware TQ40 (obsolete) */ +#define WAVE_FORMAT_SOFTSOUND 0x0080 /* Softsound */ +#define WAVE_FORMAT_VOXWARE_TQ60 0x0081 /* Voxware TQ60 (obsolete) */ +#define WAVE_FORMAT_MSRT24 0x0082 /* MSRT24 */ +#define WAVE_FORMAT_G729A 0x0083 /* G.729A */ +#define WAVE_FORMAT_MVI_MV12 0x0084 /* MVI MV12 */ +#define WAVE_FORMAT_DF_G726 0x0085 /* DF G.726 */ +#define WAVE_FORMAT_DF_GSM610 0x0086 /* DF GSM610 */ +#define WAVE_FORMAT_ISIAUDIO 0x0088 /* ISIAudio */ +#define WAVE_FORMAT_ONLIVE 0x0089 /* Onlive */ +#define WAVE_FORMAT_SBC24 0x0091 /* SBC24 */ +#define WAVE_FORMAT_DOLBY_AC3_SPDIF 0x0092 /* Dolby AC3 SPDIF */ +#define WAVE_FORMAT_MEDIASONIC_G723 0x0093 /* MediaSonic */ +#define WAVE_FORMAT_PROSODY_8KBPS 0x0094 /* Aculab plc */ +#define WAVE_FORMAT_ZYXEL_ADPCM 0x0097 /* ZyXEL ADPCM */ +#define WAVE_FORMAT_PHILIPS_LPCBB 0x0098 /* Philips LPCBB */ +#define WAVE_FORMAT_PACKED 0x0099 /* Packed */ +#define WAVE_FORMAT_MALDEN_PHONYTALK 0x00A0 /* Malden Electronics Ltd. */ +#define WAVE_FORMAT_RHETOREX_ADPCM 0x0100 /* Rhetorex ADPCM */ +#define WAVE_FORMAT_IRAT 0x0101 /* BeCubed Software's IRAT */ +#define WAVE_FORMAT_VIVO_G723 0x0111 /* Vivo G.723 */ +#define WAVE_FORMAT_VIVO_SIREN 0x0112 /* Vivo Siren */ +#define WAVE_FORMAT_DIGITAL_G723 0x0123 /* Digital G.723 */ +#define WAVE_FORMAT_SANYO_LD_ADPCM 0x0125 /* Sanyo Electric Co., Ltd. */ +#define WAVE_FORMAT_SIPROLAB_ACEPLNET 0x0130 /* Sipro Lab Telecom Inc. */ +#define WAVE_FORMAT_SIPROLAB_ACELP4800 0x0131 /* Sipro Lab Telecom Inc. */ +#define WAVE_FORMAT_SIPROLAB_ACELP8V3 0x0132 /* Sipro Lab Telecom Inc. */ +#define WAVE_FORMAT_SIPROLAB_G729 0x0133 /* Sipro Lab Telecom Inc. */ +#define WAVE_FORMAT_SIPROLAB_G729A 0x0134 /* Sipro Lab Telecom Inc. */ +#define WAVE_FORMAT_SIPROLAB_KELVIN 0x0135 /* Sipro Lab Telecom Inc. */ +#define WAVE_FORMAT_G726ADPCM 0x0140 /* Dictaphone Corporation */ +#define WAVE_FORMAT_QUALCOMM_PUREVOICE 0x0150 /* Qualcomm, Inc. */ +#define WAVE_FORMAT_QUALCOMM_HALFRATE 0x0151 /* Qualcomm, Inc. */ +#define WAVE_FORMAT_TUBGSM 0x0155 /* Ring Zero Systems, Inc. */ +#define WAVE_FORMAT_MSAUDIO1 0x0160 /* Microsoft Corporation */ +#define WAVE_FORMAT_WMAUDIO2 0x0161 /* Microsoft Corporation */ +#define WAVE_FORMAT_WMAUDIO3 0x0162 /* Microsoft Corporation */ +#define WAVE_FORMAT_WMAUDIO_LOSSLESS 0x0163 /* Microsoft Corporation */ +#define WAVE_FORMAT_WMASPDIF 0x0164 /* Microsoft Corporation */ +#define WAVE_FORMAT_UNISYS_NAP_ADPCM 0x0170 /* Unisys Corp. */ +#define WAVE_FORMAT_UNISYS_NAP_ULAW 0x0171 /* Unisys Corp. */ +#define WAVE_FORMAT_UNISYS_NAP_ALAW 0x0172 /* Unisys Corp. */ +#define WAVE_FORMAT_UNISYS_NAP_16K 0x0173 /* Unisys Corp. */ +#define WAVE_FORMAT_CREATIVE_ADPCM 0x0200 /* Creative ADPCM */ +#define WAVE_FORMAT_CREATIVE_FASTSPEECH8 0x0202 /* Creative FastSpeech8 */ +#define WAVE_FORMAT_CREATIVE_FASTSPEECH10 0x0203 /* Creative FastSpeech10 */ +#define WAVE_FORMAT_UHER_ADPCM 0x0210 /* UHER informatic GmbH */ +#define WAVE_FORMAT_QUARTERDECK 0x0220 /* Quarterdeck */ +#define WAVE_FORMAT_ILINK_VC 0x0230 /* I-link Worldwide */ +#define WAVE_FORMAT_RAW_SPORT 0x0240 /* Aureal Semiconductor */ +#define WAVE_FORMAT_ESST_AC3 0x0241 /* ESS Technology, Inc. */ +#define WAVE_FORMAT_GENERIC_PASSTHRU 0x0249 +#define WAVE_FORMAT_IPI_HSX 0x0250 /* Interactive Products, Inc. */ +#define WAVE_FORMAT_IPI_RPELP 0x0251 /* Interactive Products, Inc. */ +#define WAVE_FORMAT_CS2 0x0260 /* Consistent Software */ +#define WAVE_FORMAT_SONY_SCX 0x0270 /* Sony Corp. */ +#define WAVE_FORMAT_FM_TOWNS_SND 0x0300 /* FM Towns Snd */ +#define WAVE_FORMAT_BTV_DIGITAL 0x0400 /* BTV Digital */ +#define WAVE_FORMAT_QDESIGN_MUSIC 0x0450 /* QDesign Corporation */ +#define WAVE_FORMAT_VME_VMPCM 0x0680 /* VME VMPCM */ +#define WAVE_FORMAT_TPC 0x0681 /* AT&T Labs, Inc. */ +#define WAVE_FORMAT_OLIGSM 0x1000 /* OLIGSM */ +#define WAVE_FORMAT_OLIADPCM 0x1001 /* OLIADPCM */ +#define WAVE_FORMAT_OLICELP 0x1002 /* OLICELP */ +#define WAVE_FORMAT_OLISBC 0x1003 /* OLISBC */ +#define WAVE_FORMAT_OLIOPR 0x1004 /* OLIOPR */ +#define WAVE_FORMAT_LH_CODEC 0x1100 /* LH Codec */ +#define WAVE_FORMAT_NORRIS 0x1400 /* Norris */ +//#define WAVE_FORMAT_ISIAUDIO 0x1401 /* ISIAudio */ +#define WAVE_FORMAT_SOUNDSPACE_MUSICOMPRESS 0x1500 /* Soundspace Music Compression */ +#define WAVE_FORMAT_MPEG_ADTS_AAC 0x1600 /* Microsoft Corporation */ +#define WAVE_FORMAT_MPEG_RAW_AAC 0x1601 /* Microsoft Corporation */ +#define WAVE_FORMAT_NOKIA_MPEG_ADTS_AAC 0x1608 /* Microsoft Corporation */ +#define WAVE_FORMAT_NOKIA_MPEG_RAW_AAC 0x1609 /* Microsoft Corporation */ +#define WAVE_FORMAT_VODAFONE_MPEG_ADTS_AAC 0x160A /* Microsoft Corporation */ +#define WAVE_FORMAT_VODAFONE_MPEG_RAW_AAC 0x160B /* Microsoft Corporation */ +#define WAVE_FORMAT_DVM 0x2000 /* DVM */ +#define WAVE_FORMAT_EXTENSIBTSMIRACAST 0xFFFC /* LSZHANG TS Miracast WIFI*/ +#define WAVE_FORMAT_EXTENSIBTS 0xFFFD /* LSZHANG TS */ +#define WAVE_FORMAT_EXTENSIBLE 0xFFFE /* SubFormat */ +#define WAVE_FORMAT_DEVELOPMENT 0xFFFF /* Development */ +#define WAVE_FORMAT_DVD_LPCM 0xFFA0 /* Added by Khan for DVD-LPCM*/ + +//user define adpcm codec type from video file +#define ADPCM_CODEC_ID_IMA_QT 0xE000 +#define ADPCM_CODEC_ID_IMA_WAV 0xE001 /* from avi file format */ +#define ADPCM_CODEC_ID_IMA_DK3 0xE002 +#define ADPCM_CODEC_ID_IMA_DK4 0xE003 +#define ADPCM_CODEC_ID_IMA_WS 0xE004 +#define ADPCM_CODEC_ID_IMA_SMJPEG 0xE005 +#define ADPCM_CODEC_ID_MS 0xE006 +#define ADPCM_CODEC_ID_4XM 0xE007 +#define ADPCM_CODEC_ID_XA 0xE008 +#define ADPCM_CODEC_ID_ADX 0xE009 +#define ADPCM_CODEC_ID_EA 0xE00A +#define ADPCM_CODEC_ID_G726 0xE00B +#define ADPCM_CODEC_ID_CT 0xE00C +#define ADPCM_CODEC_ID_SWF 0xE00D /* from flv/swf file format */ +#define ADPCM_CODEC_ID_YAMAHA 0xE00E +#define ADPCM_CODEC_ID_SBPRO_4 0xE00F +#define ADPCM_CODEC_ID_SBPRO_3 0xE010 +#define ADPCM_CODEC_ID_SBPRO_2 0xE011 + +#define AMR_FORMAT_NONE 0 // undefine amr format +#define AMR_FORMAT_NARROWBAND 1 // narrow band amr format +#define AMR_FORMAT_WIDEBAND 2 // wide band amr format + +#define ADECODER_MAX_LANG_CHAR_SIZE (64) + +typedef struct Cedar_raw_data CdxPlaybkCfg; + +enum AUIDO_RAW_DATA_TYPE +{ + AUDIO_RAW_DATA_UNKOWN = 0, + AUDIO_RAW_DATA_PCM = 1, + AUDIO_RAW_DATA_AC3 = 2, + AUDIO_RAW_DATA_MPEG1 = 3, + AUDIO_RAW_DATA_MP3 = 4, + AUDIO_RAW_DATA_MPEG2 = 5, + AUDIO_RAW_DATA_AAC = 6, + AUDIO_RAW_DATA_DTS = 7, + AUDIO_RAW_DATA_ATRAC = 8, + AUDIO_RAW_DATA_ONE_BIT_AUDIO = 9, + AUDIO_RAW_DATA_DOLBY_DIGITAL_PLUS = 10, + AUDIO_RAW_DATA_DTS_HD = 11, + AUDIO_RAW_DATA_MAT = 12, + AUDIO_RAW_DATA_DST = 13, + AUDIO_RAW_DATA_WMAPRO = 14 +}; + +struct Cedar_raw_data +{ + int nRoutine;//UI set 0:pcm;1:hdmi raw data;2:spdif raw data; + int nNeedDirect;//if init flag 0:no init raw;1:init raw + int nChannels; + int nSamplerate; + int nBitpersample; + enum AUIDO_RAW_DATA_TYPE nDataType; +}; + +typedef struct AUDIOSTREAMINFO +{ + enum EAUDIOCODECFORMAT eCodecFormat; + int eSubCodecFormat; // + int nChannelNum; + int nBitsPerSample; + int nSampleRate; + int nAvgBitrate; + int nMaxBitRate; + unsigned int nFileSize; + int eAudioBitstreamSource; + int eDataEncodeType; + unsigned char strLang[ADECODER_MAX_LANG_CHAR_SIZE]; + int nCodecSpecificDataLen; + char* pCodecSpecificData; + int nFlags; + int nBlockAlign; + CdxPlaybkCfg raw_data; +}AudioStreamInfo; + +typedef struct __BsInFor +{ + int TotalplayTime; + int NowPlayTime; + int Samplerate; + int bitrate; + int chan; + int CBRflag;//1 cbr 0 vbr + int framesize; + int framecount; + int oldfs; + int oldbs; + int firstflag; + int framepcms; + int modeflag;//flag 0bits: 1:raw data 0:pcm + int ulMode;//decode flag 0:pcm ,>raw data out ,1:hdmi,2:spdif + int bitpersample;//bits per sample + //add for control from ad_cedar_ctx + int nBitStreamUnderFlow; + int nShowBitsReturn; + int out_channels; + int out_samplerate; + //global + int nDecodeMode; + int nDemuxType; + int nIsHwCodec; + int64_t NowPTSTime; + +}BsInFor; + +typedef struct AudioDecoder AudioDecoder; +struct AudioDecoder +{ + int nPrivFlag;//0:play,1:reset,2:file end + int nGetAudioInfoFlag; + int nAudioChannel; + int volumegain; + int mute; + int nEnableResample; + int RawPlayFlag; +}; + +int ParserRequestBsBuffer(AudioDecoder* pDecoder, + int nRequireSize, + unsigned char** ppBuf, + int* pBufSize, + unsigned char** ppRingBuf, + int* pRingBufSize, + int* nOffset); + +int ParserUpdateBsBuffer(AudioDecoder* pDecoder, + int nFilledLen, + int64_t nTimeStamp, + int nOffset); + +void BsQueryQuality(AudioDecoder* pDecoder, + int* pValidPercent, + int* vbv); + +int AudioStreamDataSize(AudioDecoder* pDecoder); + +int AudioStreamBufferSize(void); + +int AudioStreamBufferMaxFrameNum(void); + +int AudioPCMDataSize(AudioDecoder* pDecoder); + +int DecRequestPcmBuffer(AudioDecoder* pDecoder, char **pOutWritePtr); + +int DecUpdatePcmBuffer(AudioDecoder* pDecoder, int nPcmOutSize); + +int PlybkRequestPcmBuffer(AudioDecoder* pDecoder, unsigned char **pOutReadPtr, int *psize); + +int PlybkUpdatePcmBuffer(AudioDecoder* pDecoder, int nPcmOutSize); + +int64_t PlybkRequestPcmPts(AudioDecoder* pDecoder); + +void PcmQueryQuality(AudioDecoder* pDecoder, int* pValidPercent, int* vbv); + +void AudioDecoderSeek(AudioDecoder* pDecoder, int64_t nSeekTime); + +int InitializeAudioDecoder(AudioDecoder* pDecoder, + AudioStreamInfo* pAudioStreamInfo, + BsInFor* pBsInFor); + +int ResetAudioDecoder(AudioDecoder* pDecoder, int64_t nSeekTime); + +int DecodeAudioStream(AudioDecoder* pDecoder, + AudioStreamInfo* pAudioStreamInfo, + char* ppBuf, + int* pBufSize); + +int DestroyAudioDecoder(AudioDecoder* pDecoder); + +AudioDecoder* CreateAudioDecoder(void); + +#if defined(__ANDROID__) +void SetRawPlayParam(AudioDecoder* pDecoder,void *self); +#else +void SetRawPlayParam(AudioDecoder* pDecoder,void *self,int flag); +#endif + +#if defined(__ANDROID__) +void AudioSoundTouchSetPitch(AudioDecoder* pDecoder, float pitchDelta); +#endif + +#if !defined(__ANDROID__) +int SetAudioVolume(AudioDecoder* pDecoder, int32_t nVolume); +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/platform/mcu/xr871/src/cedarx/external/include/aencoder/aencoder.h b/platform/mcu/xr871/src/cedarx/external/include/aencoder/aencoder.h new file mode 100644 index 0000000000..297dea61db --- /dev/null +++ b/platform/mcu/xr871/src/cedarx/external/include/aencoder/aencoder.h @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2008-2016 Allwinner Technology Co. Ltd. + * All rights reserved. + * + * File : aencoder.h + * Description : audio encoder api + * History : + * + */ + +#ifndef AUDIO_ENCODER_H +#define AUDIO_ENCODER_H + +#ifdef __cplusplus +extern "C" { +#endif + +#define AMR_ENCODER (1) +#define PCM_ENCODER (1) + +typedef enum __AUDIO_ENC_RESULT +{ + ERR_AUDIO_ENC_ABSEND = -2, + ERR_AUDIO_ENC_UNKNOWN = -1, + ERR_AUDIO_ENC_NONE = 0, //decode successed, no error + ERR_AUDIO_ENC_PCMUNDERFLOW = 1, //pcm data is underflow + ERR_AUDIO_ENC_OUTFRAME_UNDERFLOW = 2, //out frame buffer is underflow. + ERR_AUDIO_ENC_ +} __audio_enc_result_t; + + +enum AUDIO_ENCODER_TYPE +{ +#if OTHER_ENCODER + AUDIO_ENCODER_AAC_TYPE, + AUDIO_ENCODER_LPCM_TYPE, //only used by mpeg2ts + AUDIO_ENCODER_MP3_TYPE, +#endif +#if PCM_ENCODER + AUDIO_ENCODER_PCM_TYPE, +#endif +#if AMR_ENCODER + AUDIO_ENCODER_AMR_TYPE, +#endif +}; + +typedef enum AUDIO_ENCODER_TYPE AUDIO_ENCODER_TYPE; + +typedef struct AudioEncConfig +{ + AUDIO_ENCODER_TYPE nType; + int nInSamplerate; //fs + int nInChan; //pcm chan 1:mon 2:stereo + int nBitrate; //bs + int nSamplerBits; //only for 16bits + int nOutSamplerate; //fs,now OutSamplerate must equal InSamplerate + int nOutChan; // chan + + //for aac: 0:add head,1:raw data; + //for pcm: 2:mpegTs pcm(big endian), + //other: common pcm(little endian) + int nFrameStyle; +}AudioEncConfig; + +typedef void* AudioEncoder; + +AudioEncoder* CreateAudioEncoder(); +void DestroyAudioEncoder(AudioEncoder* pEncoder); +int InitializeAudioEncoder(AudioEncoder *pEncoder, AudioEncConfig *pConfig); +int ResetAudioEncoder(AudioEncoder* pEncoder); +int EncodeAudioStream(AudioEncoder *pEncoder); +int WriteAudioStreamBuffer(AudioEncoder *pEncoder, char* pBuf, int len); +int RequestAudioFrameBuffer(AudioEncoder *pEncoder, char **pOutBuf, + unsigned int *size, long long *pts, int *bufId); +int ReturnAudioFrameBuffer(AudioEncoder *pEncoder, char *pOutBuf, + unsigned int size, long long pts, int bufId); + + +#ifdef __cplusplus +} +#endif + +#endif // AUDIO_ENC_API_H + diff --git a/platform/mcu/xr871/src/cedarx/external/include/zlib/zconf.h b/platform/mcu/xr871/src/cedarx/external/include/zlib/zconf.h new file mode 100644 index 0000000000..996fff2921 --- /dev/null +++ b/platform/mcu/xr871/src/cedarx/external/include/zlib/zconf.h @@ -0,0 +1,511 @@ +/* zconf.h -- configuration of the zlib compression library + * Copyright (C) 1995-2013 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id$ */ + +#ifndef ZCONF_H +#define ZCONF_H + +/* + * If you *really* need a unique prefix for all types and library functions, + * compile with -DZ_PREFIX. The "standard" zlib should be compiled without it. + * Even better than compiling with -DZ_PREFIX would be to use configure to set + * this permanently in zconf.h using "./configure --zprefix". + */ +#ifdef Z_PREFIX /* may be set to #if 1 by ./configure */ +# define Z_PREFIX_SET + +/* all linked symbols */ +# define _dist_code z__dist_code +# define _length_code z__length_code +# define _tr_align z__tr_align +# define _tr_flush_bits z__tr_flush_bits +# define _tr_flush_block z__tr_flush_block +# define _tr_init z__tr_init +# define _tr_stored_block z__tr_stored_block +# define _tr_tally z__tr_tally +# define adler32 z_adler32 +# define adler32_combine z_adler32_combine +# define adler32_combine64 z_adler32_combine64 +# ifndef Z_SOLO +# define compress z_compress +# define compress2 z_compress2 +# define compressBound z_compressBound +# endif +# define crc32 z_crc32 +# define crc32_combine z_crc32_combine +# define crc32_combine64 z_crc32_combine64 +# define deflate z_deflate +# define deflateBound z_deflateBound +# define deflateCopy z_deflateCopy +# define deflateEnd z_deflateEnd +# define deflateInit2_ z_deflateInit2_ +# define deflateInit_ z_deflateInit_ +# define deflateParams z_deflateParams +# define deflatePending z_deflatePending +# define deflatePrime z_deflatePrime +# define deflateReset z_deflateReset +# define deflateResetKeep z_deflateResetKeep +# define deflateSetDictionary z_deflateSetDictionary +# define deflateSetHeader z_deflateSetHeader +# define deflateTune z_deflateTune +# define deflate_copyright z_deflate_copyright +# define get_crc_table z_get_crc_table +# ifndef Z_SOLO +# define gz_error z_gz_error +# define gz_intmax z_gz_intmax +# define gz_strwinerror z_gz_strwinerror +# define gzbuffer z_gzbuffer +# define gzclearerr z_gzclearerr +# define gzclose z_gzclose +# define gzclose_r z_gzclose_r +# define gzclose_w z_gzclose_w +# define gzdirect z_gzdirect +# define gzdopen z_gzdopen +# define gzeof z_gzeof +# define gzerror z_gzerror +# define gzflush z_gzflush +# define gzgetc z_gzgetc +# define gzgetc_ z_gzgetc_ +# define gzgets z_gzgets +# define gzoffset z_gzoffset +# define gzoffset64 z_gzoffset64 +# define gzopen z_gzopen +# define gzopen64 z_gzopen64 +# ifdef _WIN32 +# define gzopen_w z_gzopen_w +# endif +# define gzprintf z_gzprintf +# define gzvprintf z_gzvprintf +# define gzputc z_gzputc +# define gzputs z_gzputs +# define gzread z_gzread +# define gzrewind z_gzrewind +# define gzseek z_gzseek +# define gzseek64 z_gzseek64 +# define gzsetparams z_gzsetparams +# define gztell z_gztell +# define gztell64 z_gztell64 +# define gzungetc z_gzungetc +# define gzwrite z_gzwrite +# endif +# define inflate z_inflate +# define inflateBack z_inflateBack +# define inflateBackEnd z_inflateBackEnd +# define inflateBackInit_ z_inflateBackInit_ +# define inflateCopy z_inflateCopy +# define inflateEnd z_inflateEnd +# define inflateGetHeader z_inflateGetHeader +# define inflateInit2_ z_inflateInit2_ +# define inflateInit_ z_inflateInit_ +# define inflateMark z_inflateMark +# define inflatePrime z_inflatePrime +# define inflateReset z_inflateReset +# define inflateReset2 z_inflateReset2 +# define inflateSetDictionary z_inflateSetDictionary +# define inflateGetDictionary z_inflateGetDictionary +# define inflateSync z_inflateSync +# define inflateSyncPoint z_inflateSyncPoint +# define inflateUndermine z_inflateUndermine +# define inflateResetKeep z_inflateResetKeep +# define inflate_copyright z_inflate_copyright +# define inflate_fast z_inflate_fast +# define inflate_table z_inflate_table +# ifndef Z_SOLO +# define uncompress z_uncompress +# endif +# define zError z_zError +# ifndef Z_SOLO +# define zcalloc z_zcalloc +# define zcfree z_zcfree +# endif +# define zlibCompileFlags z_zlibCompileFlags +# define zlibVersion z_zlibVersion + +/* all zlib typedefs in zlib.h and zconf.h */ +# define Byte z_Byte +# define Bytef z_Bytef +# define alloc_func z_alloc_func +# define charf z_charf +# define free_func z_free_func +# ifndef Z_SOLO +# define gzFile z_gzFile +# endif +# define gz_header z_gz_header +# define gz_headerp z_gz_headerp +# define in_func z_in_func +# define intf z_intf +# define out_func z_out_func +# define uInt z_uInt +# define uIntf z_uIntf +# define uLong z_uLong +# define uLongf z_uLongf +# define voidp z_voidp +# define voidpc z_voidpc +# define voidpf z_voidpf + +/* all zlib structs in zlib.h and zconf.h */ +# define gz_header_s z_gz_header_s +# define internal_state z_internal_state + +#endif + +#if defined(__MSDOS__) && !defined(MSDOS) +# define MSDOS +#endif +#if (defined(OS_2) || defined(__OS2__)) && !defined(OS2) +# define OS2 +#endif +#if defined(_WINDOWS) && !defined(WINDOWS) +# define WINDOWS +#endif +#if defined(_WIN32) || defined(_WIN32_WCE) || defined(__WIN32__) +# ifndef WIN32 +# define WIN32 +# endif +#endif +#if (defined(MSDOS) || defined(OS2) || defined(WINDOWS)) && !defined(WIN32) +# if !defined(__GNUC__) && !defined(__FLAT__) && !defined(__386__) +# ifndef SYS16BIT +# define SYS16BIT +# endif +# endif +#endif + +/* + * Compile with -DMAXSEG_64K if the alloc function cannot allocate more + * than 64k bytes at a time (needed on systems with 16-bit int). + */ +#ifdef SYS16BIT +# define MAXSEG_64K +#endif +#ifdef MSDOS +# define UNALIGNED_OK +#endif + +#ifdef __STDC_VERSION__ +# ifndef STDC +# define STDC +# endif +# if __STDC_VERSION__ >= 199901L +# ifndef STDC99 +# define STDC99 +# endif +# endif +#endif +#if !defined(STDC) && (defined(__STDC__) || defined(__cplusplus)) +# define STDC +#endif +#if !defined(STDC) && (defined(__GNUC__) || defined(__BORLANDC__)) +# define STDC +#endif +#if !defined(STDC) && (defined(MSDOS) || defined(WINDOWS) || defined(WIN32)) +# define STDC +#endif +#if !defined(STDC) && (defined(OS2) || defined(__HOS_AIX__)) +# define STDC +#endif + +#if defined(__OS400__) && !defined(STDC) /* iSeries (formerly AS/400). */ +# define STDC +#endif + +#ifndef STDC +# ifndef const /* cannot use !defined(STDC) && !defined(const) on Mac */ +# define const /* note: need a more gentle solution here */ +# endif +#endif + +#if defined(ZLIB_CONST) && !defined(z_const) +# define z_const const +#else +# define z_const +#endif + +/* Some Mac compilers merge all .h files incorrectly: */ +#if defined(__MWERKS__)||defined(applec)||defined(THINK_C)||defined(__SC__) +# define NO_DUMMY_DECL +#endif + +/* Maximum value for memLevel in deflateInit2 */ +#ifndef MAX_MEM_LEVEL +# ifdef MAXSEG_64K +# define MAX_MEM_LEVEL 8 +# else +# define MAX_MEM_LEVEL 9 +# endif +#endif + +/* Maximum value for windowBits in deflateInit2 and inflateInit2. + * WARNING: reducing MAX_WBITS makes minigzip unable to extract .gz files + * created by gzip. (Files created by minigzip can still be extracted by + * gzip.) + */ +#ifndef MAX_WBITS +# define MAX_WBITS 15 /* 32K LZ77 window */ +#endif + +/* The memory requirements for deflate are (in bytes): + (1 << (windowBits+2)) + (1 << (memLevel+9)) + that is: 128K for windowBits=15 + 128K for memLevel = 8 (default values) + plus a few kilobytes for small objects. For example, if you want to reduce + the default memory requirements from 256K to 128K, compile with + make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7" + Of course this will generally degrade compression (there's no free lunch). + + The memory requirements for inflate are (in bytes) 1 << windowBits + that is, 32K for windowBits=15 (default value) plus a few kilobytes + for small objects. +*/ + + /* Type declarations */ + +#ifndef OF /* function prototypes */ +# ifdef STDC +# define OF(args) args +# else +# define OF(args) () +# endif +#endif + +#ifndef Z_ARG /* function prototypes for stdarg */ +# if defined(STDC) || defined(Z_HAVE_STDARG_H) +# define Z_ARG(args) args +# else +# define Z_ARG(args) () +# endif +#endif + +/* The following definitions for FAR are needed only for MSDOS mixed + * model programming (small or medium model with some far allocations). + * This was tested only with MSC; for other MSDOS compilers you may have + * to define NO_MEMCPY in zutil.h. If you don't need the mixed model, + * just define FAR to be empty. + */ +#ifdef SYS16BIT +# if defined(M_I86SM) || defined(M_I86MM) + /* MSC small or medium model */ +# define SMALL_MEDIUM +# ifdef _MSC_VER +# define FAR _far +# else +# define FAR far +# endif +# endif +# if (defined(__SMALL__) || defined(__MEDIUM__)) + /* Turbo C small or medium model */ +# define SMALL_MEDIUM +# ifdef __BORLANDC__ +# define FAR _far +# else +# define FAR far +# endif +# endif +#endif + +#if defined(WINDOWS) || defined(WIN32) + /* If building or using zlib as a DLL, define ZLIB_DLL. + * This is not mandatory, but it offers a little performance increase. + */ +# ifdef ZLIB_DLL +# if defined(WIN32) && (!defined(__BORLANDC__) || (__BORLANDC__ >= 0x500)) +# ifdef ZLIB_INTERNAL +# define ZEXTERN extern __declspec(dllexport) +# else +# define ZEXTERN extern __declspec(dllimport) +# endif +# endif +# endif /* ZLIB_DLL */ + /* If building or using zlib with the WINAPI/WINAPIV calling convention, + * define ZLIB_WINAPI. + * Caution: the standard ZLIB1.DLL is NOT compiled using ZLIB_WINAPI. + */ +# ifdef ZLIB_WINAPI +# ifdef FAR +# undef FAR +# endif +# include + /* No need for _export, use ZLIB.DEF instead. */ + /* For complete Windows compatibility, use WINAPI, not __stdcall. */ +# define ZEXPORT WINAPI +# ifdef WIN32 +# define ZEXPORTVA WINAPIV +# else +# define ZEXPORTVA FAR CDECL +# endif +# endif +#endif + +#if defined (__BEOS__) +# ifdef ZLIB_DLL +# ifdef ZLIB_INTERNAL +# define ZEXPORT __declspec(dllexport) +# define ZEXPORTVA __declspec(dllexport) +# else +# define ZEXPORT __declspec(dllimport) +# define ZEXPORTVA __declspec(dllimport) +# endif +# endif +#endif + +#ifndef ZEXTERN +# define ZEXTERN extern +#endif +#ifndef ZEXPORT +# define ZEXPORT +#endif +#ifndef ZEXPORTVA +# define ZEXPORTVA +#endif + +#ifndef FAR +# define FAR +#endif + +#if !defined(__MACTYPES__) +typedef unsigned char Byte; /* 8 bits */ +#endif +typedef unsigned int uInt; /* 16 bits or more */ +typedef unsigned long uLong; /* 32 bits or more */ + +#ifdef SMALL_MEDIUM + /* Borland C/C++ and some old MSC versions ignore FAR inside typedef */ +# define Bytef Byte FAR +#else + typedef Byte FAR Bytef; +#endif +typedef char FAR charf; +typedef int FAR intf; +typedef uInt FAR uIntf; +typedef uLong FAR uLongf; + +#ifdef STDC + typedef void const *voidpc; + typedef void FAR *voidpf; + typedef void *voidp; +#else + typedef Byte const *voidpc; + typedef Byte FAR *voidpf; + typedef Byte *voidp; +#endif + +#if !defined(Z_U4) && !defined(Z_SOLO) && defined(STDC) +# include +# if (UINT_MAX == 0xffffffffUL) +# define Z_U4 unsigned +# elif (ULONG_MAX == 0xffffffffUL) +# define Z_U4 unsigned long +# elif (USHRT_MAX == 0xffffffffUL) +# define Z_U4 unsigned short +# endif +#endif + +#ifdef Z_U4 + typedef Z_U4 z_crc_t; +#else + typedef unsigned long z_crc_t; +#endif + +#if 1 /* was set to #if 1 by ./configure */ +# define Z_HAVE_UNISTD_H +#endif + +#if 1 /* was set to #if 1 by ./configure */ +# define Z_HAVE_STDARG_H +#endif + +#ifdef STDC +# ifndef Z_SOLO +# include /* for off_t */ +# endif +#endif + +#if defined(STDC) || defined(Z_HAVE_STDARG_H) +# ifndef Z_SOLO +# include /* for va_list */ +# endif +#endif + +#ifdef _WIN32 +# ifndef Z_SOLO +# include /* for wchar_t */ +# endif +#endif + +/* a little trick to accommodate both "#define _LARGEFILE64_SOURCE" and + * "#define _LARGEFILE64_SOURCE 1" as requesting 64-bit operations, (even + * though the former does not conform to the LFS document), but considering + * both "#undef _LARGEFILE64_SOURCE" and "#define _LARGEFILE64_SOURCE 0" as + * equivalently requesting no 64-bit operations + */ +#if defined(_LARGEFILE64_SOURCE) && -_LARGEFILE64_SOURCE - -1 == 1 +# undef _LARGEFILE64_SOURCE +#endif + +#if defined(__WATCOMC__) && !defined(Z_HAVE_UNISTD_H) +# define Z_HAVE_UNISTD_H +#endif +#ifndef Z_SOLO +# if defined(Z_HAVE_UNISTD_H) || defined(_LARGEFILE64_SOURCE) +# include /* for SEEK_*, off_t, and _LFS64_LARGEFILE */ +# ifdef VMS +# include /* for off_t */ +# endif +# ifndef z_off_t +# define z_off_t off_t +# endif +# endif +#endif + +#if defined(_LFS64_LARGEFILE) && _LFS64_LARGEFILE-0 +# define Z_LFS64 +#endif + +#if defined(_LARGEFILE64_SOURCE) && defined(Z_LFS64) +# define Z_LARGE64 +#endif + +#if defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS-0 == 64 && defined(Z_LFS64) +# define Z_WANT64 +#endif + +#if !defined(SEEK_SET) && !defined(Z_SOLO) +# define SEEK_SET 0 /* Seek from beginning of file. */ +# define SEEK_CUR 1 /* Seek from current position. */ +# define SEEK_END 2 /* Set file pointer to EOF plus "offset" */ +#endif + +#ifndef z_off_t +# define z_off_t long +#endif + +#if !defined(_WIN32) && defined(Z_LARGE64) +# define z_off64_t off64_t +#else +# if defined(_WIN32) && !defined(__GNUC__) && !defined(Z_SOLO) +# define z_off64_t __int64 +# else +# define z_off64_t z_off_t +# endif +#endif + +/* MVS linker does not support external names larger than 8 bytes */ +#if defined(__MVS__) + #pragma map(deflateInit_,"DEIN") + #pragma map(deflateInit2_,"DEIN2") + #pragma map(deflateEnd,"DEEND") + #pragma map(deflateBound,"DEBND") + #pragma map(inflateInit_,"ININ") + #pragma map(inflateInit2_,"ININ2") + #pragma map(inflateEnd,"INEND") + #pragma map(inflateSync,"INSY") + #pragma map(inflateSetDictionary,"INSEDI") + #pragma map(compressBound,"CMBND") + #pragma map(inflate_table,"INTABL") + #pragma map(inflate_fast,"INFA") + #pragma map(inflate_copyright,"INCOPY") +#endif + +#endif /* ZCONF_H */ diff --git a/platform/mcu/xr871/src/cedarx/external/include/zlib/zlib.h b/platform/mcu/xr871/src/cedarx/external/include/zlib/zlib.h new file mode 100644 index 0000000000..3e0c7672ac --- /dev/null +++ b/platform/mcu/xr871/src/cedarx/external/include/zlib/zlib.h @@ -0,0 +1,1768 @@ +/* zlib.h -- interface of the 'zlib' general purpose compression library + version 1.2.8, April 28th, 2013 + + Copyright (C) 1995-2013 Jean-loup Gailly and Mark Adler + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jean-loup Gailly Mark Adler + jloup@gzip.org madler@alumni.caltech.edu + + + The data format used by the zlib library is described by RFCs (Request for + Comments) 1950 to 1952 in the files http://tools.ietf.org/html/rfc1950 + (zlib format), rfc1951 (deflate format) and rfc1952 (gzip format). +*/ + +#ifndef ZLIB_H +#define ZLIB_H + +#include "zconf.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define ZLIB_VERSION "1.2.8" +#define ZLIB_VERNUM 0x1280 +#define ZLIB_VER_MAJOR 1 +#define ZLIB_VER_MINOR 2 +#define ZLIB_VER_REVISION 8 +#define ZLIB_VER_SUBREVISION 0 + +/* + The 'zlib' compression library provides in-memory compression and + decompression functions, including integrity checks of the uncompressed data. + This version of the library supports only one compression method (deflation) + but other algorithms will be added later and will have the same stream + interface. + + Compression can be done in a single step if the buffers are large enough, + or can be done by repeated calls of the compression function. In the latter + case, the application must provide more input and/or consume the output + (providing more output space) before each call. + + The compressed data format used by default by the in-memory functions is + the zlib format, which is a zlib wrapper documented in RFC 1950, wrapped + around a deflate stream, which is itself documented in RFC 1951. + + The library also supports reading and writing files in gzip (.gz) format + with an interface similar to that of stdio using the functions that start + with "gz". The gzip format is different from the zlib format. gzip is a + gzip wrapper, documented in RFC 1952, wrapped around a deflate stream. + + This library can optionally read and write gzip streams in memory as well. + + The zlib format was designed to be compact and fast for use in memory + and on communications channels. The gzip format was designed for single- + file compression on file systems, has a larger header than zlib to maintain + directory information, and uses a different, slower check method than zlib. + + The library does not install any signal handler. The decoder checks + the consistency of the compressed data, so the library should never crash + even in case of corrupted input. +*/ + +typedef voidpf (*alloc_func) OF((voidpf opaque, uInt items, uInt size)); +typedef void (*free_func) OF((voidpf opaque, voidpf address)); + +struct internal_state; + +typedef struct z_stream_s { + z_const Bytef *next_in; /* next input byte */ + uInt avail_in; /* number of bytes available at next_in */ + uLong total_in; /* total number of input bytes read so far */ + + Bytef *next_out; /* next output byte should be put there */ + uInt avail_out; /* remaining free space at next_out */ + uLong total_out; /* total number of bytes output so far */ + + z_const char *msg; /* last error message, NULL if no error */ + struct internal_state FAR *state; /* not visible by applications */ + + alloc_func zalloc; /* used to allocate the internal state */ + free_func zfree; /* used to free the internal state */ + voidpf opaque; /* private data object passed to zalloc and zfree */ + + int data_type; /* best guess about the data type: binary or text */ + uLong adler; /* adler32 value of the uncompressed data */ + uLong reserved; /* reserved for future use */ +} z_stream; + +typedef z_stream FAR *z_streamp; + +/* + gzip header information passed to and from zlib routines. See RFC 1952 + for more details on the meanings of these fields. +*/ +typedef struct gz_header_s { + int text; /* true if compressed data believed to be text */ + uLong time; /* modification time */ + int xflags; /* extra flags (not used when writing a gzip file) */ + int os; /* operating system */ + Bytef *extra; /* pointer to extra field or Z_NULL if none */ + uInt extra_len; /* extra field length (valid if extra != Z_NULL) */ + uInt extra_max; /* space at extra (only when reading header) */ + Bytef *name; /* pointer to zero-terminated file name or Z_NULL */ + uInt name_max; /* space at name (only when reading header) */ + Bytef *comment; /* pointer to zero-terminated comment or Z_NULL */ + uInt comm_max; /* space at comment (only when reading header) */ + int hcrc; /* true if there was or will be a header crc */ + int done; /* true when done reading gzip header (not used + when writing a gzip file) */ +} gz_header; + +typedef gz_header FAR *gz_headerp; + +/* + The application must update next_in and avail_in when avail_in has dropped + to zero. It must update next_out and avail_out when avail_out has dropped + to zero. The application must initialize zalloc, zfree and opaque before + calling the init function. All other fields are set by the compression + library and must not be updated by the application. + + The opaque value provided by the application will be passed as the first + parameter for calls of zalloc and zfree. This can be useful for custom + memory management. The compression library attaches no meaning to the + opaque value. + + zalloc must return Z_NULL if there is not enough memory for the object. + If zlib is used in a multi-threaded application, zalloc and zfree must be + thread safe. + + On 16-bit systems, the functions zalloc and zfree must be able to allocate + exactly 65536 bytes, but will not be required to allocate more than this if + the symbol MAXSEG_64K is defined (see zconf.h). WARNING: On MSDOS, pointers + returned by zalloc for objects of exactly 65536 bytes *must* have their + offset normalized to zero. The default allocation function provided by this + library ensures this (see zutil.c). To reduce memory requirements and avoid + any allocation of 64K objects, at the expense of compression ratio, compile + the library with -DMAX_WBITS=14 (see zconf.h). + + The fields total_in and total_out can be used for statistics or progress + reports. After compression, total_in holds the total size of the + uncompressed data and may be saved for use in the decompressor (particularly + if the decompressor wants to decompress everything in a single step). +*/ + + /* constants */ + +#define Z_NO_FLUSH 0 +#define Z_PARTIAL_FLUSH 1 +#define Z_SYNC_FLUSH 2 +#define Z_FULL_FLUSH 3 +#define Z_FINISH 4 +#define Z_BLOCK 5 +#define Z_TREES 6 +/* Allowed flush values; see deflate() and inflate() below for details */ + +#define Z_OK 0 +#define Z_STREAM_END 1 +#define Z_NEED_DICT 2 +#define Z_ERRNO (-1) +#define Z_STREAM_ERROR (-2) +#define Z_DATA_ERROR (-3) +#define Z_MEM_ERROR (-4) +#define Z_BUF_ERROR (-5) +#define Z_VERSION_ERROR (-6) +/* Return codes for the compression/decompression functions. Negative values + * are errors, positive values are used for special but normal events. + */ + +#define Z_NO_COMPRESSION 0 +#define Z_BEST_SPEED 1 +#define Z_BEST_COMPRESSION 9 +#define Z_DEFAULT_COMPRESSION (-1) +/* compression levels */ + +#define Z_FILTERED 1 +#define Z_HUFFMAN_ONLY 2 +#define Z_RLE 3 +#define Z_FIXED 4 +#define Z_DEFAULT_STRATEGY 0 +/* compression strategy; see deflateInit2() below for details */ + +#define Z_BINARY 0 +#define Z_TEXT 1 +#define Z_ASCII Z_TEXT /* for compatibility with 1.2.2 and earlier */ +#define Z_UNKNOWN 2 +/* Possible values of the data_type field (though see inflate()) */ + +#define Z_DEFLATED 8 +/* The deflate compression method (the only one supported in this version) */ + +#define Z_NULL 0 /* for initializing zalloc, zfree, opaque */ + +#define zlib_version zlibVersion() +/* for compatibility with versions < 1.0.2 */ + + + /* basic functions */ + +ZEXTERN const char * ZEXPORT zlibVersion OF((void)); +/* The application can compare zlibVersion and ZLIB_VERSION for consistency. + If the first character differs, the library code actually used is not + compatible with the zlib.h header file used by the application. This check + is automatically made by deflateInit and inflateInit. + */ + +/* +ZEXTERN int ZEXPORT deflateInit OF((z_streamp strm, int level)); + + Initializes the internal stream state for compression. The fields + zalloc, zfree and opaque must be initialized before by the caller. If + zalloc and zfree are set to Z_NULL, deflateInit updates them to use default + allocation functions. + + The compression level must be Z_DEFAULT_COMPRESSION, or between 0 and 9: + 1 gives best speed, 9 gives best compression, 0 gives no compression at all + (the input data is simply copied a block at a time). Z_DEFAULT_COMPRESSION + requests a default compromise between speed and compression (currently + equivalent to level 6). + + deflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_STREAM_ERROR if level is not a valid compression level, or + Z_VERSION_ERROR if the zlib library version (zlib_version) is incompatible + with the version assumed by the caller (ZLIB_VERSION). msg is set to null + if there is no error message. deflateInit does not perform any compression: + this will be done by deflate(). +*/ + + +ZEXTERN int ZEXPORT deflate OF((z_streamp strm, int flush)); +/* + deflate compresses as much data as possible, and stops when the input + buffer becomes empty or the output buffer becomes full. It may introduce + some output latency (reading input without producing any output) except when + forced to flush. + + The detailed semantics are as follows. deflate performs one or both of the + following actions: + + - Compress more input starting at next_in and update next_in and avail_in + accordingly. If not all input can be processed (because there is not + enough room in the output buffer), next_in and avail_in are updated and + processing will resume at this point for the next call of deflate(). + + - Provide more output starting at next_out and update next_out and avail_out + accordingly. This action is forced if the parameter flush is non zero. + Forcing flush frequently degrades the compression ratio, so this parameter + should be set only when necessary (in interactive applications). Some + output may be provided even if flush is not set. + + Before the call of deflate(), the application should ensure that at least + one of the actions is possible, by providing more input and/or consuming more + output, and updating avail_in or avail_out accordingly; avail_out should + never be zero before the call. The application can consume the compressed + output when it wants, for example when the output buffer is full (avail_out + == 0), or after each call of deflate(). If deflate returns Z_OK and with + zero avail_out, it must be called again after making room in the output + buffer because there might be more output pending. + + Normally the parameter flush is set to Z_NO_FLUSH, which allows deflate to + decide how much data to accumulate before producing output, in order to + maximize compression. + + If the parameter flush is set to Z_SYNC_FLUSH, all pending output is + flushed to the output buffer and the output is aligned on a byte boundary, so + that the decompressor can get all input data available so far. (In + particular avail_in is zero after the call if enough output space has been + provided before the call.) Flushing may degrade compression for some + compression algorithms and so it should be used only when necessary. This + completes the current deflate block and follows it with an empty stored block + that is three bits plus filler bits to the next byte, followed by four bytes + (00 00 ff ff). + + If flush is set to Z_PARTIAL_FLUSH, all pending output is flushed to the + output buffer, but the output is not aligned to a byte boundary. All of the + input data so far will be available to the decompressor, as for Z_SYNC_FLUSH. + This completes the current deflate block and follows it with an empty fixed + codes block that is 10 bits long. This assures that enough bytes are output + in order for the decompressor to finish the block before the empty fixed code + block. + + If flush is set to Z_BLOCK, a deflate block is completed and emitted, as + for Z_SYNC_FLUSH, but the output is not aligned on a byte boundary, and up to + seven bits of the current block are held to be written as the next byte after + the next deflate block is completed. In this case, the decompressor may not + be provided enough bits at this point in order to complete decompression of + the data provided so far to the compressor. It may need to wait for the next + block to be emitted. This is for advanced applications that need to control + the emission of deflate blocks. + + If flush is set to Z_FULL_FLUSH, all output is flushed as with + Z_SYNC_FLUSH, and the compression state is reset so that decompression can + restart from this point if previous compressed data has been damaged or if + random access is desired. Using Z_FULL_FLUSH too often can seriously degrade + compression. + + If deflate returns with avail_out == 0, this function must be called again + with the same value of the flush parameter and more output space (updated + avail_out), until the flush is complete (deflate returns with non-zero + avail_out). In the case of a Z_FULL_FLUSH or Z_SYNC_FLUSH, make sure that + avail_out is greater than six to avoid repeated flush markers due to + avail_out == 0 on return. + + If the parameter flush is set to Z_FINISH, pending input is processed, + pending output is flushed and deflate returns with Z_STREAM_END if there was + enough output space; if deflate returns with Z_OK, this function must be + called again with Z_FINISH and more output space (updated avail_out) but no + more input data, until it returns with Z_STREAM_END or an error. After + deflate has returned Z_STREAM_END, the only possible operations on the stream + are deflateReset or deflateEnd. + + Z_FINISH can be used immediately after deflateInit if all the compression + is to be done in a single step. In this case, avail_out must be at least the + value returned by deflateBound (see below). Then deflate is guaranteed to + return Z_STREAM_END. If not enough output space is provided, deflate will + not return Z_STREAM_END, and it must be called again as described above. + + deflate() sets strm->adler to the adler32 checksum of all input read + so far (that is, total_in bytes). + + deflate() may update strm->data_type if it can make a good guess about + the input data type (Z_BINARY or Z_TEXT). In doubt, the data is considered + binary. This field is only for information purposes and does not affect the + compression algorithm in any manner. + + deflate() returns Z_OK if some progress has been made (more input + processed or more output produced), Z_STREAM_END if all input has been + consumed and all output has been produced (only when flush is set to + Z_FINISH), Z_STREAM_ERROR if the stream state was inconsistent (for example + if next_in or next_out was Z_NULL), Z_BUF_ERROR if no progress is possible + (for example avail_in or avail_out was zero). Note that Z_BUF_ERROR is not + fatal, and deflate() can be called again with more input and more output + space to continue compressing. +*/ + + +ZEXTERN int ZEXPORT deflateEnd OF((z_streamp strm)); +/* + All dynamically allocated data structures for this stream are freed. + This function discards any unprocessed input and does not flush any pending + output. + + deflateEnd returns Z_OK if success, Z_STREAM_ERROR if the + stream state was inconsistent, Z_DATA_ERROR if the stream was freed + prematurely (some input or output was discarded). In the error case, msg + may be set but then points to a static string (which must not be + deallocated). +*/ + + +/* +ZEXTERN int ZEXPORT inflateInit OF((z_streamp strm)); + + Initializes the internal stream state for decompression. The fields + next_in, avail_in, zalloc, zfree and opaque must be initialized before by + the caller. If next_in is not Z_NULL and avail_in is large enough (the + exact value depends on the compression method), inflateInit determines the + compression method from the zlib header and allocates all data structures + accordingly; otherwise the allocation will be deferred to the first call of + inflate. If zalloc and zfree are set to Z_NULL, inflateInit updates them to + use default allocation functions. + + inflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_VERSION_ERROR if the zlib library version is incompatible with the + version assumed by the caller, or Z_STREAM_ERROR if the parameters are + invalid, such as a null pointer to the structure. msg is set to null if + there is no error message. inflateInit does not perform any decompression + apart from possibly reading the zlib header if present: actual decompression + will be done by inflate(). (So next_in and avail_in may be modified, but + next_out and avail_out are unused and unchanged.) The current implementation + of inflateInit() does not process any header information -- that is deferred + until inflate() is called. +*/ + + +ZEXTERN int ZEXPORT inflate OF((z_streamp strm, int flush)); +/* + inflate decompresses as much data as possible, and stops when the input + buffer becomes empty or the output buffer becomes full. It may introduce + some output latency (reading input without producing any output) except when + forced to flush. + + The detailed semantics are as follows. inflate performs one or both of the + following actions: + + - Decompress more input starting at next_in and update next_in and avail_in + accordingly. If not all input can be processed (because there is not + enough room in the output buffer), next_in is updated and processing will + resume at this point for the next call of inflate(). + + - Provide more output starting at next_out and update next_out and avail_out + accordingly. inflate() provides as much output as possible, until there is + no more input data or no more space in the output buffer (see below about + the flush parameter). + + Before the call of inflate(), the application should ensure that at least + one of the actions is possible, by providing more input and/or consuming more + output, and updating the next_* and avail_* values accordingly. The + application can consume the uncompressed output when it wants, for example + when the output buffer is full (avail_out == 0), or after each call of + inflate(). If inflate returns Z_OK and with zero avail_out, it must be + called again after making room in the output buffer because there might be + more output pending. + + The flush parameter of inflate() can be Z_NO_FLUSH, Z_SYNC_FLUSH, Z_FINISH, + Z_BLOCK, or Z_TREES. Z_SYNC_FLUSH requests that inflate() flush as much + output as possible to the output buffer. Z_BLOCK requests that inflate() + stop if and when it gets to the next deflate block boundary. When decoding + the zlib or gzip format, this will cause inflate() to return immediately + after the header and before the first block. When doing a raw inflate, + inflate() will go ahead and process the first block, and will return when it + gets to the end of that block, or when it runs out of data. + + The Z_BLOCK option assists in appending to or combining deflate streams. + Also to assist in this, on return inflate() will set strm->data_type to the + number of unused bits in the last byte taken from strm->next_in, plus 64 if + inflate() is currently decoding the last block in the deflate stream, plus + 128 if inflate() returned immediately after decoding an end-of-block code or + decoding the complete header up to just before the first byte of the deflate + stream. The end-of-block will not be indicated until all of the uncompressed + data from that block has been written to strm->next_out. The number of + unused bits may in general be greater than seven, except when bit 7 of + data_type is set, in which case the number of unused bits will be less than + eight. data_type is set as noted here every time inflate() returns for all + flush options, and so can be used to determine the amount of currently + consumed input in bits. + + The Z_TREES option behaves as Z_BLOCK does, but it also returns when the + end of each deflate block header is reached, before any actual data in that + block is decoded. This allows the caller to determine the length of the + deflate block header for later use in random access within a deflate block. + 256 is added to the value of strm->data_type when inflate() returns + immediately after reaching the end of the deflate block header. + + inflate() should normally be called until it returns Z_STREAM_END or an + error. However if all decompression is to be performed in a single step (a + single call of inflate), the parameter flush should be set to Z_FINISH. In + this case all pending input is processed and all pending output is flushed; + avail_out must be large enough to hold all of the uncompressed data for the + operation to complete. (The size of the uncompressed data may have been + saved by the compressor for this purpose.) The use of Z_FINISH is not + required to perform an inflation in one step. However it may be used to + inform inflate that a faster approach can be used for the single inflate() + call. Z_FINISH also informs inflate to not maintain a sliding window if the + stream completes, which reduces inflate's memory footprint. If the stream + does not complete, either because not all of the stream is provided or not + enough output space is provided, then a sliding window will be allocated and + inflate() can be called again to continue the operation as if Z_NO_FLUSH had + been used. + + In this implementation, inflate() always flushes as much output as + possible to the output buffer, and always uses the faster approach on the + first call. So the effects of the flush parameter in this implementation are + on the return value of inflate() as noted below, when inflate() returns early + when Z_BLOCK or Z_TREES is used, and when inflate() avoids the allocation of + memory for a sliding window when Z_FINISH is used. + + If a preset dictionary is needed after this call (see inflateSetDictionary + below), inflate sets strm->adler to the Adler-32 checksum of the dictionary + chosen by the compressor and returns Z_NEED_DICT; otherwise it sets + strm->adler to the Adler-32 checksum of all output produced so far (that is, + total_out bytes) and returns Z_OK, Z_STREAM_END or an error code as described + below. At the end of the stream, inflate() checks that its computed adler32 + checksum is equal to that saved by the compressor and returns Z_STREAM_END + only if the checksum is correct. + + inflate() can decompress and check either zlib-wrapped or gzip-wrapped + deflate data. The header type is detected automatically, if requested when + initializing with inflateInit2(). Any information contained in the gzip + header is not retained, so applications that need that information should + instead use raw inflate, see inflateInit2() below, or inflateBack() and + perform their own processing of the gzip header and trailer. When processing + gzip-wrapped deflate data, strm->adler32 is set to the CRC-32 of the output + producted so far. The CRC-32 is checked against the gzip trailer. + + inflate() returns Z_OK if some progress has been made (more input processed + or more output produced), Z_STREAM_END if the end of the compressed data has + been reached and all uncompressed output has been produced, Z_NEED_DICT if a + preset dictionary is needed at this point, Z_DATA_ERROR if the input data was + corrupted (input stream not conforming to the zlib format or incorrect check + value), Z_STREAM_ERROR if the stream structure was inconsistent (for example + next_in or next_out was Z_NULL), Z_MEM_ERROR if there was not enough memory, + Z_BUF_ERROR if no progress is possible or if there was not enough room in the + output buffer when Z_FINISH is used. Note that Z_BUF_ERROR is not fatal, and + inflate() can be called again with more input and more output space to + continue decompressing. If Z_DATA_ERROR is returned, the application may + then call inflateSync() to look for a good compression block if a partial + recovery of the data is desired. +*/ + + +ZEXTERN int ZEXPORT inflateEnd OF((z_streamp strm)); +/* + All dynamically allocated data structures for this stream are freed. + This function discards any unprocessed input and does not flush any pending + output. + + inflateEnd returns Z_OK if success, Z_STREAM_ERROR if the stream state + was inconsistent. In the error case, msg may be set but then points to a + static string (which must not be deallocated). +*/ + + + /* Advanced functions */ + +/* + The following functions are needed only in some special applications. +*/ + +/* +ZEXTERN int ZEXPORT deflateInit2 OF((z_streamp strm, + int level, + int method, + int windowBits, + int memLevel, + int strategy)); + + This is another version of deflateInit with more compression options. The + fields next_in, zalloc, zfree and opaque must be initialized before by the + caller. + + The method parameter is the compression method. It must be Z_DEFLATED in + this version of the library. + + The windowBits parameter is the base two logarithm of the window size + (the size of the history buffer). It should be in the range 8..15 for this + version of the library. Larger values of this parameter result in better + compression at the expense of memory usage. The default value is 15 if + deflateInit is used instead. + + windowBits can also be -8..-15 for raw deflate. In this case, -windowBits + determines the window size. deflate() will then generate raw deflate data + with no zlib header or trailer, and will not compute an adler32 check value. + + windowBits can also be greater than 15 for optional gzip encoding. Add + 16 to windowBits to write a simple gzip header and trailer around the + compressed data instead of a zlib wrapper. The gzip header will have no + file name, no extra data, no comment, no modification time (set to zero), no + header crc, and the operating system will be set to 255 (unknown). If a + gzip stream is being written, strm->adler is a crc32 instead of an adler32. + + The memLevel parameter specifies how much memory should be allocated + for the internal compression state. memLevel=1 uses minimum memory but is + slow and reduces compression ratio; memLevel=9 uses maximum memory for + optimal speed. The default value is 8. See zconf.h for total memory usage + as a function of windowBits and memLevel. + + The strategy parameter is used to tune the compression algorithm. Use the + value Z_DEFAULT_STRATEGY for normal data, Z_FILTERED for data produced by a + filter (or predictor), Z_HUFFMAN_ONLY to force Huffman encoding only (no + string match), or Z_RLE to limit match distances to one (run-length + encoding). Filtered data consists mostly of small values with a somewhat + random distribution. In this case, the compression algorithm is tuned to + compress them better. The effect of Z_FILTERED is to force more Huffman + coding and less string matching; it is somewhat intermediate between + Z_DEFAULT_STRATEGY and Z_HUFFMAN_ONLY. Z_RLE is designed to be almost as + fast as Z_HUFFMAN_ONLY, but give better compression for PNG image data. The + strategy parameter only affects the compression ratio but not the + correctness of the compressed output even if it is not set appropriately. + Z_FIXED prevents the use of dynamic Huffman codes, allowing for a simpler + decoder for special applications. + + deflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_STREAM_ERROR if any parameter is invalid (such as an invalid + method), or Z_VERSION_ERROR if the zlib library version (zlib_version) is + incompatible with the version assumed by the caller (ZLIB_VERSION). msg is + set to null if there is no error message. deflateInit2 does not perform any + compression: this will be done by deflate(). +*/ + +ZEXTERN int ZEXPORT deflateSetDictionary OF((z_streamp strm, + const Bytef *dictionary, + uInt dictLength)); +/* + Initializes the compression dictionary from the given byte sequence + without producing any compressed output. When using the zlib format, this + function must be called immediately after deflateInit, deflateInit2 or + deflateReset, and before any call of deflate. When doing raw deflate, this + function must be called either before any call of deflate, or immediately + after the completion of a deflate block, i.e. after all input has been + consumed and all output has been delivered when using any of the flush + options Z_BLOCK, Z_PARTIAL_FLUSH, Z_SYNC_FLUSH, or Z_FULL_FLUSH. The + compressor and decompressor must use exactly the same dictionary (see + inflateSetDictionary). + + The dictionary should consist of strings (byte sequences) that are likely + to be encountered later in the data to be compressed, with the most commonly + used strings preferably put towards the end of the dictionary. Using a + dictionary is most useful when the data to be compressed is short and can be + predicted with good accuracy; the data can then be compressed better than + with the default empty dictionary. + + Depending on the size of the compression data structures selected by + deflateInit or deflateInit2, a part of the dictionary may in effect be + discarded, for example if the dictionary is larger than the window size + provided in deflateInit or deflateInit2. Thus the strings most likely to be + useful should be put at the end of the dictionary, not at the front. In + addition, the current implementation of deflate will use at most the window + size minus 262 bytes of the provided dictionary. + + Upon return of this function, strm->adler is set to the adler32 value + of the dictionary; the decompressor may later use this value to determine + which dictionary has been used by the compressor. (The adler32 value + applies to the whole dictionary even if only a subset of the dictionary is + actually used by the compressor.) If a raw deflate was requested, then the + adler32 value is not computed and strm->adler is not set. + + deflateSetDictionary returns Z_OK if success, or Z_STREAM_ERROR if a + parameter is invalid (e.g. dictionary being Z_NULL) or the stream state is + inconsistent (for example if deflate has already been called for this stream + or if not at a block boundary for raw deflate). deflateSetDictionary does + not perform any compression: this will be done by deflate(). +*/ + +ZEXTERN int ZEXPORT deflateCopy OF((z_streamp dest, + z_streamp source)); +/* + Sets the destination stream as a complete copy of the source stream. + + This function can be useful when several compression strategies will be + tried, for example when there are several ways of pre-processing the input + data with a filter. The streams that will be discarded should then be freed + by calling deflateEnd. Note that deflateCopy duplicates the internal + compression state which can be quite large, so this strategy is slow and can + consume lots of memory. + + deflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_STREAM_ERROR if the source stream state was inconsistent + (such as zalloc being Z_NULL). msg is left unchanged in both source and + destination. +*/ + +ZEXTERN int ZEXPORT deflateReset OF((z_streamp strm)); +/* + This function is equivalent to deflateEnd followed by deflateInit, + but does not free and reallocate all the internal compression state. The + stream will keep the same compression level and any other attributes that + may have been set by deflateInit2. + + deflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent (such as zalloc or state being Z_NULL). +*/ + +ZEXTERN int ZEXPORT deflateParams OF((z_streamp strm, + int level, + int strategy)); +/* + Dynamically update the compression level and compression strategy. The + interpretation of level and strategy is as in deflateInit2. This can be + used to switch between compression and straight copy of the input data, or + to switch to a different kind of input data requiring a different strategy. + If the compression level is changed, the input available so far is + compressed with the old level (and may be flushed); the new level will take + effect only at the next call of deflate(). + + Before the call of deflateParams, the stream state must be set as for + a call of deflate(), since the currently available input may have to be + compressed and flushed. In particular, strm->avail_out must be non-zero. + + deflateParams returns Z_OK if success, Z_STREAM_ERROR if the source + stream state was inconsistent or if a parameter was invalid, Z_BUF_ERROR if + strm->avail_out was zero. +*/ + +ZEXTERN int ZEXPORT deflateTune OF((z_streamp strm, + int good_length, + int max_lazy, + int nice_length, + int max_chain)); +/* + Fine tune deflate's internal compression parameters. This should only be + used by someone who understands the algorithm used by zlib's deflate for + searching for the best matching string, and even then only by the most + fanatic optimizer trying to squeeze out the last compressed bit for their + specific input data. Read the deflate.c source code for the meaning of the + max_lazy, good_length, nice_length, and max_chain parameters. + + deflateTune() can be called after deflateInit() or deflateInit2(), and + returns Z_OK on success, or Z_STREAM_ERROR for an invalid deflate stream. + */ + +ZEXTERN uLong ZEXPORT deflateBound OF((z_streamp strm, + uLong sourceLen)); +/* + deflateBound() returns an upper bound on the compressed size after + deflation of sourceLen bytes. It must be called after deflateInit() or + deflateInit2(), and after deflateSetHeader(), if used. This would be used + to allocate an output buffer for deflation in a single pass, and so would be + called before deflate(). If that first deflate() call is provided the + sourceLen input bytes, an output buffer allocated to the size returned by + deflateBound(), and the flush value Z_FINISH, then deflate() is guaranteed + to return Z_STREAM_END. Note that it is possible for the compressed size to + be larger than the value returned by deflateBound() if flush options other + than Z_FINISH or Z_NO_FLUSH are used. +*/ + +ZEXTERN int ZEXPORT deflatePending OF((z_streamp strm, + unsigned *pending, + int *bits)); +/* + deflatePending() returns the number of bytes and bits of output that have + been generated, but not yet provided in the available output. The bytes not + provided would be due to the available output space having being consumed. + The number of bits of output not provided are between 0 and 7, where they + await more bits to join them in order to fill out a full byte. If pending + or bits are Z_NULL, then those values are not set. + + deflatePending returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. + */ + +ZEXTERN int ZEXPORT deflatePrime OF((z_streamp strm, + int bits, + int value)); +/* + deflatePrime() inserts bits in the deflate output stream. The intent + is that this function is used to start off the deflate output with the bits + leftover from a previous deflate stream when appending to it. As such, this + function can only be used for raw deflate, and must be used before the first + deflate() call after a deflateInit2() or deflateReset(). bits must be less + than or equal to 16, and that many of the least significant bits of value + will be inserted in the output. + + deflatePrime returns Z_OK if success, Z_BUF_ERROR if there was not enough + room in the internal buffer to insert the bits, or Z_STREAM_ERROR if the + source stream state was inconsistent. +*/ + +ZEXTERN int ZEXPORT deflateSetHeader OF((z_streamp strm, + gz_headerp head)); +/* + deflateSetHeader() provides gzip header information for when a gzip + stream is requested by deflateInit2(). deflateSetHeader() may be called + after deflateInit2() or deflateReset() and before the first call of + deflate(). The text, time, os, extra field, name, and comment information + in the provided gz_header structure are written to the gzip header (xflag is + ignored -- the extra flags are set according to the compression level). The + caller must assure that, if not Z_NULL, name and comment are terminated with + a zero byte, and that if extra is not Z_NULL, that extra_len bytes are + available there. If hcrc is true, a gzip header crc is included. Note that + the current versions of the command-line version of gzip (up through version + 1.3.x) do not support header crc's, and will report that it is a "multi-part + gzip file" and give up. + + If deflateSetHeader is not used, the default gzip header has text false, + the time set to zero, and os set to 255, with no extra, name, or comment + fields. The gzip header is returned to the default state by deflateReset(). + + deflateSetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. +*/ + +/* +ZEXTERN int ZEXPORT inflateInit2 OF((z_streamp strm, + int windowBits)); + + This is another version of inflateInit with an extra parameter. The + fields next_in, avail_in, zalloc, zfree and opaque must be initialized + before by the caller. + + The windowBits parameter is the base two logarithm of the maximum window + size (the size of the history buffer). It should be in the range 8..15 for + this version of the library. The default value is 15 if inflateInit is used + instead. windowBits must be greater than or equal to the windowBits value + provided to deflateInit2() while compressing, or it must be equal to 15 if + deflateInit2() was not used. If a compressed stream with a larger window + size is given as input, inflate() will return with the error code + Z_DATA_ERROR instead of trying to allocate a larger window. + + windowBits can also be zero to request that inflate use the window size in + the zlib header of the compressed stream. + + windowBits can also be -8..-15 for raw inflate. In this case, -windowBits + determines the window size. inflate() will then process raw deflate data, + not looking for a zlib or gzip header, not generating a check value, and not + looking for any check values for comparison at the end of the stream. This + is for use with other formats that use the deflate compressed data format + such as zip. Those formats provide their own check values. If a custom + format is developed using the raw deflate format for compressed data, it is + recommended that a check value such as an adler32 or a crc32 be applied to + the uncompressed data as is done in the zlib, gzip, and zip formats. For + most applications, the zlib format should be used as is. Note that comments + above on the use in deflateInit2() applies to the magnitude of windowBits. + + windowBits can also be greater than 15 for optional gzip decoding. Add + 32 to windowBits to enable zlib and gzip decoding with automatic header + detection, or add 16 to decode only the gzip format (the zlib format will + return a Z_DATA_ERROR). If a gzip stream is being decoded, strm->adler is a + crc32 instead of an adler32. + + inflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_VERSION_ERROR if the zlib library version is incompatible with the + version assumed by the caller, or Z_STREAM_ERROR if the parameters are + invalid, such as a null pointer to the structure. msg is set to null if + there is no error message. inflateInit2 does not perform any decompression + apart from possibly reading the zlib header if present: actual decompression + will be done by inflate(). (So next_in and avail_in may be modified, but + next_out and avail_out are unused and unchanged.) The current implementation + of inflateInit2() does not process any header information -- that is + deferred until inflate() is called. +*/ + +ZEXTERN int ZEXPORT inflateSetDictionary OF((z_streamp strm, + const Bytef *dictionary, + uInt dictLength)); +/* + Initializes the decompression dictionary from the given uncompressed byte + sequence. This function must be called immediately after a call of inflate, + if that call returned Z_NEED_DICT. The dictionary chosen by the compressor + can be determined from the adler32 value returned by that call of inflate. + The compressor and decompressor must use exactly the same dictionary (see + deflateSetDictionary). For raw inflate, this function can be called at any + time to set the dictionary. If the provided dictionary is smaller than the + window and there is already data in the window, then the provided dictionary + will amend what's there. The application must insure that the dictionary + that was used for compression is provided. + + inflateSetDictionary returns Z_OK if success, Z_STREAM_ERROR if a + parameter is invalid (e.g. dictionary being Z_NULL) or the stream state is + inconsistent, Z_DATA_ERROR if the given dictionary doesn't match the + expected one (incorrect adler32 value). inflateSetDictionary does not + perform any decompression: this will be done by subsequent calls of + inflate(). +*/ + +ZEXTERN int ZEXPORT inflateGetDictionary OF((z_streamp strm, + Bytef *dictionary, + uInt *dictLength)); +/* + Returns the sliding dictionary being maintained by inflate. dictLength is + set to the number of bytes in the dictionary, and that many bytes are copied + to dictionary. dictionary must have enough space, where 32768 bytes is + always enough. If inflateGetDictionary() is called with dictionary equal to + Z_NULL, then only the dictionary length is returned, and nothing is copied. + Similary, if dictLength is Z_NULL, then it is not set. + + inflateGetDictionary returns Z_OK on success, or Z_STREAM_ERROR if the + stream state is inconsistent. +*/ + +ZEXTERN int ZEXPORT inflateSync OF((z_streamp strm)); +/* + Skips invalid compressed data until a possible full flush point (see above + for the description of deflate with Z_FULL_FLUSH) can be found, or until all + available input is skipped. No output is provided. + + inflateSync searches for a 00 00 FF FF pattern in the compressed data. + All full flush points have this pattern, but not all occurrences of this + pattern are full flush points. + + inflateSync returns Z_OK if a possible full flush point has been found, + Z_BUF_ERROR if no more input was provided, Z_DATA_ERROR if no flush point + has been found, or Z_STREAM_ERROR if the stream structure was inconsistent. + In the success case, the application may save the current current value of + total_in which indicates where valid compressed data was found. In the + error case, the application may repeatedly call inflateSync, providing more + input each time, until success or end of the input data. +*/ + +ZEXTERN int ZEXPORT inflateCopy OF((z_streamp dest, + z_streamp source)); +/* + Sets the destination stream as a complete copy of the source stream. + + This function can be useful when randomly accessing a large stream. The + first pass through the stream can periodically record the inflate state, + allowing restarting inflate at those points when randomly accessing the + stream. + + inflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_STREAM_ERROR if the source stream state was inconsistent + (such as zalloc being Z_NULL). msg is left unchanged in both source and + destination. +*/ + +ZEXTERN int ZEXPORT inflateReset OF((z_streamp strm)); +/* + This function is equivalent to inflateEnd followed by inflateInit, + but does not free and reallocate all the internal decompression state. The + stream will keep attributes that may have been set by inflateInit2. + + inflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent (such as zalloc or state being Z_NULL). +*/ + +ZEXTERN int ZEXPORT inflateReset2 OF((z_streamp strm, + int windowBits)); +/* + This function is the same as inflateReset, but it also permits changing + the wrap and window size requests. The windowBits parameter is interpreted + the same as it is for inflateInit2. + + inflateReset2 returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent (such as zalloc or state being Z_NULL), or if + the windowBits parameter is invalid. +*/ + +ZEXTERN int ZEXPORT inflatePrime OF((z_streamp strm, + int bits, + int value)); +/* + This function inserts bits in the inflate input stream. The intent is + that this function is used to start inflating at a bit position in the + middle of a byte. The provided bits will be used before any bytes are used + from next_in. This function should only be used with raw inflate, and + should be used before the first inflate() call after inflateInit2() or + inflateReset(). bits must be less than or equal to 16, and that many of the + least significant bits of value will be inserted in the input. + + If bits is negative, then the input stream bit buffer is emptied. Then + inflatePrime() can be called again to put bits in the buffer. This is used + to clear out bits leftover after feeding inflate a block description prior + to feeding inflate codes. + + inflatePrime returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. +*/ + +ZEXTERN long ZEXPORT inflateMark OF((z_streamp strm)); +/* + This function returns two values, one in the lower 16 bits of the return + value, and the other in the remaining upper bits, obtained by shifting the + return value down 16 bits. If the upper value is -1 and the lower value is + zero, then inflate() is currently decoding information outside of a block. + If the upper value is -1 and the lower value is non-zero, then inflate is in + the middle of a stored block, with the lower value equaling the number of + bytes from the input remaining to copy. If the upper value is not -1, then + it is the number of bits back from the current bit position in the input of + the code (literal or length/distance pair) currently being processed. In + that case the lower value is the number of bytes already emitted for that + code. + + A code is being processed if inflate is waiting for more input to complete + decoding of the code, or if it has completed decoding but is waiting for + more output space to write the literal or match data. + + inflateMark() is used to mark locations in the input data for random + access, which may be at bit positions, and to note those cases where the + output of a code may span boundaries of random access blocks. The current + location in the input stream can be determined from avail_in and data_type + as noted in the description for the Z_BLOCK flush parameter for inflate. + + inflateMark returns the value noted above or -1 << 16 if the provided + source stream state was inconsistent. +*/ + +ZEXTERN int ZEXPORT inflateGetHeader OF((z_streamp strm, + gz_headerp head)); +/* + inflateGetHeader() requests that gzip header information be stored in the + provided gz_header structure. inflateGetHeader() may be called after + inflateInit2() or inflateReset(), and before the first call of inflate(). + As inflate() processes the gzip stream, head->done is zero until the header + is completed, at which time head->done is set to one. If a zlib stream is + being decoded, then head->done is set to -1 to indicate that there will be + no gzip header information forthcoming. Note that Z_BLOCK or Z_TREES can be + used to force inflate() to return immediately after header processing is + complete and before any actual data is decompressed. + + The text, time, xflags, and os fields are filled in with the gzip header + contents. hcrc is set to true if there is a header CRC. (The header CRC + was valid if done is set to one.) If extra is not Z_NULL, then extra_max + contains the maximum number of bytes to write to extra. Once done is true, + extra_len contains the actual extra field length, and extra contains the + extra field, or that field truncated if extra_max is less than extra_len. + If name is not Z_NULL, then up to name_max characters are written there, + terminated with a zero unless the length is greater than name_max. If + comment is not Z_NULL, then up to comm_max characters are written there, + terminated with a zero unless the length is greater than comm_max. When any + of extra, name, or comment are not Z_NULL and the respective field is not + present in the header, then that field is set to Z_NULL to signal its + absence. This allows the use of deflateSetHeader() with the returned + structure to duplicate the header. However if those fields are set to + allocated memory, then the application will need to save those pointers + elsewhere so that they can be eventually freed. + + If inflateGetHeader is not used, then the header information is simply + discarded. The header is always checked for validity, including the header + CRC if present. inflateReset() will reset the process to discard the header + information. The application would need to call inflateGetHeader() again to + retrieve the header from the next gzip stream. + + inflateGetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. +*/ + +/* +ZEXTERN int ZEXPORT inflateBackInit OF((z_streamp strm, int windowBits, + unsigned char FAR *window)); + + Initialize the internal stream state for decompression using inflateBack() + calls. The fields zalloc, zfree and opaque in strm must be initialized + before the call. If zalloc and zfree are Z_NULL, then the default library- + derived memory allocation routines are used. windowBits is the base two + logarithm of the window size, in the range 8..15. window is a caller + supplied buffer of that size. Except for special applications where it is + assured that deflate was used with small window sizes, windowBits must be 15 + and a 32K byte window must be supplied to be able to decompress general + deflate streams. + + See inflateBack() for the usage of these routines. + + inflateBackInit will return Z_OK on success, Z_STREAM_ERROR if any of + the parameters are invalid, Z_MEM_ERROR if the internal state could not be + allocated, or Z_VERSION_ERROR if the version of the library does not match + the version of the header file. +*/ + +typedef unsigned (*in_func) OF((void FAR *, + z_const unsigned char FAR * FAR *)); +typedef int (*out_func) OF((void FAR *, unsigned char FAR *, unsigned)); + +ZEXTERN int ZEXPORT inflateBack OF((z_streamp strm, + in_func in, void FAR *in_desc, + out_func out, void FAR *out_desc)); +/* + inflateBack() does a raw inflate with a single call using a call-back + interface for input and output. This is potentially more efficient than + inflate() for file i/o applications, in that it avoids copying between the + output and the sliding window by simply making the window itself the output + buffer. inflate() can be faster on modern CPUs when used with large + buffers. inflateBack() trusts the application to not change the output + buffer passed by the output function, at least until inflateBack() returns. + + inflateBackInit() must be called first to allocate the internal state + and to initialize the state with the user-provided window buffer. + inflateBack() may then be used multiple times to inflate a complete, raw + deflate stream with each call. inflateBackEnd() is then called to free the + allocated state. + + A raw deflate stream is one with no zlib or gzip header or trailer. + This routine would normally be used in a utility that reads zip or gzip + files and writes out uncompressed files. The utility would decode the + header and process the trailer on its own, hence this routine expects only + the raw deflate stream to decompress. This is different from the normal + behavior of inflate(), which expects either a zlib or gzip header and + trailer around the deflate stream. + + inflateBack() uses two subroutines supplied by the caller that are then + called by inflateBack() for input and output. inflateBack() calls those + routines until it reads a complete deflate stream and writes out all of the + uncompressed data, or until it encounters an error. The function's + parameters and return types are defined above in the in_func and out_func + typedefs. inflateBack() will call in(in_desc, &buf) which should return the + number of bytes of provided input, and a pointer to that input in buf. If + there is no input available, in() must return zero--buf is ignored in that + case--and inflateBack() will return a buffer error. inflateBack() will call + out(out_desc, buf, len) to write the uncompressed data buf[0..len-1]. out() + should return zero on success, or non-zero on failure. If out() returns + non-zero, inflateBack() will return with an error. Neither in() nor out() + are permitted to change the contents of the window provided to + inflateBackInit(), which is also the buffer that out() uses to write from. + The length written by out() will be at most the window size. Any non-zero + amount of input may be provided by in(). + + For convenience, inflateBack() can be provided input on the first call by + setting strm->next_in and strm->avail_in. If that input is exhausted, then + in() will be called. Therefore strm->next_in must be initialized before + calling inflateBack(). If strm->next_in is Z_NULL, then in() will be called + immediately for input. If strm->next_in is not Z_NULL, then strm->avail_in + must also be initialized, and then if strm->avail_in is not zero, input will + initially be taken from strm->next_in[0 .. strm->avail_in - 1]. + + The in_desc and out_desc parameters of inflateBack() is passed as the + first parameter of in() and out() respectively when they are called. These + descriptors can be optionally used to pass any information that the caller- + supplied in() and out() functions need to do their job. + + On return, inflateBack() will set strm->next_in and strm->avail_in to + pass back any unused input that was provided by the last in() call. The + return values of inflateBack() can be Z_STREAM_END on success, Z_BUF_ERROR + if in() or out() returned an error, Z_DATA_ERROR if there was a format error + in the deflate stream (in which case strm->msg is set to indicate the nature + of the error), or Z_STREAM_ERROR if the stream was not properly initialized. + In the case of Z_BUF_ERROR, an input or output error can be distinguished + using strm->next_in which will be Z_NULL only if in() returned an error. If + strm->next_in is not Z_NULL, then the Z_BUF_ERROR was due to out() returning + non-zero. (in() will always be called before out(), so strm->next_in is + assured to be defined if out() returns non-zero.) Note that inflateBack() + cannot return Z_OK. +*/ + +ZEXTERN int ZEXPORT inflateBackEnd OF((z_streamp strm)); +/* + All memory allocated by inflateBackInit() is freed. + + inflateBackEnd() returns Z_OK on success, or Z_STREAM_ERROR if the stream + state was inconsistent. +*/ + +ZEXTERN uLong ZEXPORT zlibCompileFlags OF((void)); +/* Return flags indicating compile-time options. + + Type sizes, two bits each, 00 = 16 bits, 01 = 32, 10 = 64, 11 = other: + 1.0: size of uInt + 3.2: size of uLong + 5.4: size of voidpf (pointer) + 7.6: size of z_off_t + + Compiler, assembler, and debug options: + 8: DEBUG + 9: ASMV or ASMINF -- use ASM code + 10: ZLIB_WINAPI -- exported functions use the WINAPI calling convention + 11: 0 (reserved) + + One-time table building (smaller code, but not thread-safe if true): + 12: BUILDFIXED -- build static block decoding tables when needed + 13: DYNAMIC_CRC_TABLE -- build CRC calculation tables when needed + 14,15: 0 (reserved) + + Library content (indicates missing functionality): + 16: NO_GZCOMPRESS -- gz* functions cannot compress (to avoid linking + deflate code when not needed) + 17: NO_GZIP -- deflate can't write gzip streams, and inflate can't detect + and decode gzip streams (to avoid linking crc code) + 18-19: 0 (reserved) + + Operation variations (changes in library functionality): + 20: PKZIP_BUG_WORKAROUND -- slightly more permissive inflate + 21: FASTEST -- deflate algorithm with only one, lowest compression level + 22,23: 0 (reserved) + + The sprintf variant used by gzprintf (zero is best): + 24: 0 = vs*, 1 = s* -- 1 means limited to 20 arguments after the format + 25: 0 = *nprintf, 1 = *printf -- 1 means gzprintf() not secure! + 26: 0 = returns value, 1 = void -- 1 means inferred string length returned + + Remainder: + 27-31: 0 (reserved) + */ + +#ifndef Z_SOLO + + /* utility functions */ + +/* + The following utility functions are implemented on top of the basic + stream-oriented functions. To simplify the interface, some default options + are assumed (compression level and memory usage, standard memory allocation + functions). The source code of these utility functions can be modified if + you need special options. +*/ + +ZEXTERN int ZEXPORT compress OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen)); +/* + Compresses the source buffer into the destination buffer. sourceLen is + the byte length of the source buffer. Upon entry, destLen is the total size + of the destination buffer, which must be at least the value returned by + compressBound(sourceLen). Upon exit, destLen is the actual size of the + compressed buffer. + + compress returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_BUF_ERROR if there was not enough room in the output + buffer. +*/ + +ZEXTERN int ZEXPORT compress2 OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen, + int level)); +/* + Compresses the source buffer into the destination buffer. The level + parameter has the same meaning as in deflateInit. sourceLen is the byte + length of the source buffer. Upon entry, destLen is the total size of the + destination buffer, which must be at least the value returned by + compressBound(sourceLen). Upon exit, destLen is the actual size of the + compressed buffer. + + compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_BUF_ERROR if there was not enough room in the output buffer, + Z_STREAM_ERROR if the level parameter is invalid. +*/ + +ZEXTERN uLong ZEXPORT compressBound OF((uLong sourceLen)); +/* + compressBound() returns an upper bound on the compressed size after + compress() or compress2() on sourceLen bytes. It would be used before a + compress() or compress2() call to allocate the destination buffer. +*/ + +ZEXTERN int ZEXPORT uncompress OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen)); +/* + Decompresses the source buffer into the destination buffer. sourceLen is + the byte length of the source buffer. Upon entry, destLen is the total size + of the destination buffer, which must be large enough to hold the entire + uncompressed data. (The size of the uncompressed data must have been saved + previously by the compressor and transmitted to the decompressor by some + mechanism outside the scope of this compression library.) Upon exit, destLen + is the actual size of the uncompressed buffer. + + uncompress returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_BUF_ERROR if there was not enough room in the output + buffer, or Z_DATA_ERROR if the input data was corrupted or incomplete. In + the case where there is not enough room, uncompress() will fill the output + buffer with the uncompressed data up to that point. +*/ + + /* gzip file access functions */ + +/* + This library supports reading and writing files in gzip (.gz) format with + an interface similar to that of stdio, using the functions that start with + "gz". The gzip format is different from the zlib format. gzip is a gzip + wrapper, documented in RFC 1952, wrapped around a deflate stream. +*/ + +typedef struct gzFile_s *gzFile; /* semi-opaque gzip file descriptor */ + +/* +ZEXTERN gzFile ZEXPORT gzopen OF((const char *path, const char *mode)); + + Opens a gzip (.gz) file for reading or writing. The mode parameter is as + in fopen ("rb" or "wb") but can also include a compression level ("wb9") or + a strategy: 'f' for filtered data as in "wb6f", 'h' for Huffman-only + compression as in "wb1h", 'R' for run-length encoding as in "wb1R", or 'F' + for fixed code compression as in "wb9F". (See the description of + deflateInit2 for more information about the strategy parameter.) 'T' will + request transparent writing or appending with no compression and not using + the gzip format. + + "a" can be used instead of "w" to request that the gzip stream that will + be written be appended to the file. "+" will result in an error, since + reading and writing to the same gzip file is not supported. The addition of + "x" when writing will create the file exclusively, which fails if the file + already exists. On systems that support it, the addition of "e" when + reading or writing will set the flag to close the file on an execve() call. + + These functions, as well as gzip, will read and decode a sequence of gzip + streams in a file. The append function of gzopen() can be used to create + such a file. (Also see gzflush() for another way to do this.) When + appending, gzopen does not test whether the file begins with a gzip stream, + nor does it look for the end of the gzip streams to begin appending. gzopen + will simply append a gzip stream to the existing file. + + gzopen can be used to read a file which is not in gzip format; in this + case gzread will directly read from the file without decompression. When + reading, this will be detected automatically by looking for the magic two- + byte gzip header. + + gzopen returns NULL if the file could not be opened, if there was + insufficient memory to allocate the gzFile state, or if an invalid mode was + specified (an 'r', 'w', or 'a' was not provided, or '+' was provided). + errno can be checked to determine if the reason gzopen failed was that the + file could not be opened. +*/ + +ZEXTERN gzFile ZEXPORT gzdopen OF((int fd, const char *mode)); +/* + gzdopen associates a gzFile with the file descriptor fd. File descriptors + are obtained from calls like open, dup, creat, pipe or fileno (if the file + has been previously opened with fopen). The mode parameter is as in gzopen. + + The next call of gzclose on the returned gzFile will also close the file + descriptor fd, just like fclose(fdopen(fd, mode)) closes the file descriptor + fd. If you want to keep fd open, use fd = dup(fd_keep); gz = gzdopen(fd, + mode);. The duplicated descriptor should be saved to avoid a leak, since + gzdopen does not close fd if it fails. If you are using fileno() to get the + file descriptor from a FILE *, then you will have to use dup() to avoid + double-close()ing the file descriptor. Both gzclose() and fclose() will + close the associated file descriptor, so they need to have different file + descriptors. + + gzdopen returns NULL if there was insufficient memory to allocate the + gzFile state, if an invalid mode was specified (an 'r', 'w', or 'a' was not + provided, or '+' was provided), or if fd is -1. The file descriptor is not + used until the next gz* read, write, seek, or close operation, so gzdopen + will not detect if fd is invalid (unless fd is -1). +*/ + +ZEXTERN int ZEXPORT gzbuffer OF((gzFile file, unsigned size)); +/* + Set the internal buffer size used by this library's functions. The + default buffer size is 8192 bytes. This function must be called after + gzopen() or gzdopen(), and before any other calls that read or write the + file. The buffer memory allocation is always deferred to the first read or + write. Two buffers are allocated, either both of the specified size when + writing, or one of the specified size and the other twice that size when + reading. A larger buffer size of, for example, 64K or 128K bytes will + noticeably increase the speed of decompression (reading). + + The new buffer size also affects the maximum length for gzprintf(). + + gzbuffer() returns 0 on success, or -1 on failure, such as being called + too late. +*/ + +ZEXTERN int ZEXPORT gzsetparams OF((gzFile file, int level, int strategy)); +/* + Dynamically update the compression level or strategy. See the description + of deflateInit2 for the meaning of these parameters. + + gzsetparams returns Z_OK if success, or Z_STREAM_ERROR if the file was not + opened for writing. +*/ + +ZEXTERN int ZEXPORT gzread OF((gzFile file, voidp buf, unsigned len)); +/* + Reads the given number of uncompressed bytes from the compressed file. If + the input file is not in gzip format, gzread copies the given number of + bytes into the buffer directly from the file. + + After reaching the end of a gzip stream in the input, gzread will continue + to read, looking for another gzip stream. Any number of gzip streams may be + concatenated in the input file, and will all be decompressed by gzread(). + If something other than a gzip stream is encountered after a gzip stream, + that remaining trailing garbage is ignored (and no error is returned). + + gzread can be used to read a gzip file that is being concurrently written. + Upon reaching the end of the input, gzread will return with the available + data. If the error code returned by gzerror is Z_OK or Z_BUF_ERROR, then + gzclearerr can be used to clear the end of file indicator in order to permit + gzread to be tried again. Z_OK indicates that a gzip stream was completed + on the last gzread. Z_BUF_ERROR indicates that the input file ended in the + middle of a gzip stream. Note that gzread does not return -1 in the event + of an incomplete gzip stream. This error is deferred until gzclose(), which + will return Z_BUF_ERROR if the last gzread ended in the middle of a gzip + stream. Alternatively, gzerror can be used before gzclose to detect this + case. + + gzread returns the number of uncompressed bytes actually read, less than + len for end of file, or -1 for error. +*/ + +ZEXTERN int ZEXPORT gzwrite OF((gzFile file, + voidpc buf, unsigned len)); +/* + Writes the given number of uncompressed bytes into the compressed file. + gzwrite returns the number of uncompressed bytes written or 0 in case of + error. +*/ + +ZEXTERN int ZEXPORTVA gzprintf Z_ARG((gzFile file, const char *format, ...)); +/* + Converts, formats, and writes the arguments to the compressed file under + control of the format string, as in fprintf. gzprintf returns the number of + uncompressed bytes actually written, or 0 in case of error. The number of + uncompressed bytes written is limited to 8191, or one less than the buffer + size given to gzbuffer(). The caller should assure that this limit is not + exceeded. If it is exceeded, then gzprintf() will return an error (0) with + nothing written. In this case, there may also be a buffer overflow with + unpredictable consequences, which is possible only if zlib was compiled with + the insecure functions sprintf() or vsprintf() because the secure snprintf() + or vsnprintf() functions were not available. This can be determined using + zlibCompileFlags(). +*/ + +ZEXTERN int ZEXPORT gzputs OF((gzFile file, const char *s)); +/* + Writes the given null-terminated string to the compressed file, excluding + the terminating null character. + + gzputs returns the number of characters written, or -1 in case of error. +*/ + +ZEXTERN char * ZEXPORT gzgets OF((gzFile file, char *buf, int len)); +/* + Reads bytes from the compressed file until len-1 characters are read, or a + newline character is read and transferred to buf, or an end-of-file + condition is encountered. If any characters are read or if len == 1, the + string is terminated with a null character. If no characters are read due + to an end-of-file or len < 1, then the buffer is left untouched. + + gzgets returns buf which is a null-terminated string, or it returns NULL + for end-of-file or in case of error. If there was an error, the contents at + buf are indeterminate. +*/ + +ZEXTERN int ZEXPORT gzputc OF((gzFile file, int c)); +/* + Writes c, converted to an unsigned char, into the compressed file. gzputc + returns the value that was written, or -1 in case of error. +*/ + +ZEXTERN int ZEXPORT gzgetc OF((gzFile file)); +/* + Reads one byte from the compressed file. gzgetc returns this byte or -1 + in case of end of file or error. This is implemented as a macro for speed. + As such, it does not do all of the checking the other functions do. I.e. + it does not check to see if file is NULL, nor whether the structure file + points to has been clobbered or not. +*/ + +ZEXTERN int ZEXPORT gzungetc OF((int c, gzFile file)); +/* + Push one character back onto the stream to be read as the first character + on the next read. At least one character of push-back is allowed. + gzungetc() returns the character pushed, or -1 on failure. gzungetc() will + fail if c is -1, and may fail if a character has been pushed but not read + yet. If gzungetc is used immediately after gzopen or gzdopen, at least the + output buffer size of pushed characters is allowed. (See gzbuffer above.) + The pushed character will be discarded if the stream is repositioned with + gzseek() or gzrewind(). +*/ + +ZEXTERN int ZEXPORT gzflush OF((gzFile file, int flush)); +/* + Flushes all pending output into the compressed file. The parameter flush + is as in the deflate() function. The return value is the zlib error number + (see function gzerror below). gzflush is only permitted when writing. + + If the flush parameter is Z_FINISH, the remaining data is written and the + gzip stream is completed in the output. If gzwrite() is called again, a new + gzip stream will be started in the output. gzread() is able to read such + concatented gzip streams. + + gzflush should be called only when strictly necessary because it will + degrade compression if called too often. +*/ + +/* +ZEXTERN z_off_t ZEXPORT gzseek OF((gzFile file, + z_off_t offset, int whence)); + + Sets the starting position for the next gzread or gzwrite on the given + compressed file. The offset represents a number of bytes in the + uncompressed data stream. The whence parameter is defined as in lseek(2); + the value SEEK_END is not supported. + + If the file is opened for reading, this function is emulated but can be + extremely slow. If the file is opened for writing, only forward seeks are + supported; gzseek then compresses a sequence of zeroes up to the new + starting position. + + gzseek returns the resulting offset location as measured in bytes from + the beginning of the uncompressed stream, or -1 in case of error, in + particular if the file is opened for writing and the new starting position + would be before the current position. +*/ + +ZEXTERN int ZEXPORT gzrewind OF((gzFile file)); +/* + Rewinds the given file. This function is supported only for reading. + + gzrewind(file) is equivalent to (int)gzseek(file, 0L, SEEK_SET) +*/ + +/* +ZEXTERN z_off_t ZEXPORT gztell OF((gzFile file)); + + Returns the starting position for the next gzread or gzwrite on the given + compressed file. This position represents a number of bytes in the + uncompressed data stream, and is zero when starting, even if appending or + reading a gzip stream from the middle of a file using gzdopen(). + + gztell(file) is equivalent to gzseek(file, 0L, SEEK_CUR) +*/ + +/* +ZEXTERN z_off_t ZEXPORT gzoffset OF((gzFile file)); + + Returns the current offset in the file being read or written. This offset + includes the count of bytes that precede the gzip stream, for example when + appending or when using gzdopen() for reading. When reading, the offset + does not include as yet unused buffered input. This information can be used + for a progress indicator. On error, gzoffset() returns -1. +*/ + +ZEXTERN int ZEXPORT gzeof OF((gzFile file)); +/* + Returns true (1) if the end-of-file indicator has been set while reading, + false (0) otherwise. Note that the end-of-file indicator is set only if the + read tried to go past the end of the input, but came up short. Therefore, + just like feof(), gzeof() may return false even if there is no more data to + read, in the event that the last read request was for the exact number of + bytes remaining in the input file. This will happen if the input file size + is an exact multiple of the buffer size. + + If gzeof() returns true, then the read functions will return no more data, + unless the end-of-file indicator is reset by gzclearerr() and the input file + has grown since the previous end of file was detected. +*/ + +ZEXTERN int ZEXPORT gzdirect OF((gzFile file)); +/* + Returns true (1) if file is being copied directly while reading, or false + (0) if file is a gzip stream being decompressed. + + If the input file is empty, gzdirect() will return true, since the input + does not contain a gzip stream. + + If gzdirect() is used immediately after gzopen() or gzdopen() it will + cause buffers to be allocated to allow reading the file to determine if it + is a gzip file. Therefore if gzbuffer() is used, it should be called before + gzdirect(). + + When writing, gzdirect() returns true (1) if transparent writing was + requested ("wT" for the gzopen() mode), or false (0) otherwise. (Note: + gzdirect() is not needed when writing. Transparent writing must be + explicitly requested, so the application already knows the answer. When + linking statically, using gzdirect() will include all of the zlib code for + gzip file reading and decompression, which may not be desired.) +*/ + +ZEXTERN int ZEXPORT gzclose OF((gzFile file)); +/* + Flushes all pending output if necessary, closes the compressed file and + deallocates the (de)compression state. Note that once file is closed, you + cannot call gzerror with file, since its structures have been deallocated. + gzclose must not be called more than once on the same file, just as free + must not be called more than once on the same allocation. + + gzclose will return Z_STREAM_ERROR if file is not valid, Z_ERRNO on a + file operation error, Z_MEM_ERROR if out of memory, Z_BUF_ERROR if the + last read ended in the middle of a gzip stream, or Z_OK on success. +*/ + +ZEXTERN int ZEXPORT gzclose_r OF((gzFile file)); +ZEXTERN int ZEXPORT gzclose_w OF((gzFile file)); +/* + Same as gzclose(), but gzclose_r() is only for use when reading, and + gzclose_w() is only for use when writing or appending. The advantage to + using these instead of gzclose() is that they avoid linking in zlib + compression or decompression code that is not used when only reading or only + writing respectively. If gzclose() is used, then both compression and + decompression code will be included the application when linking to a static + zlib library. +*/ + +ZEXTERN const char * ZEXPORT gzerror OF((gzFile file, int *errnum)); +/* + Returns the error message for the last error which occurred on the given + compressed file. errnum is set to zlib error number. If an error occurred + in the file system and not in the compression library, errnum is set to + Z_ERRNO and the application may consult errno to get the exact error code. + + The application must not modify the returned string. Future calls to + this function may invalidate the previously returned string. If file is + closed, then the string previously returned by gzerror will no longer be + available. + + gzerror() should be used to distinguish errors from end-of-file for those + functions above that do not distinguish those cases in their return values. +*/ + +ZEXTERN void ZEXPORT gzclearerr OF((gzFile file)); +/* + Clears the error and end-of-file flags for file. This is analogous to the + clearerr() function in stdio. This is useful for continuing to read a gzip + file that is being written concurrently. +*/ + +#endif /* !Z_SOLO */ + + /* checksum functions */ + +/* + These functions are not related to compression but are exported + anyway because they might be useful in applications using the compression + library. +*/ + +ZEXTERN uLong ZEXPORT adler32 OF((uLong adler, const Bytef *buf, uInt len)); +/* + Update a running Adler-32 checksum with the bytes buf[0..len-1] and + return the updated checksum. If buf is Z_NULL, this function returns the + required initial value for the checksum. + + An Adler-32 checksum is almost as reliable as a CRC32 but can be computed + much faster. + + Usage example: + + uLong adler = adler32(0L, Z_NULL, 0); + + while (read_buffer(buffer, length) != EOF) { + adler = adler32(adler, buffer, length); + } + if (adler != original_adler) error(); +*/ + +/* +ZEXTERN uLong ZEXPORT adler32_combine OF((uLong adler1, uLong adler2, + z_off_t len2)); + + Combine two Adler-32 checksums into one. For two sequences of bytes, seq1 + and seq2 with lengths len1 and len2, Adler-32 checksums were calculated for + each, adler1 and adler2. adler32_combine() returns the Adler-32 checksum of + seq1 and seq2 concatenated, requiring only adler1, adler2, and len2. Note + that the z_off_t type (like off_t) is a signed integer. If len2 is + negative, the result has no meaning or utility. +*/ + +ZEXTERN uLong ZEXPORT crc32 OF((uLong crc, const Bytef *buf, uInt len)); +/* + Update a running CRC-32 with the bytes buf[0..len-1] and return the + updated CRC-32. If buf is Z_NULL, this function returns the required + initial value for the crc. Pre- and post-conditioning (one's complement) is + performed within this function so it shouldn't be done by the application. + + Usage example: + + uLong crc = crc32(0L, Z_NULL, 0); + + while (read_buffer(buffer, length) != EOF) { + crc = crc32(crc, buffer, length); + } + if (crc != original_crc) error(); +*/ + +/* +ZEXTERN uLong ZEXPORT crc32_combine OF((uLong crc1, uLong crc2, z_off_t len2)); + + Combine two CRC-32 check values into one. For two sequences of bytes, + seq1 and seq2 with lengths len1 and len2, CRC-32 check values were + calculated for each, crc1 and crc2. crc32_combine() returns the CRC-32 + check value of seq1 and seq2 concatenated, requiring only crc1, crc2, and + len2. +*/ + + + /* various hacks, don't look :) */ + +/* deflateInit and inflateInit are macros to allow checking the zlib version + * and the compiler's view of z_stream: + */ +ZEXTERN int ZEXPORT deflateInit_ OF((z_streamp strm, int level, + const char *version, int stream_size)); +ZEXTERN int ZEXPORT inflateInit_ OF((z_streamp strm, + const char *version, int stream_size)); +ZEXTERN int ZEXPORT deflateInit2_ OF((z_streamp strm, int level, int method, + int windowBits, int memLevel, + int strategy, const char *version, + int stream_size)); +ZEXTERN int ZEXPORT inflateInit2_ OF((z_streamp strm, int windowBits, + const char *version, int stream_size)); +ZEXTERN int ZEXPORT inflateBackInit_ OF((z_streamp strm, int windowBits, + unsigned char FAR *window, + const char *version, + int stream_size)); +#define deflateInit(strm, level) \ + deflateInit_((strm), (level), ZLIB_VERSION, (int)sizeof(z_stream)) +#define inflateInit(strm) \ + inflateInit_((strm), ZLIB_VERSION, (int)sizeof(z_stream)) +#define deflateInit2(strm, level, method, windowBits, memLevel, strategy) \ + deflateInit2_((strm),(level),(method),(windowBits),(memLevel),\ + (strategy), ZLIB_VERSION, (int)sizeof(z_stream)) +#define inflateInit2(strm, windowBits) \ + inflateInit2_((strm), (windowBits), ZLIB_VERSION, \ + (int)sizeof(z_stream)) +#define inflateBackInit(strm, windowBits, window) \ + inflateBackInit_((strm), (windowBits), (window), \ + ZLIB_VERSION, (int)sizeof(z_stream)) + +#ifndef Z_SOLO + +/* gzgetc() macro and its supporting function and exposed data structure. Note + * that the real internal state is much larger than the exposed structure. + * This abbreviated structure exposes just enough for the gzgetc() macro. The + * user should not mess with these exposed elements, since their names or + * behavior could change in the future, perhaps even capriciously. They can + * only be used by the gzgetc() macro. You have been warned. + */ +struct gzFile_s { + unsigned have; + unsigned char *next; + z_off64_t pos; +}; +ZEXTERN int ZEXPORT gzgetc_ OF((gzFile file)); /* backward compatibility */ +#ifdef Z_PREFIX_SET +# undef z_gzgetc +# define z_gzgetc(g) \ + ((g)->have ? ((g)->have--, (g)->pos++, *((g)->next)++) : gzgetc(g)) +#else +# define gzgetc(g) \ + ((g)->have ? ((g)->have--, (g)->pos++, *((g)->next)++) : gzgetc(g)) +#endif + +/* provide 64-bit offset functions if _LARGEFILE64_SOURCE defined, and/or + * change the regular functions to 64 bits if _FILE_OFFSET_BITS is 64 (if + * both are true, the application gets the *64 functions, and the regular + * functions are changed to 64 bits) -- in case these are set on systems + * without large file support, _LFS64_LARGEFILE must also be true + */ +#ifdef Z_LARGE64 + ZEXTERN gzFile ZEXPORT gzopen64 OF((const char *, const char *)); + ZEXTERN z_off64_t ZEXPORT gzseek64 OF((gzFile, z_off64_t, int)); + ZEXTERN z_off64_t ZEXPORT gztell64 OF((gzFile)); + ZEXTERN z_off64_t ZEXPORT gzoffset64 OF((gzFile)); + ZEXTERN uLong ZEXPORT adler32_combine64 OF((uLong, uLong, z_off64_t)); + ZEXTERN uLong ZEXPORT crc32_combine64 OF((uLong, uLong, z_off64_t)); +#endif + +#if !defined(ZLIB_INTERNAL) && defined(Z_WANT64) +# ifdef Z_PREFIX_SET +# define z_gzopen z_gzopen64 +# define z_gzseek z_gzseek64 +# define z_gztell z_gztell64 +# define z_gzoffset z_gzoffset64 +# define z_adler32_combine z_adler32_combine64 +# define z_crc32_combine z_crc32_combine64 +# else +# define gzopen gzopen64 +# define gzseek gzseek64 +# define gztell gztell64 +# define gzoffset gzoffset64 +# define adler32_combine adler32_combine64 +# define crc32_combine crc32_combine64 +# endif +# ifndef Z_LARGE64 + ZEXTERN gzFile ZEXPORT gzopen64 OF((const char *, const char *)); + ZEXTERN z_off_t ZEXPORT gzseek64 OF((gzFile, z_off_t, int)); + ZEXTERN z_off_t ZEXPORT gztell64 OF((gzFile)); + ZEXTERN z_off_t ZEXPORT gzoffset64 OF((gzFile)); + ZEXTERN uLong ZEXPORT adler32_combine64 OF((uLong, uLong, z_off_t)); + ZEXTERN uLong ZEXPORT crc32_combine64 OF((uLong, uLong, z_off_t)); +# endif +#else + ZEXTERN gzFile ZEXPORT gzopen OF((const char *, const char *)); + ZEXTERN z_off_t ZEXPORT gzseek OF((gzFile, z_off_t, int)); + ZEXTERN z_off_t ZEXPORT gztell OF((gzFile)); + ZEXTERN z_off_t ZEXPORT gzoffset OF((gzFile)); + ZEXTERN uLong ZEXPORT adler32_combine OF((uLong, uLong, z_off_t)); + ZEXTERN uLong ZEXPORT crc32_combine OF((uLong, uLong, z_off_t)); +#endif + +#else /* Z_SOLO */ + + ZEXTERN uLong ZEXPORT adler32_combine OF((uLong, uLong, z_off_t)); + ZEXTERN uLong ZEXPORT crc32_combine OF((uLong, uLong, z_off_t)); + +#endif /* !Z_SOLO */ + +/* hack for buggy compilers */ +#if !defined(ZUTIL_H) && !defined(NO_DUMMY_DECL) + struct internal_state {int dummy;}; +#endif + +/* undocumented functions */ +ZEXTERN const char * ZEXPORT zError OF((int)); +ZEXTERN int ZEXPORT inflateSyncPoint OF((z_streamp)); +ZEXTERN const z_crc_t FAR * ZEXPORT get_crc_table OF((void)); +ZEXTERN int ZEXPORT inflateUndermine OF((z_streamp, int)); +ZEXTERN int ZEXPORT inflateResetKeep OF((z_streamp)); +ZEXTERN int ZEXPORT deflateResetKeep OF((z_streamp)); +#if defined(_WIN32) && !defined(Z_SOLO) +ZEXTERN gzFile ZEXPORT gzopen_w OF((const wchar_t *path, + const char *mode)); +#endif +#if defined(STDC) || defined(Z_HAVE_STDARG_H) +# ifndef Z_SOLO +ZEXTERN int ZEXPORTVA gzvprintf Z_ARG((gzFile file, + const char *format, + va_list va)); +# endif +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* ZLIB_H */ diff --git a/platform/mcu/xr871/src/cedarx/muxer/Makefile.am b/platform/mcu/xr871/src/cedarx/muxer/Makefile.am new file mode 100644 index 0000000000..7a36b24f19 --- /dev/null +++ b/platform/mcu/xr871/src/cedarx/muxer/Makefile.am @@ -0,0 +1,2 @@ + +SUBDIRS = aac mp4 ts base diff --git a/platform/mcu/xr871/src/cedarx/muxer/RecoderCbWriter.h b/platform/mcu/xr871/src/cedarx/muxer/RecoderCbWriter.h new file mode 100644 index 0000000000..c96eb0efc5 --- /dev/null +++ b/platform/mcu/xr871/src/cedarx/muxer/RecoderCbWriter.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2008-2016 Allwinner Technology Co. Ltd. + * All rights reserved. + * + * File : RecoderWriter.h + * Description : RecoderWriter + * History : + * + */ + + +#ifndef __RECODER_CBWRITER_H__ +#define __RECODER_CBWRITER_H__ + +#include "CdxWriter.h" + +typedef uint32_t (*CdxWriterCb)(void *buf, uint32_t size); + +typedef struct RecoderCbWriter RecoderCbWriterT; +struct RecoderCbWriter +{ + CdxWriterT cdx_writer; + int file_mode; + int fd_file; + CdxWriterCb cb; +}; + +typedef struct WriterDataDest { + char *pUrl; + void *pArg; +} WriterDataDest; + +CdxWriterT *CdxCbWriterCreat(WriterDataDest *dataDest); +void CdxCbWriterDestroy(CdxWriterT *writer); + +#endif diff --git a/platform/mcu/xr871/src/cedarx/muxer/RecoderWriter.h b/platform/mcu/xr871/src/cedarx/muxer/RecoderWriter.h new file mode 100644 index 0000000000..69b20bb809 --- /dev/null +++ b/platform/mcu/xr871/src/cedarx/muxer/RecoderWriter.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2008-2016 Allwinner Technology Co. Ltd. + * All rights reserved. + * + * File : RecoderWriter.h + * Description : RecoderWriter + * History : + * + */ + + +#ifndef __RECODER_WRITER_H__ +#define __RECODER_WRITER_H__ + +#include "CdxWriter.h" +#include "cedarx_fs.h" + +typedef enum CDX_FILE_MODE +{ +#if FD_SUPPORT + FD_FILE_MODE, +#endif + FP_FILE_MODE +}CDX_FILE_MODE; + +typedef struct RecoderWriter RecoderWriterT; +struct RecoderWriter +{ + CdxWriterT cdx_writer; + int file_mode; + int fd_file; + FILE *fp_file; + char *file_path; +}; + +int RWOpen(CdxWriterT *writer); +int RWClose(CdxWriterT *writer); +CdxWriterT *CdxWriterCreat(char* pUrl); + +#endif diff --git a/platform/mcu/xr871/src/cedarx/muxer/aac/CdxAacMuxer.h b/platform/mcu/xr871/src/cedarx/muxer/aac/CdxAacMuxer.h new file mode 100644 index 0000000000..3222c57994 --- /dev/null +++ b/platform/mcu/xr871/src/cedarx/muxer/aac/CdxAacMuxer.h @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2008-2016 Allwinner Technology Co. Ltd. + * All rights reserved. + * + * File : CdxAacMuxer.h + * Description : Allwinner AAC Muxer Definition + * History : + * + */ + +#ifndef CDX_AAC_MUXER_H +#define CDX_AAC_MUXER_H + +#include +#include "CdxMuxer.h" +#include "CdxMuxerBaseDef.h" + +#endif /* CDX_AAC_MUXER_H */ diff --git a/platform/mcu/xr871/src/cedarx/muxer/aac/Makefile.am b/platform/mcu/xr871/src/cedarx/muxer/aac/Makefile.am new file mode 100644 index 0000000000..c14686d636 --- /dev/null +++ b/platform/mcu/xr871/src/cedarx/muxer/aac/Makefile.am @@ -0,0 +1,27 @@ + +include $(top_srcdir)/Makefile.inc + +noinst_LTLIBRARIES = libcdx_aac_muxer.la + +## set the source files. +libcdx_aac_muxer_la_SOURCES = CdxAacMuxer.c + +libcdx_aac_muxer_la_CFLAGS = $(CFLAGS_CDXG) +LOCAL_INCLUDE = -I../include \ + -I../../include \ + -I../../base/include \ + -I../base \ + -I$(top_srcdir)/external/include/aencoder \ + -I$(top_srcdir)/../libcedarc/include \ + -I$(top_srcdir)/ + +libcdx_aac_muxer_la_CFLAGS += $(LOCAL_INCLUDE) + + +libcdx_aac_muxer_la_LDFLAGS = $(LDFLAGS_CDXG) + +libcdx_aac_muxer_la_LIBADD = $(top_srcdir)/libcore/base/libcdx_base.la + +#LOCAL_LIB = -lpthread +libcdx_aac_muxer_la_LDFLAGS += $(LOCAL_LIB) + diff --git a/platform/mcu/xr871/src/cedarx/muxer/base/CdxMuxerBaseDef.h b/platform/mcu/xr871/src/cedarx/muxer/base/CdxMuxerBaseDef.h new file mode 100644 index 0000000000..283317273f --- /dev/null +++ b/platform/mcu/xr871/src/cedarx/muxer/base/CdxMuxerBaseDef.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2008-2016 Allwinner Technology Co. Ltd. + * All rights reserved. + * + * File : CdxMuxerBaseDef.h + * Description : Allwinner Muxer Base Definition + * History : + * + */ + +#ifndef CDX_MUXER_BASE_DEF_H +#define CDX_MUXER_BASE_DEF_H + +typedef enum { + CODEC_TYPE_UNKNOWN = -1, + CODEC_TYPE_VIDEO = 0, //don't change it! + CODEC_TYPE_AUDIO = 1, //don't change it! + CODEC_TYPE_SUBTITLE, + CODEC_TYPE_DATA +}CodecType; + +typedef enum { + MUX_CODEC_ID_MPEG4 = 32, + MUX_CODEC_ID_H264 = 33, + MUX_CODEC_ID_MJPEG = 108, + MUX_CODEC_ID_AAC = 64, + MUX_CODEC_ID_MP3 = 105, + MUX_CODEC_ID_PCM, // 16-bit little-endian, twos-complement + MUX_CODEC_ID_ADPCM, // DVI/Intel IMAADPCM-ACM code 17 +}MuxCodecID; + +#endif /* CDX_MUXER_BASE_DEF_H */ diff --git a/platform/mcu/xr871/src/cedarx/muxer/base/CdxTsemaphore.h b/platform/mcu/xr871/src/cedarx/muxer/base/CdxTsemaphore.h new file mode 100644 index 0000000000..49e59266b3 --- /dev/null +++ b/platform/mcu/xr871/src/cedarx/muxer/base/CdxTsemaphore.h @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2008-2016 Allwinner Technology Co. Ltd. + * All rights reserved. + * + * File : CdxTsemaphore.h + * Description : Allwinner Muxer Writer with Thread Operation + * History : + * Author : Q Wang + * Date : 2014/12/17 + * Comment : ʼ汾V3 CedarX_1.0 + * + * Author : GJ Wu + * Date : 2016/04/18 + * Comment_: ޸ֲCedarX_2.0 + */ + +#ifndef __CDX_TSEMAPHORE_H__ +#define __CDX_TSEMAPHORE_H__ + +#include + +typedef struct CdxSem +{ + pthread_cond_t condition; + pthread_mutex_t mutex; + unsigned int semval; +}CdxSemT; + +int CdxSemInit(CdxSemT *tsem, unsigned int val); +void CdxSemDeinit(CdxSemT *tsem); +void CdxSemDown(CdxSemT *tsem); +void CdxSemUp(CdxSemT *tsem); +void CdxSemUpUnique(CdxSemT *tsem); +void CdxSemReset(CdxSemT *tsem); +void CdxSemWait(CdxSemT *tsem); +void CdxSemSignal(CdxSemT *tsem); +unsigned int CdxSemGetVal(CdxSemT *tsem); + + +#define cdx_mutex_lock(x) pthread_mutex_lock(x) +#define cdx_mutex_unlock(x) pthread_mutex_unlock(x) + +#define cdx_cond_wait_while_exp(tsem, expression) \ + pthread_mutex_lock(&tsem.mutex); \ + while (expression) { \ + pthread_cond_wait(&tsem.condition, &tsem.mutex); \ + } \ + pthread_mutex_unlock(&tsem.mutex); + +#define cdx_cond_wait_if_exp(tsem, expression) \ + pthread_mutex_lock(&tsem.mutex); \ + if (expression) { \ + pthread_cond_wait(&tsem.condition, &tsem.mutex); \ + } \ + pthread_mutex_unlock(&tsem.mutex); + +#endif + diff --git a/platform/mcu/xr871/src/cedarx/muxer/base/Makefile.am b/platform/mcu/xr871/src/cedarx/muxer/base/Makefile.am new file mode 100644 index 0000000000..55a994ce79 --- /dev/null +++ b/platform/mcu/xr871/src/cedarx/muxer/base/Makefile.am @@ -0,0 +1,38 @@ +include $(top_srcdir)/Makefile.inc + +lib_LTLIBRARIES = libcdx_muxer.la + +## set the source files. +libcdx_muxer_la_SOURCES = CdxFsCache.c \ + CdxFsSimpleCache.c \ + CdxFsWriteDirect.c \ + CdxFsWriter.c \ + CdxMuxer.c \ + CdxTsemaphore.c + +libcdx_muxer_la_CFLAGS = $(CFLAGS_CDXG) +LOCAL_INCLUDE = -I../include \ + -I../../include \ + -I../../base/include \ + -I../base \ + -I$(top_srcdir)/external/include/aencoder \ + -I$(top_srcdir)/../libcedarc/include \ + -I$(top_srcdir)/ + +libcdx_muxer_la_CFLAGS += $(LOCAL_INCLUDE) + + +libcdx_muxer_la_LDFLAGS = $(LDFLAGS_CDXG) + +libcdx_muxer_la_LIBADD = $(top_srcdir)/libcore/muxer/aac/libcdx_aac_muxer.la \ + $(top_srcdir)/libcore/muxer/mp4/libcdx_mp4_muxer.la \ + $(top_srcdir)/libcore/muxer/ts/libcdx_ts_muxer.la \ + $(top_srcdir)/libcore/base/libcdx_base.la + +# $(top_srcdir)/libcore/muxer/mp4ex/libcdx_mp4ex_muxer.la + +LOCAL_LIB = -lpthread -laencoder -lcdc_vencoder +libcdx_muxer_la_LDFLAGS += $(LOCAL_LIB) + + + diff --git a/platform/mcu/xr871/src/cedarx/muxer/include/CdxMuxer.h b/platform/mcu/xr871/src/cedarx/muxer/include/CdxMuxer.h new file mode 100644 index 0000000000..b62493a1b5 --- /dev/null +++ b/platform/mcu/xr871/src/cedarx/muxer/include/CdxMuxer.h @@ -0,0 +1,217 @@ +/* + * Copyright (c) 2008-2016 Allwinner Technology Co. Ltd. + * All rights reserved. + * + * File : CdxMuxer.h + * Description : Allwinner Muxer Definition + * History : + * + */ + +#ifndef CDX_MUXER_H +#define CDX_MUXER_H + +#include +#include +//#include +#include +#include +#include +#include + +typedef enum { + UNKNOWN_CMD = 0, + SET_CACHE_MEM, + SET_FS_WRITE_MODE, + SET_FS_SIMPLE_CACHE_SIZE, + SET_MP4_TMP_PATH, + SET_PLAY_TIME_LENGTH, + SET_IS_SDCARD_DISAPPEAR, + GET_IS_SDCARD_DISAPPEAR +} MuxCtrlCommads; + +typedef enum tag_FSWRITEMODE { + FSWRITEMODE_CACHETHREAD = 0, + FSWRITEMODE_SIMPLECACHE, + FSWRITEMODE_DIRECT, +}FSWRITEMODE; + +typedef struct CdxFsCacheMemInfo +{ + cdx_uint8 *mp_cache; + cdx_uint32 m_cache_size; +}CdxFsCacheMemInfo; + +enum CdxMuxerTypeE +{ + CDX_MUXER_UNKNOW = -1, + CDX_MUXER_MOV, + CDX_MUXER_TS, + CDX_MUXER_AVI, + CDX_MUXER_AAC, + CDX_MUXER_MP3, + CDX_MUXER_MOV_EX, + CDX_MUXER_AMR, //add +}; + +#define FS_WRITER (0) + +typedef struct CdxMuxerCreatorS CdxMuxerCreatorT; +typedef struct CdxMuxerS CdxMuxerT; +typedef enum CdxMuxerTypeE CdxMuxerTypeT; +typedef struct CdxMuxerMediaInfoS CdxMuxerMediaInfoT; +typedef struct MuxerVideoStreamInfoS MuxerVideoStreamInfoT; +typedef struct MuxerAudioStreamInfoS MuxerAudioStreamInfoT; +typedef struct CdxMuxerPacketS CdxMuxerPacketT; + +struct CdxMuxerCreatorS +{ + CdxMuxerT *(*create)(CdxWriterT* /* stream */); +}; + +struct CdxMuxerPacketS +{ + void *buf; + cdx_int32 buflen; + cdx_int64 pts; + cdx_int64 duration; + cdx_int32 type; + cdx_int32 length; + cdx_int32 streamIndex; +}; + +struct CdxMuxerOpsS +{ + cdx_int32 (*writeExtraData)(CdxMuxerT *, unsigned char* /* extradata */, + int /* extradataLen */, int idx); + cdx_int32 (*writeHeader)(CdxMuxerT *); + cdx_int32 (*writePacket)(CdxMuxerT *, CdxMuxerPacketT * /* pkt */); + cdx_int32 (*writeTrailer)(CdxMuxerT *); + cdx_int32 (*control)(CdxMuxerT *, int /* uCmd */, void * /* pParam*/); + cdx_int32 (*close)(CdxMuxerT *); + cdx_int32 (*setMediaInfo)(CdxMuxerT *, CdxMuxerMediaInfoT *); +}; + +struct CdxMuxerS +{ + enum CdxMuxerTypeE type; + struct CdxMuxerOpsS *ops; +}; + +#if VIDEO_SUPPORT +struct MuxerVideoStreamInfoS +{ + VENC_CODEC_TYPE eCodeType; + int nWidth; + int nHeight; + int nFrameRate; + int nCreatTime; + int nRotateDegree; +}; +#endif + +#define ADECODER_MAX_LANG_CHAR_SIZE (64) +struct MuxerAudioStreamInfoS +{ + AUDIO_ENCODER_TYPE eCodecFormat; + int nChannelNum; + int nBitsPerSample; + int nSampleCntPerFrame; + int nSampleRate; + int nAvgBitrate; + int nMaxBitRate; + unsigned char strLang[ADECODER_MAX_LANG_CHAR_SIZE]; +}; + +#if VIDEO_SUPPORT +#define MAX_VIDEO_NUM 2 +#endif +#define MAX_AUDIO_NUM 2 +struct CdxMuxerMediaInfoS +{ + int audioNum; +#if VIDEO_SUPPORT + int videoNum; + MuxerVideoStreamInfoT video; +#endif + MuxerAudioStreamInfoT audio; + int geo_available; + int latitudex; + int longitudex; + FSWRITEMODE writer_mode; +}; + +#if FS_WRITER +#define ByteIOContext CdxFsWriter +#else +#define ByteIOContext CdxWriterT +#endif + +#ifdef __cplusplus +extern "C" +{ +#endif + +CdxMuxerT *CdxMuxerCreate(CdxMuxerTypeT type, CdxWriterT *stream); + +static inline int CdxMuxerSetMediaInfo(CdxMuxerT *mux, CdxMuxerMediaInfoT *mediaInfo) +{ + CDX_CHECK(mux); + CDX_CHECK(mux->ops); + CDX_CHECK(mux->ops->setMediaInfo); + return mux->ops->setMediaInfo(mux, mediaInfo); +} + +static inline int CdxMuxerWriteExtraData(CdxMuxerT *mux, unsigned char* pdata, + int data_len, int index) +{ + CDX_CHECK(mux); + CDX_CHECK(mux->ops); + CDX_CHECK(mux->ops->writeHeader); + return mux->ops->writeExtraData(mux, pdata, data_len, index); +} + +static inline int CdxMuxerWriteHeader(CdxMuxerT *mux) +{ + CDX_CHECK(mux); + CDX_CHECK(mux->ops); + CDX_CHECK(mux->ops->writeHeader); + return mux->ops->writeHeader(mux); +} + +static inline int CdxMuxerWritePacket(CdxMuxerT *mux, CdxMuxerPacketT *pkt) +{ + CDX_CHECK(mux); + CDX_CHECK(mux->ops); + CDX_CHECK(mux->ops->writePacket); + return mux->ops->writePacket(mux, pkt); +} + +static inline int CdxMuxerWriteTrailer(CdxMuxerT *mux) +{ + CDX_CHECK(mux); + CDX_CHECK(mux->ops); + CDX_CHECK(mux->ops->writeTrailer); + return mux->ops->writeTrailer(mux); +} + +static inline int CdxMuxerControl(CdxMuxerT *mux, int uCmd, void * pParam) +{ + CDX_CHECK(mux); + CDX_CHECK(mux->ops); + CDX_CHECK(mux->ops->control); + return mux->ops->control(mux, uCmd, pParam); +} + +static inline int CdxMuxerClose(CdxMuxerT *mux) +{ + CDX_CHECK(mux); + CDX_CHECK(mux->ops); + CDX_CHECK(mux->ops->close); + return mux->ops->close(mux); +} + +#ifdef __cplusplus +} +#endif +#endif diff --git a/platform/mcu/xr871/src/cedarx/muxer/include/CdxWriter.h b/platform/mcu/xr871/src/cedarx/muxer/include/CdxWriter.h new file mode 100644 index 0000000000..a1b9339098 --- /dev/null +++ b/platform/mcu/xr871/src/cedarx/muxer/include/CdxWriter.h @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2008-2016 Allwinner Technology Co. Ltd. + * All rights reserved. + * + * File : CdxWriter.h + * Description : Allwinner Muxer Writer Definition + * History : + * + */ + +#ifndef __CDX_WRITER_H__ +#define __CDX_WRITER_H__ + +#include +#include +#include +#include +#include +#include +#include +#include + +typedef struct CdxWriterS CdxWriterT; +struct CdxWriterOps{ + int (*read)(CdxWriterT *thiz, void *buf, int size); + int (*write)(CdxWriterT *thiz, void *buf, int size); + long (*seek)(CdxWriterT *thiz, long moffset, int mwhere); + long (*tell)(CdxWriterT *thiz); + int (*close)(CdxWriterT *thiz); +}; + +struct CdxWriterS +{ + struct CdxWriterOps* ops; + char* uri; +}; + +static inline int CdxWriterRead(CdxWriterT *w, void *buf, int size) +{ + CDX_CHECK(w); + CDX_CHECK(w->ops); + CDX_CHECK(w->ops->read); + return w->ops->read(w, buf, size); +} + +static inline int CdxWriterWrite(CdxWriterT *w, void *buf, int size) +{ + CDX_CHECK(w); + CDX_CHECK(w->ops); + CDX_CHECK(w->ops->write); + return w->ops->write(w, buf, size); +} + +static inline int CdxWriterSeek(CdxWriterT *w, long moffset, int mwhere) +{ + CDX_CHECK(w); + CDX_CHECK(w->ops); + CDX_CHECK(w->ops->seek); + return w->ops->seek(w, moffset, mwhere); +} + +static inline int CdxWriterTell(CdxWriterT *w) +{ + CDX_CHECK(w); + CDX_CHECK(w->ops); + CDX_CHECK(w->ops->tell); + return w->ops->tell(w); +} + +static inline int CdxWriterClose(CdxWriterT *w) +{ + CDX_CHECK(w); + CDX_CHECK(w->ops); + CDX_CHECK(w->ops->close); + return w->ops->close(w); +} + + +//CdxWriterT *CdxWriterCreat(); +void CdxWriterDestroy(CdxWriterT *writer); + +#endif diff --git a/platform/mcu/xr871/src/cedarx/os_glue/include/atomic.h b/platform/mcu/xr871/src/cedarx/os_glue/include/atomic.h new file mode 100644 index 0000000000..797181c40c --- /dev/null +++ b/platform/mcu/xr871/src/cedarx/os_glue/include/atomic.h @@ -0,0 +1,41 @@ +#ifndef _OS_GLUE_ATOMIC +#define _OS_GLUE_ATOMIC +#include +#include "sys/interrupt.h" + +#define ENTER_CRITICAL() arch_irq_disable() + +#define EXIT_CRITICAL() arch_irq_enable() + +#define __sync_fetch_and_add sync_fetch_and_add +#define __sync_fetch_and_sub sync_fetch_and_sub +#define __sync_fetch_and_or sync_fetch_and_or +#define __sync_fetch_and_and sync_fetch_and_and +#define __sync_fetch_and_xor sync_fetch_and_xor +#define __sync_fetch_and_nand sync_fetch_and_nand +#define __sync_add_and_fetch sync_add_and_fetch +#define __sync_sub_and_fetch sync_sub_and_fetch +#define __sync_or_and_fetch sync_or_and_fetch +#define __sync_and_and_fetch sync_and_and_fetch +#define __sync_xor_and_fetch sync_xor_and_fetch +#define __sync_nand_and_fetch sync_nand_and_fetch +#define __sync_lock_test_and_set sync_lock_test_and_set +#define __sync_bool_compare_and_swap sync_bool_compare_and_swap +#define __sync_bool_compare_and_swap sync_bool_compare_and_swap + +signed long sync_fetch_and_add(volatile signed long *ptr, signed long value); +signed long sync_fetch_and_sub(volatile signed long *ptr, signed long value); +signed long sync_fetch_and_or(volatile signed long *ptr, signed long value); +signed long sync_fetch_and_and(volatile signed long *ptr, signed long value); +signed long sync_fetch_and_xor(volatile signed long *ptr, signed long value); +signed long sync_fetch_and_nand(volatile signed long *ptr,signed long value); +signed long sync_add_and_fetch(volatile signed long *ptr, signed long value); +signed long sync_sub_and_fetch(volatile signed long *ptr, signed long value); +signed long sync_or_and_fetch(volatile signed long *ptr, signed long value); +signed long sync_and_and_fetch(volatile signed long *ptr, signed long value); +signed long sync_xor_and_fetch(volatile signed long *ptr, signed long value); +signed long sync_nand_and_fetch(volatile signed long *ptr,signed long value); +signed long sync_lock_test_and_set(volatile signed long *ptr,signed long value); +int sync_bool_compare_and_swap(volatile signed long *ptr,signed long oldvalue, signed long newvalue); + +#endif diff --git a/platform/mcu/xr871/src/cedarx/os_glue/include/cedarx_fs.h b/platform/mcu/xr871/src/cedarx/os_glue/include/cedarx_fs.h new file mode 100644 index 0000000000..fb0ce98cf5 --- /dev/null +++ b/platform/mcu/xr871/src/cedarx/os_glue/include/cedarx_fs.h @@ -0,0 +1,56 @@ +#ifndef _FS_ +#define _FS_ +//#include +//#include +//#include + +#include "cdx_log.h" +#include "fs/fatfs/ff.h" + +//#define O_RDONLY 00000000 +//#define O_WRONLY 00000001 +//#define O_RDWR 00000002 +//#define O_CREAT 00000100 /* not fcntl */ +#ifdef __cplusplus +extern "C" +{ +#endif + +#define FILE FIL + +#define SEEK_SET 0 +#define SEEK_CUR 1 +#define SEEK_END 2 + +FILE * cedarx_fopen(const char * filename, const char * mode); +int cedarx_fseek(FILE * stream, long long offset, int whence); +long long cedarx_ftell(FILE * stream); +int cedarx_fclose(FILE * stream); +int cedarx_fwrite(const void * ptr, int size, int nmemb, FILE * stream); +int cedarx_fread(void * ptr, int size, int nmemb, FILE * stream); + +#define fopen cedarx_fopen +#define fseek cedarx_fseek +#define ftell cedarx_ftell +#define fclose cedarx_fclose +#define fwrite cedarx_fwrite +#define fread cedarx_fread + + +#if 0 +int fputc(int c, ES_FILE *fp); +#endif + +#if FD_SUPPORT +int open(const char *name, int flag, int prems); +int close(int fd); +int read(int fd, void *buf, unsigned count); +int sscanf( const char *, const char *, ...); +signed long long lseek(int fd, signed long long offset, int whence); +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/platform/mcu/xr871/src/cedarx/os_glue/include/list.h b/platform/mcu/xr871/src/cedarx/os_glue/include/list.h new file mode 100644 index 0000000000..b8d2b47f60 --- /dev/null +++ b/platform/mcu/xr871/src/cedarx/os_glue/include/list.h @@ -0,0 +1,193 @@ +#ifndef __MELIS_PTHREAD_LIST_H__ +#define __MELIS_PTHREAD_LIST_H__ + +#define prefetch(x) x +/* + * Simple doubly linked list implementation. + * + * Some of the internal functions ("__xxx") are useful when + * manipulating whole lists rather than single entries, as + * sometimes we already know the next/prev entries and we can + * generate better code by using them directly rather than + * using the generic single-entry routines. + */ + +struct list_head { + struct list_head *next, *prev; +}; + +#define LIST_HEAD_INIT(name) { &(name), &(name) } + +#define LIST_HEAD(name) \ + struct list_head name = LIST_HEAD_INIT(name) + +#define INIT_LIST_HEAD(ptr) do { \ + (ptr)->next = (ptr); (ptr)->prev = (ptr); \ +} while (0) + + +static __inline void __list_add(struct list_head * new, + struct list_head * prev, + struct list_head * next) +{ + next->prev = new; + new->next = next; + new->prev = prev; + prev->next = new; +} + +/** + * list_add - add a new entry + * @new: new entry to be added + * @head: list head to add it after + * + * Insert a new entry after the specified head. + * This is good for implementing stacks. + */ +static __inline void list_add(struct list_head *new, struct list_head *head) +{ + __list_add(new, head, head->next); +} + +/** + * list_add_tail - add a new entry + * @new: new entry to be added + * @head: list head to add it before + * + * Insert a new entry before the specified head. + * This is useful for implementing queues. + */ +static __inline void list_add_tail(struct list_head *new, struct list_head *head) +{ + __list_add(new, head->prev, head); +} + +/* + * Delete a list entry by making the prev/next entries + * point to each other. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + */ +static __inline void __list_del(struct list_head * prev, + struct list_head * next) +{ + next->prev = prev; + prev->next = next; +} + +/** + * list_del - deletes entry from list. + * @entry: the element to delete from the list. + * Note: list_empty on entry does not return true after this, the entry is in an undefined state. + */ +static __inline void list_del(struct list_head *entry) +{ + __list_del(entry->prev, entry->next); +} + +/** + * list_del_init - deletes entry from list and reinitialize it. + * @entry: the element to delete from the list. + */ +static __inline void list_del_init(struct list_head *entry) +{ + __list_del(entry->prev, entry->next); + INIT_LIST_HEAD(entry); +} + +/** + * list_empty - tests whether a list is empty + * @head: the list to test. + */ +static __inline int list_empty(struct list_head *head) +{ + return head->next == head; +} + +/** + * list_splice - join two lists + * @list: the new list to add. + * @head: the place to add it in the first list. + */ +static __inline void list_splice(struct list_head *list, struct list_head *head) +{ + struct list_head *first = list->next; + + if (first != list) { + struct list_head *last = list->prev; + struct list_head *at = head->next; + + first->prev = head; + head->next = first; + + last->next = at; + at->prev = last; + } +} + +static __inline int list_valid_check(struct list_head *head) +{ + struct list_head *next = head->next; + if(next == head) + return 0; + while(next!=head) + { + next = next->next; + if(next == next->prev) + { + return -1; + } + } + return 1; +} + +/** + * list_entry - get the struct for this entry + * @ptr: the &struct list_head pointer. + * @type: the type of the struct this is embedded in. + * @member: the name of the list_struct within the struct. + */ +#define list_entry(ptr, type, member) \ + ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member))) + +/** + * list_for_each - iterate over a list + * @pos: the &struct list_head to use as a loop counter. + * @head: the head for your list. + */ +#define list_for_each(pos, head) \ + for (pos = (head)->next /*, prefetch(pos->next)*/; pos != (head); \ + pos = pos->next /*, prefetch(pos->next)*/) +/** + * list_for_each_prev - iterate over a list backwards + * @pos: the &struct list_head to use as a loop counter. + * @head: the head for your list. + */ +#define list_for_each_prev(pos, head) \ + for (pos = (head)->prev; /*prefetch(pos->prev), */pos != (head); \ + pos = pos->prev) + + +/** + * list_for_each_safe - iterate over a list safe against removal of list entry + * @pos: the &struct list_head to use as a loop counter. + * @n: another &struct list_head to use as temporary storage + * @head: the head for your list. + */ +#define list_for_each_safe(pos, n, head) \ + for (pos = (head)->next, n = pos->next; pos != (head); \ + pos = n, n = pos->next) + +/** + * list_for_each_prev_safe - iterate over a list backwards safe against removal of list entry + * @pos: the &struct list_head to use as a loop counter. + * @n: another &struct list_head to use as temporary storage + * @head: the head for your list. + */ +#define list_for_each_prev_safe(pos, n, head) \ + for (pos = (head)->prev, n = pos->prev; pos != (head); \ + pos = n, n = pos->prev) + +#endif /* __MELIS_PTHREAD_LIST_H__ */ + diff --git a/platform/mcu/xr871/src/cedarx/os_glue/include/pthread.h b/platform/mcu/xr871/src/cedarx/os_glue/include/pthread.h new file mode 100644 index 0000000000..2922412a3c --- /dev/null +++ b/platform/mcu/xr871/src/cedarx/os_glue/include/pthread.h @@ -0,0 +1,290 @@ +#ifndef _PTHREAD_H_ +#define _PTHREAD_H_ +//#include +//#include +#include +#include +//#include +#include "kernel/os/os.h" + +#define ONCE_INITIALIZING (1 << 0) +#define ONCE_COMPLETED (1 << 1) +#define PTHREAD_ONCE_INIT 0 + + +#if defined(__LP64__) + #define __RESERVED_INITIALIZER , {0} +#else + #define __RESERVED_INITIALIZER +#endif + +typedef long long int64_t; +typedef int pthread_key_t; +typedef void * pthread_t; +typedef volatile int pthread_once_t; +typedef int pid_t; +typedef long time_t; + +typedef enum MELIS_2_PTHREAD_OS_STATE +{ + PTHREAD_TASK_STATE_READY = 0, + PTHREAD_TASK_STATE_RUNNING, + PTHREAD_TASK_STATE_EXIT, + PTHREAD_TASK_STATE_INVALID, +}pthread_osstate_t; + + +/*typedef struct { + unsigned int flag; + unsigned int entercnt; + pthread_t owner; + __krnl_event_t* lock; +}pthread_mutex_t;*/ + +typedef struct { +// unsigned int flag; +// unsigned int entercnt; +// pthread_t owner; + OS_Mutex_t lock; +} pthread_mutex_t; + +typedef struct { + int cnt; + int wait; + OS_Semaphore_t sem; +} sem_t; + +/*struct timespec { + long tv_sec; + long tv_nsec; +}; + +struct timeval{ + long tv_sec; + long tv_usec; +}; + +struct timezone { + int tz_minuteswest; + int tz_dsttime; +};*/ + +//#define CLOCK_REALTIME 0 +#define CLOCK_MONOTONIC (CLOCK_REALTIME + 1) +#define SEM_MAX_VALUE 0xffff +#define ETIMEOUT 21 +#define EINVAL 22 +#define __PTHREAD_MUTEX_INIT_VALUE 0 +#define __PTHREAD_RECURSIVE_MUTEX_INIT_VALUE 0x4000 +#define __PTHREAD_ERRORCHECK_MUTEX_INIT_VALUE 0x8000 + +#define PTHREAD_MUTEX_INITIALIZER {__PTHREAD_MUTEX_INIT_VALUE __RESERVED_INITIALIZER} +#define PTHREAD_RECURSIVE_MUTEX_INITIALIZER {__PTHREAD_RECURSIVE_MUTEX_INIT_VALUE __RESERVED_INITIALIZER} +#define PTHREAD_ERRORCHECK_MUTEX_INITIALIZER {__PTHREAD_ERRORCHECK_MUTEX_INIT_VALUE __RESERVED_INITIALIZER} + +enum { + PTHREAD_MUTEX_NORMAL = 0, + PTHREAD_MUTEX_RECURSIVE = 1, + PTHREAD_MUTEX_ERRORCHECK = 2, + + PTHREAD_MUTEX_ERRORCHECK_NP = PTHREAD_MUTEX_ERRORCHECK, + PTHREAD_MUTEX_RECURSIVE_NP = PTHREAD_MUTEX_RECURSIVE, + + PTHREAD_MUTEX_DEFAULT = PTHREAD_MUTEX_NORMAL +}; + +enum +{ + PTHREAD_EVENT_SET_0 = 1<<0, + PTHREAD_EVENT_SET_1 = 1<<1, + PTHREAD_EVENT_SET_2 = 1<<2, + PTHREAD_EVENT_SET_3 = 1<<3, + PTHREAD_EVENT_SET_4 = 1<<4, + PTHREAD_EVENT_SET_5 = 1<<5, + PTHREAD_EVENT_SET_6 = 1<<6 +}; + + +typedef sem_t pthread_cond_t; + +#define PTHREAD_COND_INITIALIZER {0 __RESERVED_INITIALIZER} + +typedef struct { + unsigned int flags; + void* stack_base; + size_t stack_size; + size_t guard_size; + int sched_policy; + int sched_priority; +#ifdef __LP64__ + char __reserved[16]; +#endif +} pthread_attr_t; + +typedef long pthread_mutexattr_t; +typedef long pthread_condattr_t; + +typedef long pthread_rwlockattr_t; + +typedef struct { +#if !defined(__LP64__) + pthread_mutex_t __unused_lock; + pthread_cond_t __unused_cond; +#endif + volatile int state; // 0=unlock, -1=writer lock, +n=reader lock + volatile int writer_thread_id; + volatile int pending_readers; + volatile int pending_writers; + int attr; +#ifdef __LP64__ + char __reserved[36]; +#else + char __reserved[12]; +#endif + +} pthread_rwlock_t; + +#ifdef __LP64__ + #define PTHREAD_RWLOCK_INITIALIZER { 0, 0, 0, 0, 0, { 0 } } +#else + #define PTHREAD_RWLOCK_INITIALIZER { PTHREAD_MUTEX_INITIALIZER, PTHREAD_COND_INITIALIZER, 0, 0, 0, 0, 0, { 0 } } +#endif + +#define PTHREAD_ONCE_INIT 0 + +#if defined(__LP64__) +#define PTHREAD_STACK_MIN (4 * PAGE_SIZE) +#else +#define PTHREAD_STACK_MIN (2 * PAGE_SIZE) +#endif + +#define PTHREAD_CREATE_DETACHED 0x00000001 +#define PTHREAD_CREATE_JOINABLE 0x00000000 + +#define PTHREAD_PROCESS_PRIVATE 0 +#define PTHREAD_PROCESS_SHARED 1 + +#define PTHREAD_SCOPE_SYSTEM 0 +#define PTHREAD_SCOPE_PROCESS 1 + +typedef void (*__pthread_cleanup_func_t)(void*); + +typedef struct __pthread_cleanup_t { + struct __pthread_cleanup_t* __cleanup_prev; + __pthread_cleanup_func_t __cleanup_routine; + void* __cleanup_arg; +} __pthread_cleanup_t; + +#ifdef __cplusplus +extern "C" +{ +#endif + pid_t gettid(void); + int pthread_atfork(void (*)(void), void (*)(void), void(*)(void)); + int pthread_attr_destroy(pthread_attr_t*); + int pthread_attr_getdetachstate(const pthread_attr_t*, int*); + int pthread_attr_getguardsize(const pthread_attr_t*, size_t*); + int pthread_attr_getschedpolicy(const pthread_attr_t*, int*); + int pthread_attr_getscope(const pthread_attr_t*, int*); + int pthread_attr_getstack(const pthread_attr_t*, void**, size_t*); + int pthread_attr_getstacksize(const pthread_attr_t*, size_t*) ; + int pthread_attr_init(pthread_attr_t*); + int pthread_attr_setdetachstate(pthread_attr_t*, int); + int pthread_attr_setguardsize(pthread_attr_t*, size_t); + int pthread_attr_setschedpolicy(pthread_attr_t*, int); + int pthread_attr_setscope(pthread_attr_t*, int); + int pthread_attr_setstack(pthread_attr_t*, void*, size_t); + int pthread_attr_setstacksize(pthread_attr_t*, size_t stack_size); + + int pthread_condattr_destroy(pthread_condattr_t*); + int pthread_condattr_getpshared(const pthread_condattr_t*, int*); + int pthread_condattr_init(pthread_condattr_t*); + int pthread_condattr_setpshared(pthread_condattr_t*, int); + + int pthread_cond_broadcast(pthread_cond_t*); + int pthread_cond_destroy(pthread_cond_t*); + int pthread_cond_init(pthread_cond_t*, const pthread_condattr_t*); + int pthread_cond_signal(pthread_cond_t*); + int pthread_cond_timedwait(pthread_cond_t*, pthread_mutex_t*, const struct timespec*); + int pthread_cond_wait(pthread_cond_t*, pthread_mutex_t*); + + int pthread_create(pthread_t*, pthread_attr_t const*, void *(*)(void*), void*); + int pthread_detach(pthread_t); + void pthread_exit(void*); + + int pthread_equal(pthread_t, pthread_t); + + int pthread_getattr_np(pthread_t, pthread_attr_t*); + void* pthread_getspecific(pthread_key_t); + + pid_t pthread_gettid_np(pthread_t); + + int pthread_join(pthread_t, void**); + + int pthread_key_create(pthread_key_t*, void (*)(void*)); + int pthread_key_delete(pthread_key_t); + + int pthread_kill(pthread_t, int); + + int pthread_mutexattr_destroy(pthread_mutexattr_t*); + int pthread_mutexattr_getpshared(const pthread_mutexattr_t*, int*); + int pthread_mutexattr_gettype(const pthread_mutexattr_t*, int*); + int pthread_mutexattr_init(pthread_mutexattr_t*) ; + int pthread_mutexattr_setpshared(pthread_mutexattr_t*, int); + int pthread_mutexattr_settype(pthread_mutexattr_t*, int); + + int pthread_mutex_destroy(pthread_mutex_t*); + int pthread_mutex_init(pthread_mutex_t*, const pthread_mutexattr_t*); + int pthread_mutex_lock(pthread_mutex_t*) ; + int pthread_mutex_timedlock(pthread_mutex_t*, const struct timespec*); + int pthread_mutex_trylock(pthread_mutex_t*) ; + int pthread_mutex_unlock(pthread_mutex_t*) ; + + int pthread_once(pthread_once_t*, void (*)(void)); + + int pthread_rwlockattr_destroy(pthread_rwlockattr_t*); + int pthread_rwlockattr_getpshared(const pthread_rwlockattr_t*, int*); + int pthread_rwlockattr_init(pthread_rwlockattr_t*); + int pthread_rwlockattr_setpshared(pthread_rwlockattr_t*, int); + + int pthread_rwlock_destroy(pthread_rwlock_t*); + int pthread_rwlock_init(pthread_rwlock_t*, const pthread_rwlockattr_t*) ; + int pthread_rwlock_rdlock(pthread_rwlock_t*) ; + int pthread_rwlock_timedrdlock(pthread_rwlock_t*, const struct timespec*); + int pthread_rwlock_timedwrlock(pthread_rwlock_t*, const struct timespec*); + int pthread_rwlock_tryrdlock(pthread_rwlock_t*); + int pthread_rwlock_trywrlock(pthread_rwlock_t*); + int pthread_rwlock_unlock(pthread_rwlock_t *rwlock); + int pthread_rwlock_wrlock(pthread_rwlock_t*); + time_t time(time_t *t); + + pthread_t pthread_self(void); + + int pthread_setname_np(pthread_t, const char*); + int pthread_setspecific(pthread_key_t, const void*); + + + + extern void __pthread_cleanup_push(__pthread_cleanup_t* c, __pthread_cleanup_func_t, void*); + extern void __pthread_cleanup_pop(__pthread_cleanup_t*, int); + + int sem_init(sem_t *sem, int pshared, unsigned int value); + int sem_destroy(sem_t *sem); + int sem_wait (sem_t *sem); + int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout); + int sem_post(sem_t *sem); + int sem_trywait(sem_t *sem); + int sem_getvalue(sem_t *sem, int *sval); + int sem_unlink(const char* name); + int sem_close(sem_t *sem); + sem_t *sem_open(const char *name, int oflag, ...); +#define gettimeofday __gettimeofday + int __gettimeofday(struct timeval* tv, struct timezone* tz); + void cdx_abort(void); + int clock_gettime(int a,struct timespec *t); + void exit(int status); +#ifdef __cplusplus +} +#endif + +#endif /* _PTHREAD_H_ */ diff --git a/platform/mcu/xr871/src/cedarx/os_glue/include/unistd.h b/platform/mcu/xr871/src/cedarx/os_glue/include/unistd.h new file mode 100644 index 0000000000..3b0318f37c --- /dev/null +++ b/platform/mcu/xr871/src/cedarx/os_glue/include/unistd.h @@ -0,0 +1,26 @@ +#ifndef _UNISTD_H_ +#define _UNISTD_H_ +#include "kernel/os/os.h" + +#ifdef __cplusplus +extern "C" +{ +#endif +static inline int usleep(unsigned int usecs) { + OS_MSleep((usecs + 999) / 1000); +// OS_MSleep((usecs + 1023) & (~1023)); + return 0; +} +static inline int msleep(unsigned int msecs) { + OS_MSleep(msecs); + return 0; +} +static inline void sleep(int seconds) { + OS_Sleep(seconds); +} + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/platform/mcu/xr871/src/cedarx/parser/amr/CdxAmrParser.h b/platform/mcu/xr871/src/cedarx/parser/amr/CdxAmrParser.h new file mode 100644 index 0000000000..6532d23a76 --- /dev/null +++ b/platform/mcu/xr871/src/cedarx/parser/amr/CdxAmrParser.h @@ -0,0 +1,50 @@ +/* +* Copyright (c) 2008-2016 Allwinner Technology Co. Ltd. +* All rights reserved. +* +* File : CdxAmrParser.h +* Description : Amr Parser +* History : +* +*/ +#ifndef CDX_AMR_PARSER_H +#define CDX_AMR_PARSER_H + +#define SEEK_TABLE_LEN 10 /* i think it's useless when we don't need parser seekto, + but i'm not sure. origin is 300 */ +#define CODEC_ID_AMR_NB 1 +#define CODEC_ID_AMR_WB 2 + +typedef struct AmrParserImplS +{ + //audio common + CdxParserT base; + CdxStreamT *stream; + cdx_int64 ulDuration;//ms + pthread_cond_t cond; + cdx_int64 fileSize;//total file length + cdx_int64 file_offset; //now read location + cdx_int32 mErrno; //Parser Status + cdx_uint32 flags; //cmd + //amr base + cdx_int64 pts; + cdx_int32 head_len; + cdx_int32 framecounts;//samples + cdx_int32 framepcms; + cdx_int32 chans; + cdx_int32 sample_rate; + cdx_int32 is_wide; + cdx_int32 seek_table_len; + cdx_int64 seek_table[SEEK_TABLE_LEN]; + +}AmrParserImplS; + +static const char AMR_header [] = "#!AMR\n"; +static const char AMRWB_header [] = "#!AMR-WB\n"; + +static const cdx_uint8 block_size_NB[16] = { 12, 13, 15, 17, 19, 20, 26, + 31, 5, 0, 0, 0, 0, 0, 0, 0 }; +static const cdx_uint8 block_size_WB[16] = {18, 24, 33, 37, 41, 47, 51, + 59, 61, 6, 6, 0, 0, 0, 1, 1}; + +#endif diff --git a/platform/mcu/xr871/src/cedarx/parser/base/id3base/BaseUtils.h b/platform/mcu/xr871/src/cedarx/parser/base/id3base/BaseUtils.h new file mode 100644 index 0000000000..1fbabc0e94 --- /dev/null +++ b/platform/mcu/xr871/src/cedarx/parser/base/id3base/BaseUtils.h @@ -0,0 +1,60 @@ +/* +* Copyright (c) 2008-2017 Allwinner Technology Co. Ltd. +* All rights reserved. +* +* File : BaseUtils.h +* Description : +* History : +* Author : Khan +* Date : 2017/3/2 +*/ + +#ifndef BASEUTILS_H +#define BASEUTILS_H + +#include +#include +#include +#include +#include +#include +#include +#include + +typedef enum _kbool +{ + kfalse = 0, + ktrue +}kbool; + +typedef enum _kerrno +{ + KERR_NONE = 0, + KERR_NULL_PTR = -1000, + KERR_INVAILD_DATA, + KERR_OUT_OF_RANGE, + KERR_NO_DATA, + KERR_NO_MEM, + KERR_RESERVED, +}kerrno; + +static __inline cdx_uint16 U16_AT(const cdx_uint8 *ptr) { + return ptr[0] << 8 | ptr[1]; +} + +static __inline cdx_uint32 U32_AT(const cdx_uint8 *ptr) { + return ptr[0] << 24 | ptr[1] << 16 | ptr[2] << 8 | ptr[3]; +} + +static __inline cdx_uint64 U64_AT(const cdx_uint8 *ptr) { + return ((cdx_uint64)U32_AT(ptr)) << 32 | U32_AT(ptr + 4); +} + +static __inline cdx_uint16 bswap16(cdx_uint16 x) +{ + x= (x>>8) | (x<<8); + return x; +} + + +#endif diff --git a/platform/mcu/xr871/src/cedarx/parser/base/id3base/CdxMetaData.h b/platform/mcu/xr871/src/cedarx/parser/base/id3base/CdxMetaData.h new file mode 100644 index 0000000000..d7ac9a58fd --- /dev/null +++ b/platform/mcu/xr871/src/cedarx/parser/base/id3base/CdxMetaData.h @@ -0,0 +1,70 @@ +/* +* Copyright (c) 2008-2017 Allwinner Technology Co. Ltd. +* All rights reserved. +* +* File : CdxMetaData.h +* Description : +* History : +* Author : Khan +* Date : 2017/3/7 +*/ +#include "BaseUtils.h" + +#ifndef CDXMETADATA_H +#define CDXMETADATA_H + +#define META_MKTAG(a,b,c,d) ((a) | ((b) << 8) | ((c) << 16) | ((unsigned)(d) << 24)) +#define META_MKBETAG(a,b,c,d) ((d) | ((c) << 8) | ((b) << 16) | ((unsigned)(a) << 24)) + +typedef struct _Map Map; + +enum _META_TITLE_KINDS +{ + ARTIST = 0, + ALBUM, + ALBUM_ARTIST, + TITLE, + COMPOSER, + GENRE, + YEAR, + AUTHOR, + CDTRACKNUMBER, + DISCNUMBER, + COMPILATION, + DATE, +}; + +enum _key_string +{ + CdxMetaKeyAlbum = META_MKBETAG('a','l','b','u'), // cstring + CdxMetaKeyArtist = META_MKBETAG('a','r','t','i'), // cstring + CdxMetaKeyAlbumArtist = META_MKBETAG('a','a','r','t'), // cstring + CdxMetaKeyComposer = META_MKBETAG('c','o','m','p'), // cstring + CdxMetaKeyGenre = META_MKBETAG('g','e','n','r'), // cstring + CdxMetaKeyTitle = META_MKBETAG('t','i','t','l'), // cstring + CdxMetaKeyYear = META_MKBETAG('y','e','a','r'), // cstring + CdxMetaKeyAlbumArt = META_MKBETAG('a','l','b','A'), // compressed image data + CdxMetaKeyAlbumArtMIME = META_MKBETAG('a','l','A','M'), // cstring + CdxMetaKeyAuthor = META_MKBETAG('a','u','t','h'), // cstring + CdxMetaKeyCDTrackNumber = META_MKBETAG('c','d','t','r'), // cstring + CdxMetaKeyDiscNumber = META_MKBETAG('d','n','u','m'), // cstring + CdxMetaKeyDate = META_MKBETAG('d','a','t','e'), // cstring + CdxMetaKeyWriter = META_MKBETAG('w','r','i','t'), // cstring + CdxMetaKeyCompilation = META_MKBETAG('c','p','i','l'), // cstring + CdxMetaKeyLocation = META_MKBETAG('l','o','c',' '), // cstring + CdxMetaKeyTimeScale = META_MKBETAG('t','m','s','l'), // cdx_int32 +}; + +typedef enum _META_TITLE_KINDS META_IDX; +typedef enum _key_string CdxMetaKeyString; + +struct _Map{ + META_IDX idx; + CdxMetaKeyString key; + const char *tag1; + const char *tag2; +}; + +void SetMetaData(CdxMediaInfoT *mediaInfo, META_IDX idx, const char* content); + +#endif diff --git a/platform/mcu/xr871/src/cedarx/parser/base/id3base/CdxUtfCode.h b/platform/mcu/xr871/src/cedarx/parser/base/id3base/CdxUtfCode.h new file mode 100644 index 0000000000..bfbeccf7ea --- /dev/null +++ b/platform/mcu/xr871/src/cedarx/parser/base/id3base/CdxUtfCode.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2008-2017 Allwinner Technology Co. Ltd. + * All rights reserved. + * + * File : CdxUtfCode.h + * Description : Parse Utf Code head file + * History : + * + */ + +#ifndef UTF_CODE_H +#define UTF_CODE_H + +#include +#include +#include +#include +#include "BaseUtils.h" + +void CdxUtf16toUtf8(const cdx_uint16* src, size_t src_len, char* dst); +void CdxUtf32toUtf8(const cdx_uint32* src, size_t src_len, char* dst); + +int32_t CdxUtf16toUtf8Length(const cdx_uint16 *src, size_t src_len); +int32_t CdxUtf32toUtf8Length(const cdx_uint32 *src, size_t src_len); + +char* CdxAllocFromUTF8(const char* in, size_t len); +char* CdxAllocFromUTF16(const cdx_uint16* in, size_t len); +char* CdxAllocFromUTF32(const cdx_uint32* in, size_t len); + +#endif diff --git a/platform/mcu/xr871/src/cedarx/parser/base/id3base/Id3Base.h b/platform/mcu/xr871/src/cedarx/parser/base/id3base/Id3Base.h new file mode 100644 index 0000000000..77ff99412a --- /dev/null +++ b/platform/mcu/xr871/src/cedarx/parser/base/id3base/Id3Base.h @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2008-2017 Allwinner Technology Co. Ltd. + * All rights reserved. + * + * File : Id3Base.h + * Description : Id3_v1 and Id3_v2 parser head file + * History : + * + */ + +#ifndef ID3_H +#define ID3_H + +#include +#include +#include +#include +#include "StringContainer.h" +#include "CdxMetaData.h" + +#define IS_VERSION_2(v) ((v >= ID3_V2_2) && (v <= ID3_V2_4)) +#define IS_VERSION_1(v) ((v >= ID3_V1) && (v <= ID3_V1_1)) + +#define V1_TAG_SIZE 128 + +typedef struct _ID3 ID3; +typedef struct _Iterator Iterator; +typedef struct _id3_header id3_header; + +struct _Iterator { + ID3 *mParent;//get from parent, parent to free + char *mID; + size_t mOffset; + const cdx_uint8 *mFrameData;//get from parent, parent to free + size_t mFrameSize; + int encoding; + + kbool (*done)(void* hself); + kerrno (*getID)(void* hself, StringCtn *id); + kerrno (*getString)(void* hself, StringCtn *s, StringCtn *ss/*NULL*/); + kerrno (*getStringInternal)(void* hself, StringCtn *s, kbool secondhalf); + const cdx_uint8* (*getData)(void* hself, size_t *length); + kerrno (*next)(void* hself); + kerrno (*findFrame)(void* hself); + size_t (*getHeaderLength)(void* hself); +}; + +enum _Version { + ID3_UNKNOWN, + ID3_V1, + ID3_V1_1, + ID3_V2_2, + ID3_V2_3, + ID3_V2_4, +}; + +typedef enum _Version Version; + +struct _id3_header { + char id[3]; + cdx_uint8 version_major; + cdx_uint8 version_minor; + cdx_uint8 flags; + cdx_uint8 enc_size[4]; +}; + +struct _ID3 { + kbool mIsValid; + cdx_uint8 *mData; + size_t mSize; + size_t mFirstFrameOffset; + Version mVersion; + + // size of the ID3 tag including header before any unsynchronization. + // only valid for IDV2+ + size_t mRawSize; + + kbool (*isValid)(void* hself); + Version (*version)(void* hself); + void * (*getAlbumArt)(void* hself, size_t *length, StringCtn *mime); + size_t (*rawSize)(void* hself); + kbool (*doParseV1)(void* hself); + kbool (*doParseV2)(void* hself); + kerrno (*doRemoveUnsynchronization)(void* hself); + kbool (*doRemoveUnsynchronizationV2_4)(void* hself, kbool iTunesHack); + + CdxStreamT* stream; + cdx_uint8* extraBuf; + size_t extraBufRange; + size_t extraBufOffset; + size_t extraBufValidSz; + size_t (*read)(void* hself, void* buf, size_t sz); + size_t (*readAt)(void* hself, int64_t offset, void* buf, size_t sz); + kbool (*getsize)(void* hself, int64_t* sz); + int64_t (*tell)(void* hself); +}; + +Iterator* GenerateIterator(ID3* parent, const char *id); +kbool EraseIterator(void* arg); +ID3* GenerateId3(CdxStreamT* in, cdx_uint8* init_buf, size_t buf_sz, + kbool ignoreV1); +kbool EraseId3(void* arg); + +void Id3BaseGetMetaData(CdxMediaInfoT *mediaInfo, ID3* id3Base); + +void Id3BaseExtraAlbumPic(CdxMediaInfoT *mediaInfo, ID3* id3Base); + +#endif diff --git a/platform/mcu/xr871/src/cedarx/parser/base/id3base/StringContainer.h b/platform/mcu/xr871/src/cedarx/parser/base/id3base/StringContainer.h new file mode 100644 index 0000000000..b69c332a96 --- /dev/null +++ b/platform/mcu/xr871/src/cedarx/parser/base/id3base/StringContainer.h @@ -0,0 +1,33 @@ +/* +* Copyright (c) 2008-2017 Allwinner Technology Co. Ltd. +* All rights reserved. +* +* File : StringContainer.h +* Description : +* History : +* Author : Khan +* Date : 2017/3/2 +*/ + +#ifndef STRINGCONTAINER_H +#define STRINGCONTAINER_H + +#include +#include +#include +#include +#include "BaseUtils.h" + +typedef struct _StringCtn StringCtn; +struct _StringCtn +{ + char* mString; + kbool (*setTo8)(void* hself, const char*, size_t); + kbool (*setTo16)(void* hself, const cdx_uint16*, size_t); + void (*clear)(void* hself); + const char* (*string)(void* hself); +}; + +StringCtn* GenerateStringContainer(void); +kbool EraseStringContainer(void*); +#endif diff --git a/platform/mcu/xr871/src/cedarx/parser/id3v2/CdxId3v2Parser.h b/platform/mcu/xr871/src/cedarx/parser/id3v2/CdxId3v2Parser.h new file mode 100644 index 0000000000..e75d7d364f --- /dev/null +++ b/platform/mcu/xr871/src/cedarx/parser/id3v2/CdxId3v2Parser.h @@ -0,0 +1,150 @@ +/* +* Copyright (c) 2008-2016 Allwinner Technology Co. Ltd. +* All rights reserved. +* +* File : CdxId3Parser.h +* Description : +* History : +* Author : Khan +* Date : 2014/12/08 +*/ + +#ifndef CDX_ID3_PARSER_H +#define CDX_ID3_PARSER_H + +#define INFLEN 1024*8//less than 8k +#define ID3TAGNUM 8 +#define SKIPLEN 1024 +#define ID3v2_HEADER_SIZE 10 + +#include "Id3Base.h" + +typedef struct __language_coding +{ + char language[4]; + cdx_int32 coding; +} language_coding_t; + +typedef enum { + IMG_FORMAT_BMP =0, + IMG_FORMAT_JPG, + IMG_FORMAT_GIF, + IMG_FORMAT_PNG, + IMG_FORMAT_UNSUPPORT = -1 +}img_format_t; + +typedef struct CDX_ID3_IMAGE_INFO +{ + int length; //image length + int FileLocation; //image location + img_format_t img_format; + int pic_type; //picture type; + int img_high; + int img_width; + int otherdata; + +}cdx_id3_image_info_t; + +typedef enum CDX_A_AUDIO_FONTTYPE +{ + A_AUDIO_FONTTYPE_ISOIEC8859_1 = 0, //ISO/IEC 8859-1 + A_AUDIO_FONTTYPE_UTF_16LE,// + A_AUDIO_FONTTYPE_UTF_16BE, + A_AUDIO_FONTTYPE_UTF_8,// + A_AUDIO_FONTTYPE_ISOIEC8859_2,// + A_AUDIO_FONTTYPE_ISOIEC8859_3,// + A_AUDIO_FONTTYPE_ISOIEC8859_4,// + A_AUDIO_FONTTYPE_ISOIEC8859_5,// + A_AUDIO_FONTTYPE_ISOIEC8859_6, + A_AUDIO_FONTTYPE_ISOIEC8859_7, + A_AUDIO_FONTTYPE_ISOIEC8859_8, + A_AUDIO_FONTTYPE_ISOIEC8859_9, + A_AUDIO_FONTTYPE_ISOIEC8859_10, + A_AUDIO_FONTTYPE_ISOIEC8859_11, + A_AUDIO_FONTTYPE_ISOIEC8859_12, + A_AUDIO_FONTTYPE_ISOIEC8859_13, + A_AUDIO_FONTTYPE_ISOIEC8859_14, + A_AUDIO_FONTTYPE_ISOIEC8859_15, + A_AUDIO_FONTTYPE_ISOIEC8859_16, + A_AUDIO_FONTTYPE_WINDOWS_1250, + A_AUDIO_FONTTYPE_WINDOWS_1251,// + A_AUDIO_FONTTYPE_WINDOWS_1252, + A_AUDIO_FONTTYPE_WINDOWS_1253, + A_AUDIO_FONTTYPE_WINDOWS_1254, + A_AUDIO_FONTTYPE_WINDOWS_1255, + A_AUDIO_FONTTYPE_WINDOWS_1256, + A_AUDIO_FONTTYPE_WINDOWS_1257, + A_AUDIO_FONTTYPE_WINDOWS_1258, + A_AUDIO_FONTTYPE_KOI8_R, + A_AUDIO_FONTTYPE_KOI8_U, + A_AUDIO_FONTTYPE_GB2312, + A_AUDIO_FONTTYPE_GBK, + A_AUDIO_FONTTYPE_BIG5, + A_AUDIO_FONTTYPE_ = -1 +}cdx_audio_fonttype_e; + +struct Id3Pic +{ + cdx_uint8* addr; + cdx_int32 lenth; + struct Id3Pic* father; +}; + +typedef struct Id3v2ParserImplS +{ + //audio common + CdxParserT base; + CdxStreamT *stream; + cdx_int32 mErrno; //Parser Status + cdx_uint32 flags; //cmd + cdx_int32 forceStop; + //id3 base + CdxParserT *child;/*Ϊparserһýļparsermp3 parser*/ + cdx_int32 shareStreamWithChild; + cdx_char * keyinfo; +// cdx_char newurl[128]; + cdx_int64 fdoffset; + cdx_int64 file_size; + CdxDataSourceT cdxDataSource; + cdx_int32 Id3v2len; + cdx_int64 file_offset; + CdxStreamT *childStream; + pthread_mutex_t lock; + +#if 0 + /*ID3*/ + cdx_int32 mGenre_sz; // + char *mGenre; // pop soft... + cdx_audio_fonttype_e mGenreCharEncode; + + cdx_int32 mtitle_sz; // + char *mtitle; + cdx_audio_fonttype_e mtitleCharEncode; + + cdx_int32 mauthor_sz; // ݳ + char *mauthor; + cdx_audio_fonttype_e mauthorCharEncode; + + cdx_int32 mAlbum_sz; // ר + char *mAlbum; + cdx_audio_fonttype_e mAlbumCharEncode; + + cdx_int32 mYear_sz; // Ʒ + char *mYear; + cdx_audio_fonttype_e mYearCharEncode; + + cdx_int32 mAPic_sz; // attached picture + cdx_id3_image_info_t *mAPic; + cdx_audio_fonttype_e mAPicCharEncode; + + cdx_int32 mInforBufLeftLength; + cdx_uint8 *mInforBuf; + cdx_uint8 mInfor[INFLEN]; + struct Id3Pic *pAlbumArt; + cdx_int32 pAlbumArtid; +#endif + + ID3* id3v2; +}Id3v2ParserImplS; + +#endif diff --git a/platform/mcu/xr871/src/cedarx/parser/include/CdxParser.h b/platform/mcu/xr871/src/cedarx/parser/include/CdxParser.h new file mode 100644 index 0000000000..26f62a01c0 --- /dev/null +++ b/platform/mcu/xr871/src/cedarx/parser/include/CdxParser.h @@ -0,0 +1,721 @@ +/* + * Copyright (c) 2008-2016 Allwinner Technology Co. Ltd. + * All rights reserved. + * + * File : CdxParser.h + * Description : Parser + * History : + * + */ + +#ifndef CDX_PARSER_H +#define CDX_PARSER_H + +#include +#include +#include +#include +#include +#include +//#include +#include +//#include +#include + +#ifdef __ANDROID__ +#include +#endif + +enum CdxParserTypeE +{ + CDX_PARSER_UNKNOW = -1, + CDX_PARSER_MOV, + CDX_PARSER_MKV, + CDX_PARSER_ASF, + CDX_PARSER_TS, + CDX_PARSER_AVI, + CDX_PARSER_FLV, + CDX_PARSER_PMP, + + CDX_PARSER_HLS, + CDX_PARSER_DASH, + CDX_PARSER_MMS, + CDX_PARSER_BD, + CDX_PARSER_OGG, + CDX_PARSER_M3U9, + CDX_PARSER_RMVB, + CDX_PARSER_PLAYLIST, + CDX_PARSER_APE, + CDX_PARSER_FLAC, + CDX_PARSER_AMR, + CDX_PARSER_ATRAC, + CDX_PARSER_MP3, + CDX_PARSER_DTS, + CDX_PARSER_AC3, + CDX_PARSER_AAC, + CDX_PARSER_WAV, + CDX_PARSER_REMUX, /* rtsp, etc... */ + CDX_PARSER_WVM, + CDX_PARSER_MPG, + CDX_PARSER_MMSHTTP, + CDX_PARSER_AWTS, + CDX_PARSER_SSTR, + CDX_PARSER_CAF, + CDX_PARSER_G729, + CDX_PARSER_DSD, + CDX_PARSER_AIFF, + CDX_PARSER_ID3V2, + CDX_PARSER_ENV, + CDX_PARSER_SSTR_PLAYREADY, + CDX_PARSER_AWRAWSTREAM, + CDX_PARSER_AWSPECIALSTREAM, +}; + +typedef struct CdxPacketS CdxPacketT; +typedef struct CdxMediaInfoS CdxMediaInfoT; +typedef enum CdxParserTypeE CdxParserTypeT; +typedef struct CdxParserCreatorS CdxParserCreatorT; +typedef struct CdxParserS CdxParserT; +//typedef struct ParserMetaDataS ParserMetaDataT; + +#define MINOR_STREAM 0x0001 /*0 major stream, 1 minor stream*/ +#define FIRST_PART 0x0002 +#define LAST_PART 0x0004 +#define KEY_FRAME 0x0008 + +struct CdxPacketS +{ + void *buf; + void *ringBuf; + cdx_int32 buflen; + cdx_int32 ringBufLen; + int64_t pts; + cdx_int64 duration; + cdx_int32 type; + cdx_int32 length; + cdx_uint32 flags; /* MINOR_STREAM, FIRST_PART, LAST_PART, etc... */ + cdx_int32 streamIndex; + cdx_int64 pcr; + cdx_int32 infoVersion; + void *info;//VideoInfo/AudioInfo/SubtitleInfo +}; + +//#define PARSER_METADATA_ORGINAL_URI "parser.orginalUri" +//#define PARSER_METADATA_REDIRECT_URI "parser.redirectUri" +//#define PARSER_METADATA_ACCESSIBLE_URI "parser.accessibleUri" + +struct ParserCacheStateS +{ + cdx_int32 nCacheCapacity; + cdx_int32 nCacheSize; + cdx_int32 nBandwidthKbps; + cdx_int32 nPercentage; /* current_caching_positioin / duration */ +}; + +struct StreamSeekPos +{ + cdx_int64 pos; + cdx_int64 time; + cdx_int64 startTime; +}; + +enum EPARSERNOTIFY //* notify. +{ + PARSER_NOTIFY_VIDEO_STREAM_CHANGE = PARSER_NOTIFY_VALID_RANGE_MIN, + PARSER_NOTIFY_AUDIO_STREAM_CHANGE, + PARSER_NOTIFY_TIMESHIFT_END_INFO, + PARSER_NOTIFY_META_DATA, + PARSER_NOTIFY_MAX, +}; +CHECK_PARSER_NOTIFY_MAX_VALID(PARSER_NOTIFY_MAX) + +enum CdxParserCommandE +{ + CDX_PSR_CMD_SWITCH_AUDIO, + CDX_PSR_CMD_SWITCH_SUBTITLE, + + CDX_PSR_CMD_DISABLE_AUDIO, + CDX_PSR_CMD_DISABLE_VIDEO, + CDX_PSR_CMD_DISABLE_SUBTITLE, + + CDX_PSR_CMD_SET_DURATION, + CDX_PSR_CMD_REPLACE_STREAM, + CDX_PSR_CMD_SET_LASTSEGMENT_FLAG, + CDX_PSR_CMD_CLR_INFO, + + CDX_PSR_CMD_STREAM_SEEK, + CDX_PSR_CMD_GET_CACHESTATE, + + CDX_PSR_CMD_SET_FORCESTOP, + CDX_PSR_CMD_CLR_FORCESTOP, + CDX_PSR_CMD_UPDATE_PARSER, + CDX_PSR_CMD_SET_HDCP, + CDX_PSR_CMD_SET_SECURE_BUFFER_COUNT, + CDX_PSR_CMD_SET_SECURE_BUFFERS, + CDX_PSR_CMD_GET_STREAM_EXTRADATA, + +#if LIVEMODE_VIDEO + // get the shiftTimeUrl for cmcc livemode1&2 seekTo + CDX_PSR_CMD_GET_REDIRECT_URL, +#endif + + // for cmcc get the stream url in LogRecorder + CDX_PSR_CMD_GET_URL, + // get the statusCode of http + CDX_PSR_CMD_GET_STREAM_STATUSCODE, + // for Ali YUNOS get TS info + CDX_PSR_CMD_GET_TS_M3U_BANDWIDTH, + CDX_PSR_CMD_GET_TS_SEQ_NUM, + CDX_PSR_CMD_GET_TS_LENGTH, + CDX_PSR_CMD_GET_TS_DURATION, + CDX_PSR_CMD_SET_YUNOS_UUID, + + // for cmcc timeShift set lastSeqNum + CDX_PSR_CMD_SET_TIMESHIFT_LAST_SEQNUM, + + // parser and stream use the same setCallback cmd, + // the code below must be the end of this structure + CDX_PSR_CMD_SET_CALLBACK = STREAM_CMD_SET_CALLBACK, +}; + +enum CdxParserStatusE +{ + PSR_OK, + PSR_INVALID, + PSR_OPEN_FAIL, + PSR_IO_ERR, + PSR_USER_CANCEL, + PSR_INVALID_OPERATION, + PSR_UNKNOWN_ERR, + PSR_EOS, + /*other error info*/ +}; + +#define AUDIO_STREAM_LIMIT 1 +#define VIDEO_STREAM_LIMIT 1 +#define SUBTITLE_STREAM_LIMIT 32 + +#if VIDEO_SUPPORT +struct VideoInfo +{ + cdx_atomic_t ref; + cdx_int32 videoNum; + VideoStreamInfo video[VIDEO_STREAM_LIMIT]; +}; +#endif + +struct AudioInfo +{ + cdx_atomic_t ref; + cdx_int32 audioNum; + AudioStreamInfo audio[AUDIO_STREAM_LIMIT]; +}; + +#if SUBTITLE_SUPPORT +struct SubtitleInfo +{ + cdx_atomic_t ref; + cdx_int32 subtitleNum; + SubtitleStreamInfo subtitle[SUBTITLE_STREAM_LIMIT]; +}; +#endif + +struct CdxProgramS +{ + cdx_int32 id; + cdx_uint32 flags; + cdx_uint32 duration; //unit: ms + + cdx_int32 audioNum, audioIndex; + AudioStreamInfo audio[AUDIO_STREAM_LIMIT]; + cdx_uint64 audioIndexMask; + +#if VIDEO_SUPPORT + cdx_int32 videoNum, videoIndex; + VideoStreamInfo video[VIDEO_STREAM_LIMIT]; + cdx_uint64 videoIndexMask; +#endif + +#if SUBTITLE_SUPPORT + cdx_int32 subtitleNum, subtitleIndex; + SubtitleStreamInfo subtitle[SUBTITLE_STREAM_LIMIT]; +#endif + + cdx_int64 firstPts; + cdx_int32 metadataNum; +}; + +#define PROGRAM_LIMIT 1 //no switch program interface now, so limit 1 + +struct CdxMediaInfoS +{ + cdx_int64 fileSize; + cdx_uint32 bitrate; + cdx_bool bSeekable; + cdx_int32 programNum, programIndex; + + struct CdxProgramS program[PROGRAM_LIMIT]; + + void *privData; + +/* cdx_uint8 album[64]; + cdx_int32 albumsz; + + cdx_uint8 author[64]; + cdx_int32 authorsz; + + cdx_uint8 genre[64]; + cdx_int32 genresz; + + cdx_uint8 title[64]; + cdx_int32 titlesz; + + cdx_uint8 year[64]; + cdx_int32 yearsz; + + cdx_uint8 composer[64]; + cdx_int32 composersz; + + cdx_uint8 date[64]; + cdx_int32 datesz; + + cdx_uint8 artist[64]; + cdx_int32 artistsz; + + cdx_uint8 writer[64]; + cdx_int32 writersz; + + cdx_uint8 albumArtist[64]; + cdx_int32 albumArtistsz; + + cdx_uint8 compilation[64]; + cdx_int32 compilationsz; + + cdx_uint8 cdTrackNumber[64]; + cdx_int32 cdTrackNumbersz;*/ + + cdx_uint8 location[64]; + cdx_uint8 rotate[4]; + cdx_int32 discNumber; + cdx_uint8 *pAlbumArtBuf; + cdx_int32 nAlbumArtBufSize; + + int id3v2HadParsed; +}; + +#define MUTIL_AUDIO 0x0001U /*will disable switch audio*/ +//#define MUTIL_VIDEO 0x0002U /*will disable switch video*/ +#define MUTIL_SUBTITLE 0x0004U /*will disable switch subtitle*/ + +#define DISABLE_AUDIO 0x0008U +#define DISABLE_VIDEO 0x0010U +#define DISABLE_SUBTITLE 0x0020U +#define NO_NEED_DURATION 0x0040U + +#define BD_BASE 0x0080U +#define BD_DEPENDENT 0x0100U +#define BD_TXET 0x0180U + +// for DASH +#define SEGMENT_MP4 0x0200U + +// for mms playlist +#define NO_NEED_CLOSE_STREAM 0x0400U +// for miracast +#define MIRACST 0x0800U + +// for smooth streaming +#define SEGMENT_SMS 0x1000U + +// for cmcc timeShift +#define CMCC_TIME_SHIFT 0x2000U + +#define NOT_LASTSEGMENT 0x4000U + +// for playready sstr +#define SEGMENT_PLAYREADY 0x8000U + +#define MutilAudioStream(flags) (!!(flags & MUTIL_AUDIO)) + +typedef struct HdcpOpsS HdcpOps; +struct HdcpOpsS +{ + int (*init)(void **); + void (*deinit)(void *); + cdx_uint32 (*decrypt)(void *handle, const cdx_uint8 privateData[16], + const cdx_uint8 *in, cdx_uint8 *out, cdx_uint32 len, int streamType); +}; +typedef struct DownloadObjectS DownloadObject; +struct DownloadObjectS +{ + int seqNum; + char *uri; + int statusCode; //http statusCode + cdx_int64 seqSize; //segment size, byte + cdx_int64 seqDuration;//segment duration, us + cdx_int64 spendTime; //ms + cdx_int64 rate; //bps +}; + +typedef struct TimeShiftEndInfoS TimeShiftEndInfoT; +struct TimeShiftEndInfoS +{ + int timeShiftLastSeqNum; + cdx_int64 timeShiftDuration; +}; + +struct CdxParserCreatorS +{ + CdxParserT *(*create)(CdxStreamT *, cdx_uint32 /*flags*/); + cdx_uint32 (*probe)(CdxStreamProbeDataT *);/*return score(0-100)*/ +}; + +struct CdxParserOpsS +{ + cdx_int32 (*control)(CdxParserT *, cdx_int32 /* cmd */, void * /* param */); + + cdx_int32 (*prefetch)(CdxParserT *, CdxPacketT * /* pkt */); + + cdx_int32 (*read)(CdxParserT *, CdxPacketT * /* pkt */); + + cdx_int32 (*getMediaInfo)(CdxParserT *, CdxMediaInfoT * /* MediaInfo */); + + cdx_int32 (*seekTo)(CdxParserT *, cdx_int64 /* timeUs */); + + cdx_uint32 (*attribute)(CdxParserT *); /*return falgs define as open's falgs*/ + + cdx_int32 (*getStatus)(CdxParserT *); /*return enum CdxPrserStatusE*/ + + cdx_int32 (*close)(CdxParserT *); + + cdx_int32 (*init)(CdxParserT *); +}; + +struct CdxParserS +{ + enum CdxParserTypeE type; + struct CdxParserOpsS *ops; +}; + +struct ParserUriKeyInfoS +{ + const char *comment; + const char *scheme[10]; + const char *suffix[10]; + const char *attr[10]; +}; + +#ifdef __cplusplus +extern "C" +{ +#endif + +int AwParserRegister(CdxParserCreatorT *creator, CdxParserTypeT type, + struct ParserUriKeyInfoS *keyInfo); + +int CdxParserPrepare(CdxDataSourceT *source, cdx_uint32 flags, pthread_mutex_t *mutex, + cdx_bool *exit, CdxParserT **parser, CdxStreamT **stream, + ContorlTask *parserTasks, ContorlTask *streamTasks); +CdxParserT *CdxParserCreate(CdxStreamT *, cdx_uint32 /*flags*/); +int CdxParserOpen(CdxStreamT *stream, cdx_uint32 flags, pthread_mutex_t *mutex, cdx_bool *exit, + CdxParserT **parser, ContorlTask *parserTasks); +#define CdxParserForceStop(parser) \ + (CdxParserControl(parser, CDX_PSR_CMD_SET_FORCESTOP, NULL)) + +#define CdxParserClrForceStop(parser) \ + (CdxParserControl(parser, CDX_PSR_CMD_CLR_FORCESTOP, NULL)) + +static inline cdx_int32 CdxParserControl(CdxParserT *parser, cdx_int32 cmd, void *param) +{ + CDX_CHECK(parser); + CDX_CHECK(parser->ops); + CDX_CHECK(parser->ops->control); + return parser->ops->control(parser, cmd, param); +} + +static inline cdx_int32 CdxParserPrefetch(CdxParserT *parser, CdxPacketT *pkt) +{ + CDX_CHECK(parser); + CDX_CHECK(parser->ops); + CDX_CHECK(parser->ops->prefetch); + return parser->ops->prefetch(parser, pkt); +} + +static inline cdx_int32 CdxParserRead(CdxParserT *parser, CdxPacketT *pkt) +{ + CDX_CHECK(parser); + CDX_CHECK(parser->ops); + CDX_CHECK(parser->ops->read); + return parser->ops->read(parser, pkt); +} + +static void PrintMediaInfo(CdxMediaInfoT *mediaInfo) +{ + CDX_LOGD("*********PrintMediaInfo begin*********"); + struct CdxProgramS *program = &mediaInfo->program[0]; + + CDX_LOGD("fileSize = %lld, " + "bSeekable = %d, " + "duration = %d, " + "audioNum = %d, " +#if VIDEO_SUPPORT + "videoNum = %d, " +#endif +#if SUBTITLE_SUPPORT + "subtitleNum = %d " +#endif + , + mediaInfo->fileSize, + mediaInfo->bSeekable, + program->duration, + program->audioNum +#if VIDEO_SUPPORT + ,program->videoNum +#endif +#if SUBTITLE_SUPPORT + ,program->subtitleNum +#endif + ); + + int i; +#if VIDEO_SUPPORT + for (i = 0; i < VIDEO_STREAM_LIMIT && i < program->videoNum; i++) + { + VideoStreamInfo *video = program->video + i; + CDX_LOGD("***Video[%d]*** " + "eCodecFormat = 0x%x, " + "nWidth = %d, " + "nHeight = %d, " + "nFrameRate = %d, " + "nFrameDuration = %d, " + "bIs3DStream = %d ", + i, + video->eCodecFormat, + video->nWidth, + video->nHeight, + video->nFrameRate, + video->nFrameDuration, + video->bIs3DStream); + + } +#endif + + for (i = 0; i < AUDIO_STREAM_LIMIT && i < program->audioNum; i++) + { + AudioStreamInfo *audio = program->audio + i; + CDX_UNUSE(audio); + CDX_LOGD("***Audio[%d]*** " + "eCodecFormat = 0x%x, " + "eSubCodecFormat = 0x%x, " + "nChannelNum = %d, " + "nBitsPerSample = %d, " + "nSampleRate = %d ", + i, + audio->eCodecFormat, + audio->eSubCodecFormat, + audio->nChannelNum, + audio->nBitsPerSample, + audio->nSampleRate); + + } + +#if SUBTITLE_SUPPORT + for (i = 0; i < SUBTITLE_STREAM_LIMIT && i < program->subtitleNum; i++) + { + SubtitleStreamInfo *subtitle = program->subtitle + i; + + CDX_LOGD("***Subtitle[%d]*** " + "eCodecFormat = 0x%x, " + "strLang = (%s) ", + i, + subtitle->eCodecFormat, + subtitle->strLang); + } +#endif + + CDX_LOGD("*********PrintMediaInfo end*********"); +} + +#ifdef __ANDROID__ +#define ACODEC "ro.codec.audio." +#define VCODEC "ro.codec.video." +static int AudioCodecIsForbidden(int eCodecFormat) +{ + char value[PROP_VALUE_MAX] = {0}; + switch (eCodecFormat) + { + case AUDIO_CODEC_FORMAT_AC3: + case AUDIO_CODEC_FORMAT_DTS: + property_get(ACODEC "dts", value, "0"); + if (!strcmp(value, "off")) + return 1; + break; + case AUDIO_CODEC_FORMAT_COOK: + property_get(ACODEC "cook", value, "0"); + if (!strcmp(value, "off")) + return 1; + break; + case AUDIO_CODEC_FORMAT_WMA_STANDARD: + case AUDIO_CODEC_FORMAT_WMA_LOSS: + case AUDIO_CODEC_FORMAT_WMA_PRO: + property_get(ACODEC "wma", value, "0"); + if (!strcmp(value, "off")) + return 1; + break; + default: + break; + } + + return 0; +} + +#if VIDEO_SUPPORT +static int VideoCodecIsForbidden(int eCodecFormat) +{ + char value[PROP_VALUE_MAX] = {0}; + switch (eCodecFormat) + { + case VIDEO_CODEC_FORMAT_DIVX5: + property_get(VCODEC "divx5", value, "0"); + if (!strcmp(value, "off")) + return 1; + break; + case VIDEO_CODEC_FORMAT_RX: + property_get(VCODEC "rx", value, "0"); + if (!strcmp(value, "off")) + return 1; + break; + case VIDEO_CODEC_FORMAT_WMV3: + property_get(VCODEC "vc-1", value, "0"); + if (!strcmp(value, "off")) + return 1; + break; + default: + break; + } + + return 0; +} +#endif + +static int FilterForbiddenStream(CdxMediaInfoT *mediaInfo) +{ + struct CdxProgramS *program = &mediaInfo->program[0]; + int i; + + int videoNum = program->videoNum; + program->videoIndexMask = 0; + for (i = 0; i < videoNum && i < VIDEO_STREAM_LIMIT; i++) + { + VideoStreamInfo *video = program->video + i; + if (VideoCodecIsForbidden(video->eCodecFormat)) + { + logw("video codec %d is forbidden", video->eCodecFormat); + program->videoIndexMask |= 1 << i; + } + } + + int audioNum = program->audioNum; + program->audioIndexMask = 0; + for (i = 0; i < audioNum && i < AUDIO_STREAM_LIMIT; i++) + { + AudioStreamInfo *audio = program->audio + i; + if (AudioCodecIsForbidden(audio->eCodecFormat)) + { + logw("audio codec %d is forbidden", audio->eCodecFormat); + /* Todo: forbidden the evil one, not anyone */ + program->audioIndexMask |= 1 << i; + } + } + + return 0; +} +#endif + +/** + * Get the information of the media from the parser. + * + * @param parser A parser which is prepared. + * @param mediaInfo A struct hold the information of the media. + * + * @return 0 for success. + * + * @warning Don't allocate memory for member variables like pCodecSpecificData + * of mediaInfo in the parser's implementation of this interface. + * pCodecSpecificData won't be freed after the function return, so the + * memory will lose if you do that. Just set pCodecSpecificData point + * to the area which will be freed by the parser, for example, when + * the parser is closed. + */ +static inline cdx_int32 CdxParserGetMediaInfo(CdxParserT *parser, CdxMediaInfoT *mediaInfo) +{ + int ret; + CDX_CHECK(parser); + CDX_CHECK(parser->ops); + CDX_CHECK(parser->ops->getMediaInfo); + ret = parser->ops->getMediaInfo(parser, mediaInfo); + PrintMediaInfo(mediaInfo); +#ifdef __ANDROID__ + FilterForbiddenStream(mediaInfo); +#endif + return ret; +} + +static inline cdx_int32 CdxParserSeekTo(CdxParserT *parser, cdx_int64 timeUs) +{ + CDX_CHECK(parser); + CDX_CHECK(parser->ops); + CDX_CHECK(parser->ops->seekTo); + return parser->ops->seekTo(parser, timeUs); +} + +static inline cdx_uint32 CdxParserAttribute(CdxParserT *parser) +{ + CDX_CHECK(parser); + CDX_CHECK(parser->ops); + CDX_CHECK(parser->ops->attribute); + return parser->ops->attribute(parser); +} + +static inline cdx_int32 CdxParserGetStatus(CdxParserT *parser) +{ + CDX_CHECK(parser); + CDX_CHECK(parser->ops); + CDX_CHECK(parser->ops->getStatus); + return parser->ops->getStatus(parser); +} + +static inline cdx_int32 CdxParserClose(CdxParserT *parser) +{ + CDX_CHECK(parser); + CDX_CHECK(parser->ops); + CDX_CHECK(parser->ops->close); + return parser->ops->close(parser); +} + +static inline cdx_int32 CdxParserInit(CdxParserT *parser) +{ + CDX_CHECK(parser); + CDX_CHECK(parser->ops); + CDX_CHECK(parser->ops->init); + return parser->ops->init(parser); +} +enum { + PROBE_SPECIFIC_DATA_ERROR =-3, + PROBE_SPECIFIC_DATA_NONE =-2, + PROBE_SPECIFIC_DATA_UNCOMPELETE = -1, + PROBE_SPECIFIC_DATA_SUCCESS = 1, +}; + +#if VIDEO_SUPPORT +cdx_int32 ProbeVideoSpecificData(VideoStreamInfo* pVideoInfo, cdx_uint8* pDataBuf, + cdx_uint32 dataLen, cdx_uint32 eVideoCodecFormat, enum CdxParserTypeE type); +cdx_int32 probeH265RefPictureNumber(cdx_uint8* pDataBuf, cdx_uint32 nDataLen); +#endif + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/platform/mcu/xr871/src/cedarx/parser/mp3/CdxMp3Parser.h b/platform/mcu/xr871/src/cedarx/parser/mp3/CdxMp3Parser.h new file mode 100644 index 0000000000..2f011d0bc7 --- /dev/null +++ b/platform/mcu/xr871/src/cedarx/parser/mp3/CdxMp3Parser.h @@ -0,0 +1,179 @@ +/* +* Copyright (c) 2008-2016 Allwinner Technology Co. Ltd. +* All rights reserved. +* +* File : CdxMp3Parser.h +* Description : +* History : +* Author : Khan +* Date : 2014/08/08 +*/ + +#ifndef _CDX_MP3_PARSER_H_ +#define _CDX_MP3_PARSER_H_ + +#include "Id3Base.h" + +#define ENABLE_INFO_DEBUG 1 +#define ENABLE_FILE_DEBUG 0 +#define AV_TIME_BASE 1000000 + +#define MP3_PROBE_SIZE 10 * 1024 +#define MP3_PACKET_SIZE 1024 +#define AVPROBE_SCORE_MAX 100 + +#define ID3v2_HEADER_SIZE 10 +#define MPA_STEREO 0 +#define MPA_JSTEREO 1 +#define MPA_DUAL 2 +#define MPA_MONO 3 + +#define INFLEN 1024*8 +#define ID3TAGNUM 8 +#define SKIPLEN 1024 + +typedef enum mp3_sta_ { + MP3_STA_IDLE, + MP3_STA_SEEKING, + MP3_STA_PREFETCHING, + MP3_STA_READING, + MP3_STA_INITING, +}mp3_sta; + +#define MKTAG(a,b,c,d) ((a) | ((b) << 8) | ((c) << 16) | ((unsigned)(d) << 24)) + +const uint16_t CdxMpaFreqTab[3] = { 44100, 48000, 32000 }; + +const uint16_t CdxMpaBitrateTab[2][3][15] = { + { {0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448 }, + {0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384 }, + {0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320 } }, + { {0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256}, + {0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160}, + {0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160} + } +}; + +#define MPADECODEHEADER \ + int mFrameSize; \ + int errorProtection; \ + int layer; \ + int mSampleRate; \ + int mSampleRateIndex; /* between 0 and 8 */ \ + int mBitRate; \ + int nChannels; \ + int mode; \ + int modeExt; \ + int lsf; + +typedef struct MPADecodeHeader { + MPADECODEHEADER +} MPADecodeHeader; +#if 0 +static int CdxId3v2Match(const cdx_uint8 *buf) +{ + return ('I' == buf[0]) && + ('D' == buf[1]) && + ('3' == buf[2]) && + (0xff != buf[3]) && + (0xff != buf[4]) && + (0 == (buf[6] & 0x80)) && + (0 == (buf[7] & 0x80)) && + (0 == (buf[8] & 0x80)) && + (0 == (buf[9] & 0x80)); +} + +static int CdxId3v2TagLen(const uint8_t * buf) +{ + int len = ((buf[9] & 0x7f) + + ((buf[6] & 0x7f) << 21) + + ((buf[7] & 0x7f) << 14) + + (buf[8] & 0x7f) << 7) + + ID3v2_HEADER_SIZE; + + if (buf[5] & 0x10) + len += ID3v2_HEADER_SIZE; + return len; +} +#endif + +typedef struct __language_coding +{ + char language[4]; + cdx_int32 coding; +}anguage_coding_s; + +typedef enum { + IMG_FORMAT_BMP =0, + IMG_FORMAT_JPG, + IMG_FORMAT_GIF, + IMG_FORMAT_PNG, + IMG_FORMAT_UNSUPPORT = -1 +}img_format_t; + +typedef struct CDX_ID3_IMAGE_INFO +{ + int length; //image length + int FileLocation; //image location + img_format_t img_format; + int pic_type; //picture type; + int img_high; + int img_width; + int otherdata; + +}cdx_id3_image_info_t; + +typedef struct XINGSeeker { + int64_t mFirstFramePos; + int64_t mDurationUs; + int32_t mSizeBytes; + int32_t mEncoderDelay; + int32_t mEncoderPadding; + + // TOC entries in XING header. Skip the first one since it's always 0. + unsigned char mTOC[99]; + cdx_bool mTOCValid; +}XINGSeeker; + +typedef struct VBRISeeker { + int64_t mBasePos; + int64_t mDurationUs; + cdx_uint32 mSegmentsize; + cdx_uint32 mSegments[1024]; +}VBRISeeker; + +typedef struct MP3ParserImpl{ + // Cdx Struct + CdxParserT base; + CdxStreamT *stream; + + int mStatus; + int mErrno; // errno + int exitFlag; + + pthread_cond_t cond; + + XINGSeeker *mXINGSeeker; + VBRISeeker *mVBRISeeker; + + cdx_int64 mFileSize; + cdx_uint64 mFirstFramePos; + cdx_uint32 mFixedHeader; + int64_t mCurrentPos; + cdx_uint16 mChannels; + cdx_uint32 mSampleRate; + cdx_int32 mBitRate; + cdx_int32 mavgBitRate; + cdx_uint32 mFrameSize; + cdx_int32 readPacketSize; + /*Duration*/ + cdx_uint64 mDuration; + + cdx_uint32 mSeeking; + cdx_int64 mSeekingTime; + int teeFd; + + ID3* id3v1; +} MP3ParserImpl; + +#endif diff --git a/platform/mcu/xr871/src/cedarx/playback/audioDecComponent.h b/platform/mcu/xr871/src/cedarx/playback/audioDecComponent.h new file mode 100644 index 0000000000..d31342b030 --- /dev/null +++ b/platform/mcu/xr871/src/cedarx/playback/audioDecComponent.h @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2008-2016 Allwinner Technology Co. Ltd. + * All rights reserved. + * + * File : audioDecComponent.h + * Description : audio decoder component + * History : + * + */ + +#ifndef AUDIO_DECODE_COMPONENT +#define AUDIO_DECODE_COMPONENT + +#include "player_i.h" +#include "avtimer.h" +#include "adecoder.h" + +typedef struct AUDIOSTREAMDATAINFO +{ + char* pData; + int nLength; + int64_t nPts; + int64_t nPcr; + int bIsFirstPart; + int bIsLastPart; +}AudioStreamDataInfo; + +typedef struct AudioDecComp AudioDecComp; + +AudioDecComp* AudioDecCompCreate(void); + +int AudioDecCompDestroy(AudioDecComp* a); + +int AudioDecCompStart(AudioDecComp* a); + +int AudioDecCompStop(AudioDecComp* a); + +int AudioDecCompPause(AudioDecComp* a); + +enum EPLAYERSTATUS AudioDecCompGetStatus(AudioDecComp* a); + +int AudioDecCompReset(AudioDecComp* a, int64_t nSeekTime); + +int AudioDecCompSetEOS(AudioDecComp* a); + +int AudioDecCompSetCallback(AudioDecComp* a, PlayerCallback callback, void* pUserData); + +int AudioDecCompSetAudioStreamInfo(AudioDecComp* a, + AudioStreamInfo* pStreamInfo, + int nStreamCount, + int nDefaultStreamIndex); + +int AudioDecCompAddAudioStream(AudioDecComp* a, AudioStreamInfo* pStreamInfo); + +int AudioDecCompGetAudioStreamCnt(AudioDecComp* a); + +int AudioDecCompCurrentStreamIndex(AudioDecComp* a); + +int AudioDecCompGetAudioStreamInfo(AudioDecComp* a, int* pStreamNum, + AudioStreamInfo** ppStreamInfo); + +int AudioDecCompGetAudioSampleRate(AudioDecComp* a, unsigned int* pSampleRate, + unsigned int* pChannelNum, unsigned int* pBitRate); + +int AudioDecCompSetTimer(AudioDecComp* a, AvTimer* timer); + +int AudioDecCompRequestStreamBuffer(AudioDecComp* a, + int nRequireSize, + char** ppBuf, + int* pBufSize, + char** ppRingBuf, + int* pRingBufSize, + int nStreamIndex); + +int AudioDecCompSubmitStreamData(AudioDecComp* a, + AudioStreamDataInfo* pDataInfo, + int nStreamIndex); + +int AudioDecCompStreamBufferSize(AudioDecComp* a, int nStreamIndex); + +int AudioDecCompStreamDataSize(AudioDecComp* a, int nStreamIndex); + +int AudioDecCompStreamFrameNum(AudioDecComp* a, int nStreamIndex); + +int AudioDecCompRequestPcmData(AudioDecComp* a, + unsigned char** ppData, + unsigned int* pSize, + int64_t* pPts, + CdxPlaybkCfg* cfg); + +int AudioDecCompReleasePcmData(AudioDecComp* a, int nReleaseSize); + +int AudioDecCompPcmDataSize(AudioDecComp* a, int nStreamIndex); + +int AudioDecCompSwitchStream(AudioDecComp* a, int nStreamIndex); + +void AudioDecRawSendCmdToHalClbk(void *pself,void *param); + +#endif diff --git a/platform/mcu/xr871/src/cedarx/playback/audioRenderComponent.h b/platform/mcu/xr871/src/cedarx/playback/audioRenderComponent.h new file mode 100644 index 0000000000..10c7aa6638 --- /dev/null +++ b/platform/mcu/xr871/src/cedarx/playback/audioRenderComponent.h @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2008-2016 Allwinner Technology Co. Ltd. + * All rights reserved. + * + * File : audioRenderComponent.h + * Description : audio render component + * History : + * + */ + +#ifndef AUDIO_RENDER_H +#define AUDIO_RENDER_H + +#include "player_i.h" +#include "audioDecComponent.h" +#include "soundControl.h" + +typedef struct AudioRenderComp AudioRenderComp; + +AudioRenderComp* AudioRenderCompCreate(void); + +int AudioRenderCompDestroy(AudioRenderComp* a); + +int AudioRenderCompStart(AudioRenderComp* a); + +int AudioRenderCompStop(AudioRenderComp* a); + +int AudioRenderCompPause(AudioRenderComp* a); + +enum EPLAYERSTATUS AudioRenderCompGetStatus(AudioRenderComp* a); + +int AudioRenderCompReset(AudioRenderComp* a); + +int AudioRenderCompSetEOS(AudioRenderComp* a); + +int AudioRenderCompSetCallback(AudioRenderComp* a, PlayerCallback callback, void* pUserData); + +int AudioRenderCompSetTimer(AudioRenderComp* a, AvTimer* timer); + +int AudioRenderCompSetAudioSink(AudioRenderComp* a, SoundCtrl* pAudioSink); + +int AudioRenderCompSetDecodeComp(AudioRenderComp* a, AudioDecComp* d); + +int64_t AudioRenderCompCacheTimeUs(AudioRenderComp* a); + +int AudioRenderCompGetAudioOutBalance(AudioRenderComp* a); + +//* set audio balance, 1 means left channel, 2 means right channel, 3 means stereo. +int AudioRenderCompSetAudioOutBalance(AudioRenderComp* a, int nBalance); + +//* set audio mute, bMute = 1 means mute audio, 0 means resume from mute. +int AudioRenderCompSetAudioMute(AudioRenderComp* a, int bMute); + +//* get the audio mute setting, 1 means audio muted, 0 means not muted. +int AudioRenderCompGetAudioMuteFlag(AudioRenderComp* a); + +int AudioRenderCompSetForceWriteToDeviceFlag(AudioRenderComp* a, int bForceFlag); + +int AudioRenderCompSetPlaybackRate(AudioRenderComp* a,const XAudioPlaybackRate* rate); + +#endif diff --git a/platform/mcu/xr871/src/cedarx/playback/avtimer.h b/platform/mcu/xr871/src/cedarx/playback/avtimer.h new file mode 100644 index 0000000000..ba1b6a71e9 --- /dev/null +++ b/platform/mcu/xr871/src/cedarx/playback/avtimer.h @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2008-2016 Allwinner Technology Co. Ltd. + * All rights reserved. + * + * File : avtimer.h + * Description : audio-video synchronization timer + * History : + * + */ + +#ifndef AVTIMER_H +#define AVTIMER_H + +#include + +//************************************************************// +//***************** define status of a timer *****************// +//************************************************************// +#define TIMER_STATUS_START 0x55 +#define TIMER_STATUS_STOP 0xaa + +typedef struct AvTimer AvTimer; +struct AvTimer +{ + //* set clock counting speed. + int (*SetSpeed)(AvTimer* t, int speed); + + //* get the current counting speed. + int (*GetSpeed)(AvTimer* t); + + //* set time. + int (*SetTime)(AvTimer* t, int64_t time); + + //* get the current time. + int64_t (*GetTime)(AvTimer* t); + + //* trasform pts to system time + //* Careful: this interface should only be used by video render + int64_t (*PtsToSystemTime)(AvTimer* t, int64_t pts); + + //* run the timer, start clock counting at 'start_time' and running at speed 'speed'. + int (*Start)(AvTimer* t); + + //* stop the timer. + void (*Stop)(AvTimer* t); + + int (*GetStatus)(AvTimer* t); + + int (*SetPlayRate)(AvTimer* t, float rate); + +}; + +//* create a timer, which is implemented in file 'avtimer.c'. +AvTimer* AvTimerCreate(void); + +void AvTimerDestroy(AvTimer*); + +#endif diff --git a/platform/mcu/xr871/src/cedarx/playback/baseComponent.h b/platform/mcu/xr871/src/cedarx/playback/baseComponent.h new file mode 100644 index 0000000000..410c7ef499 --- /dev/null +++ b/platform/mcu/xr871/src/cedarx/playback/baseComponent.h @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2008-2016 Allwinner Technology Co. Ltd. + * All rights reserved. + * + * File : baseComponent.h + * Description : base component + * History : + * Date : 2016/08/03 + * Comment : first version + */ + +#ifndef BASE_COMPONENT_H +#define BASE_COMPONENT_H + +#include +//#include +#include "pthread.h" +#include + +struct AwMessage { + AWMESSAGE_COMMON_MEMBERS + + sem_t *replySem; + int *result; + union { + int64_t seekTime; + void *opaque; + int64_t int64Value; + }; +}; + +typedef struct BaseMsgHandler { + msgHandlerT start; + msgHandlerT stop; + msgHandlerT pause; + msgHandlerT reset; + msgHandlerT setEos; + msgHandlerT quit; + union { + msgHandlerT continueWork; + msgHandlerT decode; + msgHandlerT encode; + msgHandlerT mux; + msgHandlerT render; + }; +} BaseMsgHandler; + +enum MESSAGE_ID { + MESSAGE_ID_START = 0, + MESSAGE_ID_STOP = 1, + MESSAGE_ID_PAUSE = 2, + MESSAGE_ID_QUIT = 3, + MESSAGE_ID_RESET = 4, + MESSAGE_ID_EOS = 5, + + /* continue decoder/encoder/mux/render */ + MESSAGE_ID_CONTINUE = 6, + + MESSAGE_ID_SET_WINDOW, + MESSAGE_ID_SET_3D_MODE, + MESSAGE_ID_SET_AUDIO_SINK, + MESSAGE_ID_SET_VIDEO_HIDE, + MESSAGE_ID_SET_HOLD_LAST_PICTURE, + MESSAGE_ID_SET_TIMESHIFT, + MESSAGE_ID_SET_LAYER_INFO, + MESSAGE_ID_SET_SUB_RENDER, + MESSAGE_ID_SET_DI, +}; + +typedef struct BaseCompCtx { + /** + * It should point to a string literal. + */ + const char *name; + + /** + * This mq should be initialized before call BaseCompInit() + */ + AwMessageQueue *mq; + + /* continueWork doesn't need a reply */ + sem_t replySem[MESSAGE_ID_EOS + 1]; + BaseMsgHandler handler; +} BaseCompCtx; + +int BaseCompInit(BaseCompCtx *p, const char *name, + AwMessageQueue *mq, BaseMsgHandler *handler); + +void BaseCompDestroy(BaseCompCtx *p); + +typedef void (*task_t)(void *); + +int BaseCompStart(BaseCompCtx *p, task_t afterPostBeforeWait, void *arg); + +int BaseCompStop(BaseCompCtx *p, task_t afterPostBeforeWait, void *arg); + +int BaseCompPause(BaseCompCtx *p, task_t afterPostBeforeWait, void *arg); + +int BaseCompReset(BaseCompCtx *p, int64_t nSeekTime, + task_t afterPostBeforeWait, void *arg); + +int BaseCompSetEos(BaseCompCtx *p, task_t afterPostBeforeWait, void *arg); + +int BaseCompQuit(BaseCompCtx *p, task_t afterPostBeforeWait, void *arg); + +int BaseCompContinue(BaseCompCtx *p); + +#endif diff --git a/platform/mcu/xr871/src/cedarx/playback/bitrateEstimater.h b/platform/mcu/xr871/src/cedarx/playback/bitrateEstimater.h new file mode 100644 index 0000000000..85ecea049f --- /dev/null +++ b/platform/mcu/xr871/src/cedarx/playback/bitrateEstimater.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2008-2016 Allwinner Technology Co. Ltd. + * All rights reserved. + * + * File : bitrateEstimater.h + * Description : bitrate estimater + * History : + * + */ + +#ifndef BITRATE_ESTIMATER_H +#define BITRATE_ESTIMATER_H + +#include +#include +#include + +#define BITRATE_ARRAY_SIZE 50 +#define BITRATE_ESTIMATE_INTERVAL 5 +#define PTS_DISCONTINUE_INTERVAL 1000000 + +typedef struct BR_ESTIMATER_NODE +{ + int64_t nFramePts; + int nFrameLen; +}BitrateEstimateNode; + +typedef struct BitrateEstimater +{ + pthread_mutex_t mutex; + int nBitrate; + int nWritePos; + int nValidNodeCnt; + int nWritePosLastEstimate; + BitrateEstimateNode nodes[BITRATE_ARRAY_SIZE]; +}BitrateEstimater; + +BitrateEstimater* BitrateEstimaterCreate(void); + +void BitrateEstimaterDestroy(BitrateEstimater* be); + +void BitrateEstimaterUpdate(BitrateEstimater* be, int64_t nPts, int nFrameLen); + +int BitrateEstimaterGetBitrate(BitrateEstimater* be); + +void BitrateEstimaterReset(BitrateEstimater* be); + +#endif diff --git a/platform/mcu/xr871/src/cedarx/playback/framerateEstimater.h b/platform/mcu/xr871/src/cedarx/playback/framerateEstimater.h new file mode 100644 index 0000000000..1ef054e445 --- /dev/null +++ b/platform/mcu/xr871/src/cedarx/playback/framerateEstimater.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2008-2016 Allwinner Technology Co. Ltd. + * All rights reserved. + * + * File : framerateEstimater.h + * Description : framerate estimater + * History : + * + */ + +#ifndef FRAME_ESTIMATER_H +#define FRAME_ESTIMATER_H + +#include +#include +#include + +#define FRAMERATE_ARRAY_SIZE 4 +#define FRAMERATE_START_ESTIMATE_SIZE 2 + +typedef struct FramerateEstimater +{ + int64_t nPts[FRAMERATE_ARRAY_SIZE]; + int nWritePos; + int nValidPtsCnt; + int nFrameDuration; + float fPlayRate; + pthread_mutex_t mutex; +}FramerateEstimater; + +FramerateEstimater* FramerateEstimaterCreate(void); + +void FramerateEstimaterDestroy(FramerateEstimater* fe); + +void FramerateEstimaterUpdate(FramerateEstimater* fe, int64_t nPts); + +int FramerateEstimaterGetFramerate(FramerateEstimater* fe); + +int FramerateEstimaterGetFrameDuration(FramerateEstimater* fe); //* in unit of us. + +int FramerateEstimaterSetPlayrate(FramerateEstimater* fe,float rate); + +void FramerateEstimaterReset(FramerateEstimater* fe); + +#endif diff --git a/platform/mcu/xr871/src/cedarx/playback/include/player.h b/platform/mcu/xr871/src/cedarx/playback/include/player.h new file mode 100644 index 0000000000..5c754c0214 --- /dev/null +++ b/platform/mcu/xr871/src/cedarx/playback/include/player.h @@ -0,0 +1,407 @@ +/* + * Copyright (c) 2008-2016 Allwinner Technology Co. Ltd. + * All rights reserved. + * + * File : player.h + * Description : player + * History : + * + */ + +#ifndef PLAYER_H +#define PLAYER_H + +#include "cdx_log.h" +//#include "cdx_config.h" + +#if VIDEO_SUPPORT +#include "vdecoder.h" +#include "layerControl.h" +#include "deinterlace.h" +#endif +#include "adecoder.h" +#include "soundControl.h" +#if SUBTITLE_SUPPORT +#include "sdecoder.h" +#include "subtitleControl.h" +#endif + + +#include + +enum EPLAYERNOTIFY { + PLAYBACK_NOTIFY_EOS = PLAYBACK_NOTIFY_VALID_RANGE_MIN, + + //* param == NULL; + PLAYBACK_NOTIFY_FIRST_PICTURE, + + //* width = ((int*)param)[0]; height = ((int*)param)[1]; + PLAYBACK_NOTIFY_VIDEO_SIZE, + //* leftOffset = ((int*)param)[0; topOffset = ((int*)param)[1]; + //* cropWidth = ((int*)param)[2]; cropHeight = ((int*)param)[3]; + PLAYBACK_NOTIFY_VIDEO_CROP, + + //* subtitle_id = ((unsigned int*)param)[0]; + //* pSubtitleItem = (SubtitleItem*)((unsigned int*)param)[1]; + PLAYBACK_NOTIFY_SUBTITLE_ITEM_AVAILABLE, + //* subtitle_id = (unsigned int)param; + PLAYBACK_NOTIFY_SUBTITLE_ITEM_EXPIRED, + + //* video stream not supported, video decoder crash. + PLAYBACK_NOTIFY_VIDEO_UNSUPPORTED, + //* audio stream not supported, audio decoder crash. + PLAYBACK_NOTIFY_AUDIO_UNSUPPORTED, + //* subtitle stream not supported, subtitle decoder crash. + PLAYBACK_NOTIFY_SUBTITLE_UNSUPPORTED, + PLAYBACK_NOTIFY_AUDIORAWPLAY, + + PLAYBACK_NOTIFY_SET_SECURE_BUFFER_COUNT, + PLAYBACK_NOTIFY_SET_SECURE_BUFFERS, + + PLAYBACK_NOTIFY_AUDIO_INFO, + + PLAYBACK_NOTIFY_VIDEO_RENDER_FRAME, + + PLAYBACK_NOTIFY_MAX, +}; +CHECK_PLAYBACK_NOTIFY_MAX_VALID(PLAYBACK_NOTIFY_MAX) + +enum EPLAYERSTATUS +{ + PLAYER_STATUS_STOPPED = 0, + PLAYER_STATUS_STARTED, + PLAYER_STATUS_PAUSED +}; + +enum EMEDIATYPE +{ + MEDIA_TYPE_VIDEO = 0, + MEDIA_TYPE_AUDIO, + MEDIA_TYPE_SUBTITLE, + MEDIA_TYPE_METADATA +}; + +enum EPICTURE3DMODE +{ + PICTURE_3D_MODE_NONE = 0, + PICTURE_3D_MODE_TWO_SEPERATED_PICTURE, + PICTURE_3D_MODE_SIDE_BY_SIDE, + PICTURE_3D_MODE_TOP_TO_BOTTOM, + PICTURE_3D_MODE_LINE_INTERLEAVE, + PICTURE_3D_MODE_COLUME_INTERLEAVE +}; + +enum EDISPLAY3DMODE +{ + DISPLAY_3D_MODE_2D = 0, + DISPLAY_3D_MODE_3D, + DISPLAY_3D_MODE_HALF_PICTURE +}; + +enum EDISPLAYRATIO +{ + DISPLAY_RATIO_FULL_SCREEN, + DISPLAY_RATIO_LETTERBOX, + //* add new mode. +}; + +typedef struct MEDIASTREAMDATAINFO +{ + char* pData; + int nLength; + int64_t nPts; + int64_t nPcr; + int bIsFirstPart; + int bIsLastPart; + int64_t nDuration; //* in unit of us. + int nStreamChangeFlag; + int nStreamChangeNum; + void *pStreamInfo; +}MediaStreamDataInfo; + +typedef int (*PlayerCallback)(void* pUserData, int eMessageId, void* param); + +typedef void* Player; + + +#ifdef __cplusplus +extern "C" { +#endif + +Player* PlayerCreate(void); + +void PlayerDestroy(Player* pl); + +int PlayerSetCallback(Player* pl, PlayerCallback callback, void* pUserData); + + +//******************************* START **********************************// +//** Play Control APIs. +//** + +int PlayerStart(Player* pl); + +int PlayerStop(Player* pl); //* media stream information is still kept by the player. + +int PlayerPause(Player* pl); + +//* for seek operation, mute be called under paused status. +int PlayerReset(Player* pl, int64_t nSeekTimeUs); + +//* must be called under stopped status, all stream information cleared. +int PlayerClear(Player* pl); + +#if VIDEO_SUPPORT +//* set player playback in fast mode. +//* in fast mode, video is showed directly without any synchronization, and +//* audio is should not send in. +int PlayerFast(Player* pl, int bDecodeKeyframeOnly); + +//* return from fast mode, the player is set to started status when return. +int PlayerStopFast(Player* pl); +#endif + +enum EPLAYERSTATUS PlayerGetStatus(Player* pl); + +int64_t PlayerGetPosition(Player* pl); //* current time positon in us. + +int64_t PlayerGetPositionCMCC(Player* pl); //* current time positon in us. + +int64_t PlayerGetPts(Player* pl); //* current presentation time stamp. + +int PlayerSetDiscardAudio(Player* pl, int f); // for IPTV, discard audi in fast mode + +//******************************** END ***********************************// + +//******************************* START **********************************// +//** Streaming Control APIs. +//** + +int PlayerRequestStreamBuffer(Player* pl, + int nRequireSize, + void** ppBuf, + int* pBufSize, + void** ppRingBuf, + int* pRingBufSize, + enum EMEDIATYPE eMediaType, + int nStreamIndex); + +int PlayerSubmitStreamData(Player* pl, + MediaStreamDataInfo* pDataInfo, + enum EMEDIATYPE eMediaType, + int nStreamIndex); + +int PlayerSetEos(Player* pl); + +int PlayerSetFirstPts(Player* pl, int64_t nFirstPts); + +#if VIDEO_SUPPORT +//* estimated by pts and stream data size. +int PlayerGetVideoBitrate(Player* pl); + +//* estimated by pts. +int PlayerGetVideoFrameRate(Player* pl); + +//* estimated by pts. +int PlayerGetVideoFrameDuration(Player* pl); + +//* how much video stream data in stream buffere. +int PlayerGetVideoStreamDataSize(Player* pl); + +//* the size of video stream buffer +int PlayerGetVideoStreamBufferSize(Player* pl); + +//* how many stream frame in buffer. +int PlayerGetVideoStreamFrameNum(Player* pl); + +//* how many picture has been decoded and waiting to show. +int PlayerGetValidPictureNum(Player* pl); +#endif + +//* estimated by pts and stream data size. +int PlayerGetAudioBitrate(Player* pl); + +//* get audio sample rate, channel count and how many bits per sample of pcm data. +int PlayerGetAudioParam(Player* pl, int* pSampleRate, int* pChannelCount, int* pBitsPerSample); + +//* how much audio stream data in stream buffer. +int PlayerGetAudioStreamDataSize(Player* pl); + +//* how many stream frame in buffer. +int PlayerGetAudioStreamFrameNum(Player* pl); + +//* how much audio pcm data has been decoded to the pcm buffer. +int PlayerGetAudioPcmDataSize(Player* pl); + +//* how much audio pcm data cached in audio device. +int PlayerGetAudioCacheTimeInSoundDevice(Player* pl); + +//******************************** END ***********************************// + +//******************************* START **********************************// +//** Video APIs. +//** +#if VIDEO_SUPPORT +int PlayerSetVideoStreamInfo(Player* pl, VideoStreamInfo* pStreamInfo); + +int PlayerCanSupportVideoStream(Player* pl, VideoStreamInfo* pStreamInfo); + +int PlayerHasVideo(Player* pl); + +int PlayerConfigVideoScaleDownRatio(Player* pl, int nHorizonRatio, int nVerticalRatio); + +int PlayerConfigVideoRotateDegree(Player* pl, int nDegree); + +int PlayerConfigVideoDeinterlace(Player* pl, int bOpenDeinterlace); +int PlayerConfigDispErrorFrame(Player* pl, int bDispErrorFrame); +int PlayerConfigDropDelayFrame(Player* pl, int bDropDelayFrame); +int PlayerConfigTvStreamFlag(Player* pl, int bFlag); +#endif +//******************************** END ***********************************// + +//******************************* START **********************************// +//** Audio APIs. +//** + +int PlayerSetAudioStreamInfo(Player* pl, AudioStreamInfo* pStreamInfo, + int nStreamNum, int nDefaultStream); + +int PlayerAddAudioStream(Player* pl, AudioStreamInfo* pStreamInfo); + +int PlayerCanSupportAudioStream(Player* pl, AudioStreamInfo* pStreamInfo); + +int PlayerGetAudioStreamCnt(Player* pl); + +int PlayerGetAudioStreamInfo(Player* pl, int* pStreamNum, AudioStreamInfo** ppStreamInfo); + +int PlayerHasAudio(Player* pl); + +int PlayerSwitchAudio(Player* pl, int nStreamIndex); + +//hkw switch audio track for IPTV +int PlayerStopAudio(Player* pl); + +int PlayerStartAudio(Player* pl); + +//******************************** END ***********************************// + +//******************************* START **********************************// +//** Subtitle APIs. +//** +#if SUBTITLE_SUPPORT +int PlayerSetSubtitleStreamInfo(Player* pl, SubtitleStreamInfo* pStreamInfo, + int nStreamNum, int nDefaultStream); + +int PlayerAddSubtitleStream(Player* pl, SubtitleStreamInfo* pStreamInfo); + +int PlayerCanSupportSubtitleStream(Player* pl, SubtitleStreamInfo* pStreamInfo); + +int PlayerGetSubtitleStreamCnt(Player* pl); + +/* stream info array is allocated inside, user need to free the memory by + * calling free(*ppStreamInfo); + */ +int PlayerGetSubtitleStreamInfo(Player* pl, int* pStreamNum, SubtitleStreamInfo** ppStreamInfo); + +/* stream info array is allocated inside, user need to free the memory by + * calling free(*ppStreamInfo); + */ +int PlayerProbeSubtitleStreamInfo(const char* strFileName, + int* pStreamNum, + SubtitleStreamInfo** ppStreamInfo); + +/* stream info array is allocated inside, user need to free the memory by + * calling free(*ppStreamInfo); + */ +int PlayerProbeSubtitleStreamInfoFd(int fd, int offset, int len, int* pStreamNum, + SubtitleStreamInfo** ppStreamInfo); + +int PlayerSwitchSubtitle(Player* pl, int nStreamIndex); + +//* adjust subtitle show time, in +int PlayerSetSubtitleShowTimeAdjustment(Player* pl, int nTimeMs); + +//* get the adjustment of subtitle show time, in unit of ms. +int PlayerGetSubtitleShowTimeAdjustment(Player* pl); + +//* set subRender +int PlayerSetSubCtrl(Player* pl, SubCtrl* pSubCtrl); +#endif +//******************************** END ***********************************// + +//******************************* START **********************************// +//** Display Control APIs. +//** +#if VIDEO_SUPPORT +int PlayerSetWindow(Player* pl, LayerCtrl* pLc); + +//* this API is not used in new display +int PlayerSet3DMode(Player* pl, enum EPICTURE3DMODE ePicture3DMode, + enum EDISPLAY3DMODE eDisplay3DMode); + +int PlayerGet3DMode(Player* pl, enum EPICTURE3DMODE* ePicture3DMode, + enum EDISPLAY3DMODE* eDisplay3DMode); + +//* some times you need to know the picture size from the decoder under player. +int PlayerGetPictureSize(Player* pl, int* pWidth, int* pHeight); + +//* Get the video crop window. +int PlayerGetPictureCrop(Player* pl, int* pLeftOff, int* pTopOff, + int* pCropWidth, int* pCropHeight); + +//* Hide video layer. +int PlayerVideoHide(Player* pl); + +//* show video layer. +int PlayerVideoShow(Player* pl); + +//* set whether keep last picture when player stopped. +//* the last picture is hold in default. +int PlayerSetHoldLastPicture(Player* pl, int bHold); + +//* set video window. +int PlayerSetVideoRegion(Player* pl, int x, int y, int nWidth, int nHeight); + +//* set picture display ratio, full stream, letter box or other modes. +int PlayerSetDisplayRatio(Player* pl, enum EDISPLAYRATIO eDisplayRatio); + +//* set deinterlace +int PlayerSetDeinterlace(Player* pl, Deinterlace* pDi); +#endif +//******************************** END ***********************************// + +//******************************* START **********************************// +//** Audio Output Control APIs. +//** +int PlayerSetAudioSink(Player* pl, SoundCtrl* pAudioSink); + +//* get audio balance, return 1 means left channel, 2 means right channel, 3 means stereo. +int PlayerGetAudioBalance(Player* pl); + +//* set audio balance, 1 means left channel, 2 means right channel, 3 means stereo. +int PlayerSetAudioBalance(Player* pl, int nAudioBalance); + +//* mute the audio, bMute = 1 means mute the audio, 0 means resume from muted. +int PlayerSetAudioMute(Player* pl, int bMute); + +/* get the audio mute setting, 1 means audio muted, 0 means not muted, -1 means + * there is no audio stream. + */ +int PlayerGetAudioMuteFlag(Player* pl); + +int PlayerSetAudioForceWriteToDeviceFlag(Player* pl, int bForceFlag); + +int PlayerConfigExtraScaleInfo(Player* pl, int nWidthTh, int nHeightTh, + int nHorizontalScaleRatio, int nVerticalScaleRatio); + +int PlayerSetPlayBackSettings(Player* pl,const struct XAudioPlaybackRate* rate); + +int PlayerGetPlayBackSettings(Player* pl,XAudioPlaybackRate* rate); + +//******************************** END ***********************************// + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/platform/mcu/xr871/src/cedarx/playback/include/soundControl.h b/platform/mcu/xr871/src/cedarx/playback/include/soundControl.h new file mode 100644 index 0000000000..5f16007018 --- /dev/null +++ b/platform/mcu/xr871/src/cedarx/playback/include/soundControl.h @@ -0,0 +1,166 @@ +/* + * Copyright (c) 2008-2016 Allwinner Technology Co. Ltd. + * All rights reserved. + * + * File : soundControl.h + * Description : soundControl + * History : + * + */ + +#ifndef SOUND_CONTROL_H +#define SOUND_CONTROL_H +#include "cdx_log.h" +#include "adecoder.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum XAudioTimestretchStretchMode { + AUDIO_TIMESTRETCH_STRETCH_DEFAULT = 0, + AUDIO_TIMESTRETCH_STRETCH_SPEECH = 1, +} XAudioTimestretchStretchMode; + +typedef enum XAudioTimestretchFallbackMode { + XAUDIO_TIMESTRETCH_FALLBACK_CUT_REPEAT = -1, + XAUDIO_TIMESTRETCH_FALLBACK_DEFAULT = 0, + XAUDIO_TIMESTRETCH_FALLBACK_MUTE = 1, + XAUDIO_TIMESTRETCH_FALLBACK_FAIL = 2, +} XAudioTimestretchFallbackMode; + +typedef struct XAudioPlaybackRate { + float mSpeed; + float mPitch; + enum XAudioTimestretchStretchMode mStretchMode; + enum XAudioTimestretchFallbackMode mFallbackMode; +}XAudioPlaybackRate; + +typedef struct SoundCtrl SoundCtrl; + +typedef struct SoundControlOpsS SoundControlOpsT; + +struct SoundControlOpsS +{ + void (*destroy)(SoundCtrl* s); + + void (*setFormat)(SoundCtrl* s, CdxPlaybkCfg* cfg); + + int (*start)(SoundCtrl* s); + + int (*stop)(SoundCtrl* s); + + int (*pause)(SoundCtrl* s); + + int (*flush)(SoundCtrl* s, void *block); + + int (*write)(SoundCtrl* s, void* pData, int nDataSize); + + int (*reset)(SoundCtrl* s); + + int (*getCachedTime)(SoundCtrl* s); + + int (*getFrameCount)(SoundCtrl* s); + + int (*setPlaybackRate)(SoundCtrl* s,const XAudioPlaybackRate *rate); + +}; + +struct SoundCtrl +{ + struct SoundControlOpsS* ops; +}; + +static inline void SoundDeviceDestroy(SoundCtrl* s) +{ + CDX_CHECK(s); + CDX_CHECK(s->ops); + CDX_CHECK(s->ops->destroy); + return s->ops->destroy(s); +} + +static inline void SoundDeviceSetFormat(SoundCtrl* s, CdxPlaybkCfg* cfg) +{ + CDX_CHECK(s); + CDX_CHECK(s->ops); + CDX_CHECK(s->ops->setFormat); + return s->ops->setFormat(s, cfg); +} + +static inline int SoundDeviceStart(SoundCtrl* s) +{ + CDX_CHECK(s); + CDX_CHECK(s->ops); + CDX_CHECK(s->ops->start); + return s->ops->start(s); +} + +static inline int SoundDeviceStop(SoundCtrl* s) +{ + CDX_CHECK(s); + CDX_CHECK(s->ops); + CDX_CHECK(s->ops->stop); + return s->ops->stop(s); +} + +static inline int SoundDevicePause(SoundCtrl* s) +{ + CDX_CHECK(s); + CDX_CHECK(s->ops); + CDX_CHECK(s->ops->pause); + return s->ops->pause(s); +} + +static inline int SoundDeviceFlush(SoundCtrl* s, void* block) +{ + CDX_CHECK(s); + CDX_CHECK(s->ops); + CDX_CHECK(s->ops->flush); + return s->ops->flush(s, block); +} + +static inline int SoundDeviceWrite(SoundCtrl* s, void* pData, int nDataSize) +{ + CDX_CHECK(s); + CDX_CHECK(s->ops); + CDX_CHECK(s->ops->write); + return s->ops->write(s, pData, nDataSize); +} + +static inline int SoundDeviceReset(SoundCtrl* s) +{ + CDX_CHECK(s); + CDX_CHECK(s->ops); + CDX_CHECK(s->ops->reset); + return s->ops->reset(s); +} + +static inline int SoundDeviceGetCachedTime(SoundCtrl* s) +{ + CDX_CHECK(s); + CDX_CHECK(s->ops); + CDX_CHECK(s->ops->getCachedTime); + return s->ops->getCachedTime(s); +} + +static inline int SoundDeviceGetFrameCount(SoundCtrl* s) +{ + CDX_CHECK(s); + CDX_CHECK(s->ops); + CDX_CHECK(s->ops->getFrameCount); + return s->ops->getFrameCount(s); +} + +static inline int SoundDeviceSetPlaybackRate(SoundCtrl* s,const XAudioPlaybackRate *rate) +{ + CDX_CHECK(s); + CDX_CHECK(s->ops); + return s->ops->setPlaybackRate(s,rate); +} + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/platform/mcu/xr871/src/cedarx/playback/player_i.h b/platform/mcu/xr871/src/cedarx/playback/player_i.h new file mode 100644 index 0000000000..a3c0cd9b66 --- /dev/null +++ b/platform/mcu/xr871/src/cedarx/playback/player_i.h @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2008-2016 Allwinner Technology Co. Ltd. + * All rights reserved. + * + * File : player_i.h + * Description : player_i + * History : + * + */ + +#ifndef PLAYER_I_H +#define PLAYER_I_H + +#include "player.h" +#include +#include +#include + +enum EPLAYERINTERNALNOTIFY //* player internal notify. +{ + PLAYER_VIDEO_DECODER_NOTIFY_STREAM_UNDERFLOW = 32, + PLAYER_VIDEO_DECODER_NOTIFY_CRASH, + PLAYER_VIDEO_DECODER_NOTIFY_EOS, + PLAYER_VIDEO_DECODER_NOTIFY_SET_SECURE_BUFFER_COUNT, + PLAYER_VIDEO_DECODER_NOTIFY_SET_SECURE_BUFFERS, + + PLAYER_VIDEO_RENDER_NOTIFY_FIRST_PICTURE = 64, + PLAYER_VIDEO_RENDER_NOTIFY_PICTURE_PTS, + PLAYER_VIDEO_RENDER_NOTIFY_EOS, + PLAYER_VIDEO_RENDER_NOTIFY_VIDEO_SIZE, + PLAYER_VIDEO_RENDER_NOTIFY_VIDEO_CROP, + PLAYER_VIDEO_RENDER_NOTIFY_VIDEO_FRAME, + PLAYER_VIDEO_RENDER_NOTIFY_CRASH, + + PLAYER_AUDIO_DECODER_NOTIFY_STREAM_UNDERFLOW = 96, + PLAYER_AUDIO_DECODER_NOTIFY_CRASH, + PLAYER_AUDIO_DECODER_NOTIFY_EOS, + PLAYER_AUDIO_DECODER_NOTIFY_AUDIORAWPLAY, + + PLAYER_AUDIO_RENDER_NOTIFY_FIRST_FRAME = 128, + PLAYER_AUDIO_RENDER_NOTIFY_EOS, + PLAYER_AUDIO_RENDER_NOTIFY_PTS_AND_CACHETIME, + PLAYER_AUDIO_RENDER_NOTIFY_AUDIO_INFO, + + PLAYER_SUBTITLE_DECODER_NOTIFY_STREAM_UNDERFLOW = 160, + PLAYER_SUBTITLE_DECODER_NOTIFY_CRASH, + PLAYER_SUBTITLE_DECODER_NOTIFY_EOS, + + PLAYER_SUBTITLE_RENDER_NOTIFY_ITEM_PTS_AND_DURATION = 192, + PLAYER_SUBTITLE_RENDER_NOTIFY_ITEM_AVAILABLE, + PLAYER_SUBTITLE_RENDER_NOTIFY_ITEM_EXPIRED, + PLAYER_SUBTITLE_RENDER_NOTIFY_EOS, +}; + +// report every 5 seconds. +#define CONFIG_AUDIO_RENDER_TIME_DIFFERENCE_REPORT_PERIOD (5000000) + +// choose the larger pts of nFirstVideoPts and nFirstAudioPts as the player start time. +#define CONFIG_USE_LARGER_PTS_AS_START_TIME (1) + +// if pts difference is more than 4 seconds, we judge there is a pts jump(loop back) event. +#define CONFIG_THREASHOLD_OF_PTS_DIFFERENCE_TO_JUDGE_PTS_jUMP (4000000) + +// if pts difference is more than 1.5 seconds, we judge there is a pts jump(loop back) event. +#define CONFIG_THREASHOLD_OF_PTS_DIFFERENCE_TO_JUDGE_PTS_jUMP_DTMB (1500000) + +/* don't wait for synchronization for too long, should not smaller than + * CONFIG_THREASHOLD_OF_PTS_DIFFERENCE_TO_JUDGE_PTS_jUMP. + */ +#define CONFIG_MAX_WAIT_TIME_FOR_SYNC (4000000) + +// 20 seconds. +#define CONFIG_THREASHOLD_OF_PTS_DIFFERENCE_TO_JUDGE_PTS_jUMP_FOR_SUBTITLE (20000000) + +#define TIMER_DROP_AUDIO_DATA 0x04 +#define TIMER_DROP_VIDEO_DATA 0x05 +#define TIMER_NEED_NOTIFY_AGAIN 0x06 + +/*static int64_t GetSysTime(void) +{ + int64_t time; + struct timeval t; + gettimeofday(&t, NULL); + time = (int64_t)t.tv_sec * 1000000; + time += t.tv_usec; + return time; +}*/ + +#define CDX_PLAYER_UNUSE(param) (void)param + +#endif diff --git a/platform/mcu/xr871/src/cedarx/playback/streamManager.h b/platform/mcu/xr871/src/cedarx/playback/streamManager.h new file mode 100644 index 0000000000..71519c217c --- /dev/null +++ b/platform/mcu/xr871/src/cedarx/playback/streamManager.h @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2008-2016 Allwinner Technology Co. Ltd. + * All rights reserved. + * + * File : streamManager.h + * Description : stream manager + * History : + * + */ + +#ifndef STREAM_MANAGER_H +#define STREAM_MANAGER_H + +#include + +typedef struct StreamFrameS StreamFrame; +struct StreamFrameS { + void* pData; + int nLength; + int64_t nPts; + int64_t nPcr; + int64_t nDuration; +}; + +typedef struct StreamManager StreamManager; + +StreamManager* StreamManagerCreate(int nMaxBufferSize, int nMaxFrameNum, int nStreamID); + +void StreamManagerDestroy(StreamManager* pSm); + +void StreamManagerReset(StreamManager* pSm); + +int StreamManagerBufferSize(StreamManager* pSm); + +int StreamManagerStreamFrameNum(StreamManager* pSm); + +int StreamManagerStreamDataSize(StreamManager* pSm); + +int StreamManagerRequestBuffer(StreamManager* pSm, int nRequireSize, char** ppBuf, int* pBufSize); + +int StreamManagerAddStream(StreamManager* pSm, StreamFrame* pStreamFrame); + +StreamFrame* StreamManagerRequestStream(StreamManager* pSm); + +StreamFrame* StreamManagerGetFrameInfo(StreamManager* pSm, int nFrameIndex); + +int StreamManagerReturnStream(StreamManager* pSm, StreamFrame* pStreamFrame); + +/* This function is deprecated. It does nothing. */ +int StreamManagerFlushStream(StreamManager* pSm, StreamFrame* pStreamFrame); + +/* Rewind the stream to a specific position/time. You should call this function + * after switch track. + */ +int StreamManagerRewind(StreamManager *p, int64_t curTime); + +#endif diff --git a/platform/mcu/xr871/src/cedarx/record/include/CaptureControl.h b/platform/mcu/xr871/src/cedarx/record/include/CaptureControl.h new file mode 100644 index 0000000000..03d27cc46e --- /dev/null +++ b/platform/mcu/xr871/src/cedarx/record/include/CaptureControl.h @@ -0,0 +1,77 @@ +#ifndef CAPTURE_CONTROL_H +#define CAPTURE_CONTROL_H +#include "cdx_log.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + int nChannels; + int nSamplerate; + int nBitpersample; +} CdxCapbkCfg; + +typedef struct CaptureCtrl CaptureCtrl; + +struct CaptureControlOpsS +{ + void (*destroy)(CaptureCtrl* c); + void (*setFormat)(CaptureCtrl* c, CdxCapbkCfg* cfg); + int (*start)(CaptureCtrl* c); + int (*stop)(CaptureCtrl* c); + int (*read)(CaptureCtrl* c, void* pData, int nDataSize); +}; + +struct CaptureCtrl +{ + struct CaptureControlOpsS* ops; +}; + +CaptureCtrl* CaptureDeviceCreate(); + +static inline void CaptureDeviceDestroy(CaptureCtrl* c) +{ + CDX_CHECK(c); + CDX_CHECK(c->ops); + CDX_CHECK(c->ops->destroy); + return c->ops->destroy(c); +} + +static inline void CaptureDeviceSetFormat(CaptureCtrl* c, CdxCapbkCfg* cfg) +{ + CDX_CHECK(c); + CDX_CHECK(c->ops); + CDX_CHECK(c->ops->setFormat); + return c->ops->setFormat(c, cfg); +} + +static inline int CaptureDeviceStart(CaptureCtrl* c) +{ + CDX_CHECK(c); + CDX_CHECK(c->ops); + CDX_CHECK(c->ops->start); + return c->ops->start(c); +} + +static inline int CaptureDeviceStop(CaptureCtrl* c) +{ + CDX_CHECK(c); + CDX_CHECK(c->ops); + CDX_CHECK(c->ops->stop); + return c->ops->stop(c); +} + +static inline int CaptureDeviceRead(CaptureCtrl* c, void* pData, int nDataSize) +{ + CDX_CHECK(c); + CDX_CHECK(c->ops); + CDX_CHECK(c->ops->read); + return c->ops->read(c, pData, nDataSize); +} + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/platform/mcu/xr871/src/cedarx/stream/http/CdxHttpStream.h b/platform/mcu/xr871/src/cedarx/stream/http/CdxHttpStream.h new file mode 100644 index 0000000000..e5d235a5be --- /dev/null +++ b/platform/mcu/xr871/src/cedarx/stream/http/CdxHttpStream.h @@ -0,0 +1,228 @@ +/* + * Copyright (c) 2008-2016 Allwinner Technology Co. Ltd. + * All rights reserved. + * + * File : CdxHttpStream.h + * Description : Part of http stream. + * History : + * + */ + +#ifndef CDX_HTTP_STREAM_H +#define CDX_HTTP_STREAM_H + +#include +#include +#include +#include +#include +#include + +#define __SAVE_BITSTREAMS (0) + +//#define closesocket close + +#define __CONFIG_ZLIB (0) + +#define CONFIG_ALI_YUNOS (0) + +struct HttpHeaderField +{ + const char *key; + const char *val; +}; + +typedef struct AW_HTTP_FIELD_TYPE +{ + char *fieldName; + struct AW_HTTP_FIELD_TYPE *next; +}CdxHttpFieldT; + +typedef struct AW_HTTP_HEADER +{ + char *protocol; + char *method; + char *uri; + unsigned int statusCode; + char *reasonPhrase; + unsigned int httpMinorVersion; + // Field variables + CdxHttpFieldT *firstField; + CdxHttpFieldT *lastField; + unsigned int fieldNb; + char *fieldSearch; + CdxHttpFieldT *fieldSearchPos; + // Body variables + char *body; + size_t bodySize; + char *buffer; + size_t bufferSize; + unsigned int isParsed; + // + char *cookies; + int posHdrSep; + } CdxHttpHeaderT; + +typedef struct BandwidthEntry +{ + cdx_int64 mDelayUs; + cdx_int32 mNumBytes; +}BandwidthEntryT; + +enum HttpStreamStateE +{ + HTTP_STREAM_IDLE = 0x00L, + HTTP_STREAM_CONNECTING = 0x01L, + HTTP_STREAM_READING = 0x02L, + HTTP_STREAM_SEEKING = 0x03L, +}; + +typedef struct CdxHttpStreamImpl +{ + CdxStreamT base; + CdxStreamT *tcpStream; + + CdxStreamProbeDataT probeData; + cdx_int32 ioState; //for interface use + int seekAble; //seekable flag. + CdxUrlT* url; //scheme,port,etc + cdx_int32 exitFlag; //when close, exit + cdx_int32 forceStopFlag; //forceStop, not exit GetNetworkData + cdx_int64 totalSize; //content-length + cdx_int64 bufPos; //stream data is buffered to this file pos. + cdx_int64 readPos; //stream data is read to this pos. + cdx_int32 eosFlag; //for internal use. + //all stream data is read from network + void *data; //point to response(2K) + cdx_char *sourceUri; //the source uri + + char *httpDataBuffer; //databuf, store one group data. + //1) 4KB, store response entity+probeData; + //2) 1024*n Bytes: normal data. + int httpDataSize; //total data size of httpDataBuffer + + char *httpDataBufferChunked; // store parsed data from chunked data; + int httpDataSizeChunked; // valid data size of httpDataBufferChunked + int httpDataBufferPosChunked; // read pos of httpDataBufferChunked, + // 0~httpDataSizeChunked-1 + int restChunkSize; // when chunked, during CdxStreamOpen, + // httpDataBuffer may not store the whole chunk, + // this variable store rest size of the chunk. + int dataCRLF; // data\r\n, next chunk need to get rid of \r\n. + cdx_char tmpChunkedLen[10]; // store temp chunked len while force stop. + // eg: abcd\r\n, ab + int tmpChunkedSize; // size of temp chunked len while force stop. + // eg: ab is 2 bytes + int lenCRLF; // len\r\n, next chunk need to get rid of \r\n. + int chunkedLen; // while force stop, store current chunked len. + // len\r\n + + const cdx_char *ua; // UA + int nHttpHeaderSize; //header number to be added + struct CdxHttpHeaderField *pHttpHeader; //http header + ExtraDataContainerT hfsContainer; + int chunkedFlag; //set when "Transfer-Encoding: chunked" + cdx_atomic_t ref; //reference count, for free resource while + //still blocking. + pthread_t threadId; + + pthread_t getNetworkDataThreadId; + int seekFlag; + int pauseReadDataFlag; + pthread_mutex_t bufferMutex; + pthread_cond_t bufferCond; + pthread_mutex_t seekMutex; + pthread_cond_t seekCond; + pthread_mutex_t pauseReadDataMutex; + pthread_cond_t pauseReadDataCond; + + pthread_mutex_t lock; //for break blocking operation + pthread_cond_t cond; + + cdx_char *buffer; //httpStream's buffer + cdx_int64 maxBufSize; //max buffer size + cdx_int64 validDataSize; + cdx_char *bufReadPtr; //read pointer + cdx_char *bufWritePtr; //write pointer + cdx_char *bufEndPtr; //point to the end of buffer + + cdx_int64 mTotalTransferTimeUs; //for bandwidth measurement. + cdx_int32 mTotalTransferBytes; + cdx_int32 mBandwidthTimes; + BandwidthEntryT bandWidthHistory[100]; + cdx_int32 index; + + //cdx_char *protectAreaPtr; //protect area, not cover, for seek back. + cdx_int64 protectAreaPos; //protectArea begin file pos. + //readPos-protectAreaPos: 0~maxProtectAreaSize + cdx_int64 protectAreaSize; //Size of protectArea + cdx_int32 maxProtectAreaSize; + cdx_int32 bufFullFlag; //buffer full flag. + cdx_int32 getNetworkDataFlag; //in GetNetworkData loop flag. + + enum HttpStreamStateE state; + AwPoolT *pool; + cdx_int32 bStreamReadEos; //CdxStreamRead eos. + + //cdx_int32 nResponseDataSize; //when reconnect, store the size of data + //in response entity. +#if __SAVE_BITSTREAMS + FILE *fp_http; + cdx_int32 streamIndx; + cdx_char location[256]; +#endif + + ParserCallback callback; + void *pUserData; + cdx_int64 downloadStart; + cdx_int64 downloadEnd; + cdx_int64 downloadTimeMs; + +#if CONFIG_ALI_YUNOS + cdx_int64 downloadFirstTime; + cdx_int64 downloadLastTime; + int mYunOSstatusCode; + char tcpIp[128]; +#endif + + int isHls; + cdx_int64 baseOffset; //* for id3 parser + +#if __CONFIG_ZLIB + int compressed; + z_stream inflateStream; + cdx_uint8 *inflateBuffer; +#endif + + int keepAlive; + int isAuth; + cdx_bool isDTMB; + cdx_bool enoughData; +}CdxHttpStreamImplT; + +typedef struct CdxHttpSendBuffer +{ + void *size; + void *buf; +}CdxHttpSendBufferT; + +extern CdxHttpHeaderT *CdxHttpNewHeader(void); +extern char *CdxHttpGetField(CdxHttpHeaderT *httpHdr, const char *fieldName); +extern char* CdxHttpBuildRequest(CdxHttpHeaderT *httpHdr); +extern CdxUrlT *CdxUrlRedirect(CdxUrlT **url, char *redir); +extern const char *GetUA(int n, CdxHttpHeaderFieldT *pHttpHeader); +extern CdxUrlT* CdxUrlNew(char* url); +extern void CdxHttpSetUri(CdxHttpHeaderT *httpHdr, const char *uri); +extern void CdxHttpSetField(CdxHttpHeaderT *httpHdr, const char *fieldName); +extern void CdxHttpFree(CdxHttpHeaderT *httpHdr); +extern int CdxHttpResponseAppend(CdxHttpHeaderT *httpHdr, char *response, int length); +extern int CdxHttpIsHeaderEntire(CdxHttpHeaderT *httpHdr); +extern int CdxHttpResponseParse(CdxHttpHeaderT *httpHdr); +extern int CdxHttpAuthenticate(CdxHttpHeaderT *httpHdr, CdxUrlT *url, int *authRetry); +extern cdx_int32 ReadChunkedSize(CdxStreamT *stream, cdx_char tmpLen[], cdx_int32* size); +extern cdx_int32 ReadChunkedData(CdxStreamT *stream, void *ptr, cdx_int32 size); +extern char *RmSpace(char *str); +extern int CdxHttpAddBasicAuthentication(CdxHttpHeaderT *httpHdr, const char *username, + const char *password); + +#endif diff --git a/platform/mcu/xr871/src/cedarx/stream/include/CdxSeqBuffer.h b/platform/mcu/xr871/src/cedarx/stream/include/CdxSeqBuffer.h new file mode 100644 index 0000000000..5117d517af --- /dev/null +++ b/platform/mcu/xr871/src/cedarx/stream/include/CdxSeqBuffer.h @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2008-2016 Allwinner Technology Co. Ltd. + * All rights reserved. + * + * File : CdxSeqBuffer.h + * Description : SeqBuffer + * History : + * + */ + +#ifndef CDX_SEQ_BUFFER_H +#define CDX_SEQ_BUFFER_H +#include +#include +#include +#include + +struct SeqBufferS +{ + CdxListNodeT node; + CdxBufferT *val; + cdx_uint32 seqNum; +}; + +#define SeqBufferDel(buf)\ + CdxListDel(&buf->node); \ + CdxBufferDestroy(buf->val); \ + CdxFree(buf) + +static inline void SeqBufferClear(CdxListT *seqBufList) +{ + struct SeqBufferS *posSeqBuf, *tmpSeqBuf; + + CdxListForEachEntrySafe(posSeqBuf, tmpSeqBuf, seqBufList, node) + { + SeqBufferDel(posSeqBuf); + } + return ; +} + +static inline cdx_err SeqBufferInsert(CdxListT *seqBufList, struct SeqBufferS *seqBuf) +{ + struct SeqBufferS *posSeqBuf; + CdxListForEachEntryReverse(posSeqBuf, seqBufList, node) + { + if (posSeqBuf->seqNum <= seqBuf->seqNum) + { + break; + } + } + + if (posSeqBuf->seqNum == seqBuf->seqNum) + { + CDX_LOGW("Discarding duplicate buffer (%u, %u)", + posSeqBuf->seqNum, seqBuf->seqNum); + return CDX_FAILURE; + } + + CdxListAddAfter(&seqBuf->node, &posSeqBuf->node); + return CDX_SUCCESS; +} + +#endif diff --git a/platform/mcu/xr871/src/cedarx/stream/include/CdxStream.h b/platform/mcu/xr871/src/cedarx/stream/include/CdxStream.h new file mode 100644 index 0000000000..15c7df272d --- /dev/null +++ b/platform/mcu/xr871/src/cedarx/stream/include/CdxStream.h @@ -0,0 +1,575 @@ +/* + * Copyright (c) 2008-2016 Allwinner Technology Co. Ltd. + * All rights reserved. + * + * File : CdxStream.h + * Description : Stream + * History : + * + */ + +/* +********************************************************************** +* File Name : CdxStream.h +* Author : bzchen@allwinnertech.com +* Version : 1.0 +* Data : 2013.08.29 +* Description: This file define data structure and interface of the stream module. +**********************************************************************/ + +#ifndef CDX_STREAM_H +#define CDX_STREAM_H +#include +#include +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct CdxStreamProbeDataS CdxStreamProbeDataT; +typedef struct CdxStreamCreatorS CdxStreamCreatorT; + +#define STREAM_SEEK_SET 0 //* offset from the file start. +#define STREAM_SEEK_CUR 1 //* offset from current file position. +#define STREAM_SEEK_END 2 //* offset from the file end. + +#define CDX_STREAM_FLAG_SEEK 0x01U +#define CDX_STREAM_FLAG_STT 0x02U /*seek to time*/ +#define CDX_STREAM_FLAG_NET 0x04U /*net work stream*/ +#define CDX_STREAM_FLAG_DTMB 0x08U /*dtmb live stream */ + +typedef struct CdxDataSourceS CdxDataSourceT; +typedef struct CdxStreamS CdxStreamT; +typedef CdxStreamT CustomerStream; + +enum DSExtraDataTypeE +{ + EXTRA_DATA_UNKNOWN, + EXTRA_DATA_HTTP_HEADER, + EXTRA_DATA_APPOINTED_TS, + EXTRA_DATA_RTP, + EXTRA_DATA_AES, + EXTRA_DATA_BDMV, + EXTRA_DATA_RTSP, + EXTRA_DATA_HTTP, //download spend time etc. + EXTRA_DATA_HLS, +}; + +struct CdxDataSourceS +{ + void* pHTTPServer; + cdx_char *uri; /* format : "scheme://..." */ + void *extraData; /* extra data for some stream, ex: http header for http stream */ + enum DSExtraDataTypeE extraDataType; + cdx_int64 offset; /* for id3 parser */ + int probeSize; +}; + +#if 1 +/* These two struct is deprecated, you should use CdxKeyedVectorT interface */ +typedef struct CdxHttpHeaderField +{ + const char *key; + const char *val; +}CdxHttpHeaderFieldT; + +typedef struct CdxHttpHeaderFields +{ + int num; + CdxHttpHeaderFieldT *pHttpHeader; +}CdxHttpHeaderFieldsT; +#endif + +typedef struct ExtraDataContainerS +{ + void *extraData; + enum DSExtraDataTypeE extraDataType; +}ExtraDataContainerT; + +/*------------------------BDMV stream header---------------------------*/ +struct IoOperationS +{ + int (*openDir)(void * /*cbhdr*/, const char * /*name*/, void ** /*dir handler*/); + int (*readDir)(void * /*cbhdr*/, void * /*dir handler*/, char * /*dname*/, int /*dnameLen*/); + int (*closeDir)(void * /*cbhdr*/, void * /*dir handler*/); + + int (*openFile)(void * /*cbhdr*/, const char * /*pathname*/, int /*flags*/); + int (*accessFile)(void * /*cbhdr*/, const char * /*pathname*/, int /*mode*/); +}; + +struct BdmvExtraDataS +{ + struct IoOperationS *ioCb; + void *cbHdr; +}; +/*------------------------RTP end-------------------------------------*/ + +/*------------------------RTP stream header----------------------------*/ +typedef struct RtpStreamItfS RtpStreamItfT; +struct RtpStreamItfOpsS +{ + int (*filter)(RtpStreamItfT *, CdxBufferT * /*in*/, CdxListT ** /*out*/); + void (*destroy)(RtpStreamItfT *); +}; + +struct RtpStreamItfS +{ + struct RtpStreamItfOpsS *ops; +}; + +struct RtpStreamExtraDataS +{ + RtpStreamItfT *itf; + cdx_uint32 timeScale; + cdx_bool multicast; +}; +/*------------------------RTP end-------------------------------------*/ + +/*------------------------AES stream header----------------------------*/ +enum PaddingType +{ + CDX_PKCS7, +}; + +typedef struct AesStreamExtraDataS +{ + cdx_uint8 key[16]; + cdx_uint8 iv[16]; + enum PaddingType paddingType; + void *extraData; /* extra data for some stream, ex: http header for http stream */ + enum DSExtraDataTypeE extraDataType; +}AesStreamExtraDataT; +/*------------------------AES end------------------------------------*/ + +//***********************************************************// +//* Define IO status. +//***********************************************************// +enum CdxIOStateE +{ + CDX_IO_STATE_OK = 0, //* nothing wrong of the data accession. + CDX_IO_STATE_INVALID, + CDX_IO_STATE_ERROR, //* unknown error, can't access the media file. + CDX_IO_STATE_EOS, /*end of stream*/ + CDX_IO_STATE_CLEAR // for mms playlist +}; + +/*for stream->control*/ +enum CdxStreamCommandE +{ + STREAM_CMD_GET_DURATION = 0x101, + /* Get the duration of the media file. + *param is a pointer to a int64_t variable, duration = *(int64_t*)param. + *return 0 if OK, return -1 if not supported by the stream handler. + */ + + STREAM_CMD_READ_NOBLOCK = 0x102, + /* for sock recv in seek operation, return as soon as possible. + * return 0 if OK, return -1 if not support. + */ + + STREAM_CMD_GET_SOCKRECVBUFLEN = 0x103, + /* Get the socket recv buf len, *(cdx_int32*)param. + * return 0 if OK, return -1 if not support. + */ + /* To Add More commands here.*/ + + /* struct StreamCacheStateS */ + STREAM_CMD_GET_CACHESTATE = 0x104, + + STREAM_CMD_SET_FORCESTOP = 0x105, + /* Force stop the stream running job. */ + + STREAM_CMD_CLR_FORCESTOP = 0x106, + /* Resume the stream running job. */ + + STREAM_CMD_RESET_STREAM = 0x107, + /*for mms playlist*/ + + STREAM_CMD_EXT_IO_OPERATION = 0x108, + /*for bdmv io operation: opendir, readdir, closedir, open, access*/ + + STREAM_CMD_SET_CALLBACK = 0x109, + /*for setting callback*/ + STREAM_CMD_SET_ISHLS= 0x110, + /*for setting hls parser flag*/ + + STREAM_CMD_GET_IP = 0x110, + /* For get tcp ip*/ + STREAM_CMD_REQUEST_WRITE_INFO = 0x120, + STREAM_CMD_UPDATE_WRITE_INFO = 0x121, + STREAM_CMD_SET_EOF = 0x122, + STREAM_CMD_SET_RELATIVE_START_POS, + STREAM_CMD_SET_RELATIVE_FILE_SIZE, +}; + +/*stream event*/ +enum CdxStreamEventE +{ + STREAM_EVT_DOWNLOAD_START = STREAM_EVENT_VALID_RANGE_MIN, + STREAM_EVT_DOWNLOAD_END, + STREAM_EVT_DOWNLOAD_ERROR, + STREAM_EVT_NET_DISCONNECT, + STREAM_EVT_DOWNLOAD_RESPONSE_HEADER, + STREAM_EVT_DOWNLOAD_START_TIME, + STREAM_EVT_DOWNLOAD_FIRST_TIME, + STREAM_EVT_DOWNLOAD_END_TIME, + STREAM_EVT_DOWNLOAD_GET_TCP_IP, + STREAM_EVT_DOWNLOAD_DOWNLOAD_ERROR, + STREAM_EVT_CMCC_LOG_RECORD, + + STREAM_EVT_MAX, +}; +CHECK_STREAM_EVENT_MAX_VALID(STREAM_EVT_MAX) + +/* STREAM_CMD_EXT_IO_OPERATION param */ +enum ExtIoctlCmdE +{ + EXT_IO_OPERATION_OPENDIR = 0, + EXT_IO_OPERATION_READDIR, + EXT_IO_OPERATION_CLOSEDIR, + + EXT_IO_OPERATION_OPENFILE, + EXT_IO_OPERATION_READFILE, + EXT_IO_OPERATION_CLOSEFILE, + + EXT_IO_OPERATION_ACCESS, +}; + +struct ExtIoctlParamS +{ + int cmd; + + void *inHdr; /* (int)'dirId', (int)'fd', 'DIR *' */ + char *inPath; + + char *inBuf; + int inBufLen; + + void *outHdr; /* (int)'dirid', (int)'fd', 'DIR *' */ + int outRet; +}; + +typedef enum CdxIOStateE CdxIOStateT; +typedef enum CdxStreamCommandE CdxStreamCommandT; + +struct StreamCacheStateS +{ + cdx_int32 nCacheCapacity; + cdx_int32 nCacheSize; + cdx_int32 nBandwidthKbps; + cdx_int32 nPercentage; /* ((100 * download_offset)/totle_size)*/ +}; + +struct CdxStreamProbeDataS +{ + cdx_char *buf; + cdx_uint32 len; + + /* maybe we should define it as char private[0]? */ + cdx_char *uri[0]; +}; + +typedef int (*ParserCallback)(void* pUserData, int eMessageId, void* param); +struct CallBack +{ + ParserCallback callback; + void *pUserData; +}; + +typedef struct ContorlTaskS ContorlTask; +struct ContorlTaskS +{ + cdx_int32 cmd; + void *param; + ContorlTask *next; +}; +struct CdxStreamCreatorS +{ + CdxStreamT *(*create)(CdxDataSourceT *); +}; + +struct CdxStreamOpsS +{ + cdx_int32 (*connect)(CdxStreamT * /*stream*/); + + CdxStreamProbeDataT *(*getProbeData)(CdxStreamT * /*stream*/); + + cdx_int32 (*stream_read)(CdxStreamT * /*stream*/, void * /*buf*/, cdx_uint32 /*len*/); + + cdx_int32 (*stream_close)(CdxStreamT * /*stream*/); + + cdx_int32 (*getIOState)(CdxStreamT * /*stream*/); + + cdx_uint32 (*attribute)(CdxStreamT * /*stream*/); + + cdx_int32 (*control)(CdxStreamT * /*stream*/, cdx_int32 /*cmd*/, void * /*param*/); + + /*Ͻӿڱʵ*/ + + cdx_int32 (*stream_write)(CdxStreamT *, void * /*buf*/, cdx_uint32 /*len*/); + + cdx_int32 (*getMetaData)(CdxStreamT *, const cdx_char * /*key*/, void ** /*pVal*/); + + cdx_int32 (*seek)(CdxStreamT * /*stream*/, cdx_int64 /*offset*/, cdx_int32 /*whence*/); + + cdx_int32 (*seekToTime)(CdxStreamT * /*stream*/, cdx_int64 /*time us*/); + + cdx_bool (*eos)(CdxStreamT * /*stream*/); + + cdx_int64 (*tell)(CdxStreamT * /*stream*/); + + cdx_int64 (*size)(CdxStreamT * /*stream*/); + + //cdx_int32 (*forceStop)(CdxStreamT * /*stream*/); + + /*cut Size/CachedSize/SetBufferSize/GetBufferSize */ +}; + +struct CdxStreamS +{ + struct CdxStreamOpsS *ops; +}; + +int AwStreamRegister(CdxStreamCreatorT *creator, cdx_char *type); + +CdxStreamT *CdxStreamCreate(CdxDataSourceT *source); + +/**************************************************************************** + * Danger! Danger! Danger! + * + * CdxStreamOpen() includes two parts: create() and connect(). + * create(): It is (or should) nonblock, and unlikely to fail. + * connect(): It can block, and has a great chance to fail. + * + * After create() return, you can use the stream to force stop connect(). This + * is why we separate CdxStreamOpen() into two parts. + * + * So, you must call CdxStreamClose() even when CdxStreamOpen() failed. Or you + * will suffer from memory and file descriptor leak. Yes, it's pain in the ass! + * + ***************************************************************************/ +int CdxStreamOpen(CdxDataSourceT *source, pthread_mutex_t *mutex, cdx_bool *exit, + CdxStreamT **stream, ContorlTask *streamTasks); + +static inline cdx_int32 CdxStreamConnect(CdxStreamT *stream) +{ + CDX_CHECK(stream); + CDX_CHECK(stream->ops); + CDX_CHECK(stream->ops->connect); + return stream->ops->connect(stream); +} + +static inline CdxStreamProbeDataT *CdxStreamGetProbeData(CdxStreamT *stream) +{ + CDX_CHECK(stream); + CDX_CHECK(stream->ops); + CDX_CHECK(stream->ops->getProbeData); + return stream->ops->getProbeData(stream); +} + +static inline cdx_int32 CdxStreamRead(CdxStreamT *stream, void *buf, cdx_int32 len) +{ + CDX_CHECK(stream); + CDX_CHECK(stream->ops); + CDX_CHECK(stream->ops->stream_read); + return stream->ops->stream_read(stream, buf, len); +} + +static inline cdx_int32 CdxStreamWrite(CdxStreamT *stream, void *buf, cdx_int32 len) +{ + CDX_CHECK(stream); + CDX_CHECK(stream->ops); + + return stream->ops->stream_write ? stream->ops->stream_write(stream, buf, len) : -1; +} + +static inline cdx_int32 CdxStreamClose(CdxStreamT *stream) +{ + CDX_CHECK(stream); + CDX_CHECK(stream->ops); + CDX_CHECK(stream->ops->stream_close); + return stream->ops->stream_close(stream); +} + +static inline cdx_int32 CdxStreamGetIoState(CdxStreamT *stream) +{ + CDX_CHECK(stream); + CDX_CHECK(stream->ops); + CDX_CHECK(stream->ops->getIOState); + return stream->ops->getIOState(stream); +} + +static inline cdx_uint32 CdxStreamAttribute(CdxStreamT *stream) +{ + CDX_CHECK(stream); + CDX_CHECK(stream->ops); + CDX_CHECK(stream->ops->attribute); + return stream->ops->attribute(stream); +} + +static inline cdx_int32 CdxStreamControl(CdxStreamT *stream, + cdx_int32 cmd, void *param) +{ + CDX_CHECK(stream); + CDX_CHECK(stream->ops); + CDX_CHECK(stream->ops->control); + return stream->ops->control(stream, cmd, param); +} + +//#define STREAM_METADATA_ORGINAL_URI "stream.orginalUri" +#define STREAM_METADATA_REDIRECT_URI "stream.redirectUri" +#define STREAM_METADATA_ACCESSIBLE_URI "stream.accessibleUri" + +static inline cdx_int32 CdxStreamGetMetaData(CdxStreamT *stream, + const cdx_char *key, void **pVal) +{ + CDX_CHECK(stream); + CDX_CHECK(stream->ops); + return stream->ops->getMetaData ? + stream->ops->getMetaData(stream, key, pVal) : + -1; +} + +static inline cdx_int32 CdxStreamSeek(CdxStreamT *stream, cdx_int64 offset, + cdx_int32 whence) +{ + CDX_CHECK(stream); + CDX_CHECK(stream->ops); + + return stream->ops->seek ? stream->ops->seek(stream, offset, whence) : -1; +} + +static inline cdx_int32 CdxStreamSeekToTime(CdxStreamT *stream, cdx_int64 timeUs) +{ + CDX_CHECK(stream); + CDX_CHECK(stream->ops); + + return stream->ops->seekToTime ? stream->ops->seekToTime(stream, timeUs) : -1; +} + +static inline cdx_bool CdxStreamEos(CdxStreamT *stream) +{ + CDX_CHECK(stream); + CDX_CHECK(stream->ops); + + return stream->ops->eos ? stream->ops->eos(stream) : CDX_FALSE; +} + +static inline cdx_int64 CdxStreamTell(CdxStreamT *stream) +{ + CDX_CHECK(stream); + CDX_CHECK(stream->ops); + + return stream->ops->tell ? stream->ops->tell(stream) : -1; +} +/* +CDX_INTERFACE cdx_int32 CdxStreamForceStop(CdxStreamT *stream) +{ + CDX_CHECK(stream); + CDX_CHECK(stream->ops); + + return stream->ops->forceStop ? stream->ops->forceStop(stream) : -1; +} +*/ + +static inline cdx_int64 CdxStreamSize(CdxStreamT *stream) +{ + CDX_CHECK(stream); + CDX_CHECK(stream->ops); + + return stream->ops->size ? stream->ops->size(stream) : (-1LL); +} + +#define CdxStreamSeekAble(stream) \ + (!!(CdxStreamAttribute(stream) & CDX_STREAM_FLAG_SEEK)) + +#define CdxStreamIsNetStream(stream) \ + (!!(CdxStreamAttribute(stream) & CDX_STREAM_FLAG_NET)) + +#define CdxStreamForceStop(stream) \ + (CdxStreamControl(stream, STREAM_CMD_SET_FORCESTOP, NULL)) + +#define CdxStreamClrForceStop(stream) \ + (CdxStreamControl(stream, STREAM_CMD_CLR_FORCESTOP, NULL)) + +#define CdxStreamIsDtmbStream(stream) \ + (!!(CdxStreamAttribute(stream) & CDX_STREAM_FLAG_DTMB)) + +static inline cdx_int32 CdxStreamSkip(CdxStreamT *stream, cdx_uint32 len) +{ + if (len == 0) + { + return 0; + } + if (CdxStreamSeekAble(stream)) + { + return CdxStreamSeek(stream, (cdx_int64)len, STREAM_SEEK_CUR); + } + else + { + cdx_int32 ret = 0; + int i = 0, skipSize = 0, unitSize = 0; + cdx_char dummyBuf[512]; + if (len > 512 * 10) + { + CDX_LOGE("too large(%u) to dummy for a unseekable stream", len); + return -1; + } + + skipSize = len; + for (i = 0; i < 10; i++) + { + unitSize = (skipSize > 512) ? 512 : skipSize; + ret = CdxStreamRead(stream, dummyBuf, unitSize); + if (ret != unitSize) + { + return -1; + } + skipSize -= ret; + if (skipSize == 0) + { + break; + } + } + return CDX_SUCCESS; + } +} + +#define CdxStreamGetU8(stream) \ + ({cdx_uint8 data; CdxStreamRead(stream, &data, 1); GetU8(&data);}) + +#define CdxStreamGetLE16(stream) \ + ({cdx_uint8 data[2]; CdxStreamRead(stream, data, 2); GetLE16(data);}) + +#define CdxStreamGetLE32(stream) \ + ({cdx_uint8 data[4]; CdxStreamRead(stream, data, 4); GetLE32(data);}) + +#define CdxStreamGetLE64(stream) \ + ({cdx_uint8 data[8]; CdxStreamRead(stream, data, 8); GetLE64(data);}) + +#define CdxStreamGetBE16(stream) \ + ({cdx_uint8 data[2]; CdxStreamRead(stream, data, 2); GetBE16(data);}) + +#define CdxStreamGetBE32(stream) \ + ({cdx_uint8 data[4]; CdxStreamRead(stream, data, 4); GetBE32(data);}) + +#define CdxStreamGetBE64(stream) \ + ({cdx_uint8 data[8]; CdxStreamRead(stream, data, 8); GetBE64(data);}) + +typedef struct REQUEST_BUFFER_INFO +{ + unsigned char* bufPtr; + unsigned int bufLen; +}StreamBufferInfo; + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/platform/mcu/xr871/src/cedarx/stream/include/CdxStreamErrno.h b/platform/mcu/xr871/src/cedarx/stream/include/CdxStreamErrno.h new file mode 100644 index 0000000000..21abd4c1fa --- /dev/null +++ b/platform/mcu/xr871/src/cedarx/stream/include/CdxStreamErrno.h @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2008-2016 Allwinner Technology Co. Ltd. + * All rights reserved. + * + * File : CdxStreamErrno.h + * Description : StreamErrno + * History : + * + */ + +#ifndef CDX_STREAM_ERRNO_H +#define CDX_STREAM_ERRNO_H + +enum CdxStreamErrno +{ + CEDARX_STREAM_ERR_BASE = 0x04670000, + /*㶨ģÿģӦͳһ*/ + CEDARX_STREAM_ERR_REGISTER, + CEDARX_STREAM_ERR_NOT_REGISTER, + CEDARX_STREAM_ERR_CONN_TIMEOUT, + CEDARX_STREAM_ERR_PARAMETER_INVAILD, +}; + +#endif diff --git a/platform/mcu/xr871/src/cedarx/xplayer/cache.h b/platform/mcu/xr871/src/cedarx/xplayer/cache.h new file mode 100644 index 0000000000..1351399781 --- /dev/null +++ b/platform/mcu/xr871/src/cedarx/xplayer/cache.h @@ -0,0 +1,121 @@ +/* +* Copyright (c) 2008-2016 Allwinner Technology Co. Ltd. +* All rights reserved. +* +* File : cache.h +* Description : cache policy for net stream +* History : +* Author : AL3 +* Date : 2015/05/05 +* Comment : first version +* +*/ + +#ifndef CACHE_H +#define CACHE_H + +//#include +#include +#include +#include "CdxParser.h" +#if VIDEO_SUPPORT +#include "vdecoder.h" +#endif +#include "player.h" + +#define likely(p) __builtin_expect(!!(p), 1) +#define unlikely(p) __builtin_expect(!!(p), 0) + +typedef struct CacheNode_t CacheNode; +struct CacheNode_t +{ + unsigned char* pData; + int nLength; + int64_t nPts; + int64_t nPcr; + int bIsFirstPart; + int bIsLastPart; + int eMediaType; + int nStreamIndex; + int nFlags; + int64_t nDuration; + int infoVersion; + void* info; + CacheNode* pNext; +}; + +typedef struct StreamCache_t +{ + pthread_mutex_t mutex; + int nMaxBufferSize; + int nStartPlaySize; + + int nDataSize; + int nFrameNum; +#if VIDEO_SUPPORT + int nVideoFrameNum; +#endif + CacheNode* pHead; + CacheNode* pTail; + + int nPassedDataSize; + int nPassedFrameNum; +#if VIDEO_SUPPORT + int nPassedVideoFrameNum; +#endif + CacheNode* pPassedHead; + + int64_t nLastValidPts; + CacheNode* pNodeWithLastValidPts; + int64_t nLastValidPcr; + CacheNode* pNodeWithLastValidPcr; + int64_t nFirstPts; + + int nBitrate; //* for ts/m2ts stream seek processing. + Player* pPlayer; //* for ts/m2ts stream seek processing. + CdxParserTypeT eContainerFormat; //* for seek processing. +#if VIDEO_SUPPORT + enum EVIDEOCODECFORMAT eVideoCodecFormat; //* for finding key frame in cache when seek. +#endif +}StreamCache; + +StreamCache* StreamCacheCreate(void); + +void StreamCacheDestroy(StreamCache* c); + +void StreamCacheSetSize(StreamCache* c, int nStartPlaySize, int nMaxBufferSize); + +int StreamCacheGetSize(StreamCache* c); + +int StreamCacheUnderflow(StreamCache* c); + +int StreamCacheOverflow(StreamCache* c); + +int StreamCacheDataEnough(StreamCache* c); + +CacheNode* StreamCacheNextFrame(StreamCache* c); + +void StreamCacheFlushOneFrame(StreamCache* c); + +int StreamCacheAddOneFrame(StreamCache* c, CacheNode* node); + +void StreamCacheFlushAll(StreamCache* c); + +int StreamCacheGetBufferFullness(StreamCache* c); + +int StreamCacheGetLoadingProgress(StreamCache* c); + +int StreamCacheSetMediaFormat(StreamCache* c, + CdxParserTypeT eContainerFormat, +#if VIDEO_SUPPORT + enum EVIDEOCODECFORMAT eVideoCodecFormat, +#else + int eVideoCodecFormat, +#endif + int nBitrate); + +int StreamCacheSetPlayer(StreamCache* c, Player* pPlayer); + +int64_t StreamCacheSeekTo(StreamCache* c, int64_t nSeekTimeUs); + +#endif diff --git a/platform/mcu/xr871/src/cedarx/xplayer/demuxComponent.h b/platform/mcu/xr871/src/cedarx/xplayer/demuxComponent.h new file mode 100644 index 0000000000..ee498102da --- /dev/null +++ b/platform/mcu/xr871/src/cedarx/xplayer/demuxComponent.h @@ -0,0 +1,159 @@ +/* +* Copyright (c) 2008-2016 Allwinner Technology Co. Ltd. +* All rights reserved. +* +* File : demuxComponent.h +* Description : stream control and video stream demux +* History : +* Author : AL3 +* Date : 2015/05/05 +* Comment : first version +* +*/ + +#ifndef DEMUX_COMPONENT_H +#define DEMUX_COMPONENT_H + +#if 0 +#include +#include +#include +#include +#include +#endif + +#include "player.h" //* player library in "android/hardware/aw/" +#include "mediaInfo.h" +#include + +typedef void* DemuxComp; + +#define SOURCE_TYPE_URL 0x1 +#define SOURCE_TYPE_FD 0x2 +#define SOURCE_TYPE_ISTREAMSOURCE 0x3 + +enum EDEMUXNOTIFY //* player internal notify. +{ + DEMUX_NOTIFY_PREPARED = DEMUX_NOTIFY_VALID_RANGE_MIN, + DEMUX_NOTIFY_EOS, + DEMUX_NOTIFY_IOERROR, + DEMUX_NOTIFY_RESET_PLAYER, + DEMUX_NOTIFY_SEEK_FINISH, + DEMUX_NOTIFY_CACHE_STAT, + DEMUX_NOTIFY_BUFFER_START, + DEMUX_NOTIFY_BUFFER_END, + DEMUX_NOTIFY_PAUSE_PLAYER, + DEMUX_NOTIFY_RESUME_PLAYER, + + DEMUX_IOREQ_ACCESS, + DEMUX_IOREQ_OPEN, + DEMUX_IOREQ_OPENDIR, + DEMUX_IOREQ_READDIR, + DEMUX_IOREQ_CLOSEDIR, + DEMUX_VIDEO_STREAM_CHANGE, + DEMUX_AUDIO_STREAM_CHANGE, + + //for cmcc + DEMUX_NOTIFY_HLS_DOWNLOAD_START, + DEMUX_NOTIFY_HLS_DOWNLOAD_ERROR, + DEMUX_NOTIFY_HLS_DOWNLOAD_END, + + //* CMCC 2.1.7.12-m3 log + DEMUX_NOTIFY_LOG_RECORDER, + + // for timeShift end + DEMUX_NOTIFY_TIMESHIFT_DURATION, + DEMUX_NOTIFY_METADATA, + DEMUX_NOTIFY_MAX, +}; +CHECK_DEMUX_NOTIFY_MAX_VALID(DEMUX_NOTIFY_MAX) + +enum EDEMUXERROR +{ + DEMUX_ERROR_NONE = 0, + DEMUX_ERROR_UNKNOWN = -1, + DEMUX_ERROR_IO = -2, + DEMUX_ERROR_USER_CANCEL = -3, +}; + +enum ECACHEPOLICY +{ + CACHE_POLICY_ADAPTIVE = 0, + CACHE_POLICY_SMOOTH = 2, +}; + +typedef int (*DemuxCallback)(void* pUserData, int eMessageId, void* param); + +DemuxComp* DemuxCompCreate(void); + +void DemuxCompDestroy(DemuxComp* d); + +void DemuxCompClear(DemuxComp* d); //* clear the data source, like just created. + +int DemuxCompSetUrlSource(DemuxComp* d, void* pHTTPServer, + const char* pUrl, const CdxKeyedVectorT* pHeaders); + +int DemuxCompSetFdSource(DemuxComp* d, int fd, int64_t nOffset, int64_t nLength); + +int DemuxCompSetStreamSource(DemuxComp* d, const char* pUri); + +int DemuxCompSetPlayer(DemuxComp* d, Player* player); + +int DemuxCompSetHdcpOps(DemuxComp* d, struct HdcpOpsS* pHdcp); + +int DemuxCompSetCallback(DemuxComp* d, DemuxCallback callback, void* pUserData); + +int DemuxCompPrepareAsync(DemuxComp* d); + +int DemuxCompCancelPrepare(DemuxComp* d); //* should call back DEMUX_PREPARE_FINISH message. + +#if VIDEO_SUPPORT +int DemuxProbeH265RefPictureNumber(char* pDataBuf, int nDataLen); +#endif + +MediaInfo* DemuxCompGetMediaInfo(DemuxComp* d); + +int DemuxCompStart(DemuxComp* d); + +int DemuxCompStop(DemuxComp* d); //* close the data source, must call prepare again to restart. + +int DemuxCompPause(DemuxComp* d); //* no pause status in demux component, return OK immediately. + +int DemuxCompGetStatus(DemuxComp* d); + +int DemuxCompSeekTo(DemuxComp* d, int nSeekTimeMs); + +int DemuxCompCancelSeek(DemuxComp* d); //* should not call back DEMUX_SEEK_FINISH message. + +int DemuxCompNotifyFirstFrameShowed(DemuxComp* d); //* notify video first frame showed. + +#if ONLINE_SUPPORT +int DemuxCompSetCacheStatReportInterval(DemuxComp* d, int ms); + +int DemuxCompSetCachePolicy(DemuxComp* d, + enum ECACHEPOLICY eCachePolicy, + int nStartPlaySize, + int nStartPlayTimeMs, + int nMaxBufferSize); +#endif + +#if SECURE_BUFFER_SUPPORT +int DemuxCompSetSecureBufferCount(DemuxComp* d, void* param); + +int DemuxCompSetSecureBuffers(DemuxComp* d,void* param); +#endif + +int DemuxCompSeekToResetUrl(DemuxComp* d, int nSeekTimeMs); + +int DemuxCompGetLiveMode(DemuxComp* d); + +int DemuxCompSetLiveMode(DemuxComp* d, int liveMode); + +#if ONLINE_SUPPORT +int DemuxCompGetCacheSize(DemuxComp* d); +#endif + +int DemuxCompSetHdcpOps(DemuxComp* d, struct HdcpOpsS* pHdcpOps); + +#endif + diff --git a/platform/mcu/xr871/src/cedarx/xplayer/include/mediaInfo.h b/platform/mcu/xr871/src/cedarx/xplayer/include/mediaInfo.h new file mode 100644 index 0000000000..8156fd2508 --- /dev/null +++ b/platform/mcu/xr871/src/cedarx/xplayer/include/mediaInfo.h @@ -0,0 +1,89 @@ +/* +* Copyright (c) 2008-2016 Allwinner Technology Co. Ltd. +* All rights reserved. +* +* File : mediainfo.h +* Description : media infomation +* History : +* Author : AL3 +* Date : 2015/05/05 +* Comment : first version +* +*/ + +#ifndef MEDIA_INFO_H +#define MEDIA_INFO_H + +#include "player.h" //* player library in "LIBRARY/PLAYER/" +#include "CdxParser.h" //* parser library in "LIBRARY/DEMUX/PARSER/include/" + +#define WIDTH_4K 3840 +#define HEIGHT_4K 2160 +#define WIDTH_1080P 1920 +#define HEIGHT_1080P 1080 + +enum ECONTAINER +{ + CONTAINER_TYPE_MOV = CDX_PARSER_MOV, + CONTAINER_TYPE_MKV = CDX_PARSER_MKV, + CONTAINER_TYPE_ASF = CDX_PARSER_ASF, + CONTAINER_TYPE_MPG = CDX_PARSER_MPG, + CONTAINER_TYPE_TS = CDX_PARSER_TS, + CONTAINER_TYPE_AVI = CDX_PARSER_AVI, + CONTAINER_TYPE_FLV = CDX_PARSER_FLV, + CONTAINER_TYPE_PMP = CDX_PARSER_PMP, + + CONTAINER_TYPE_HLS = CDX_PARSER_HLS, + CONTAINER_TYPE_DASH = CDX_PARSER_DASH, + CONTAINER_TYPE_MMS = CDX_PARSER_MMS, + CONTAINER_TYPE_BD = CDX_PARSER_BD, + CONTAINER_TYPE_OGG = CDX_PARSER_OGG, + CONTAINER_TYPE_M3U9 = CDX_PARSER_M3U9, + CONTAINER_TYPE_RMVB = CDX_PARSER_RMVB, + CONTAINER_TYPE_PLAYLIST = CDX_PARSER_PLAYLIST, + CONTAINER_TYPE_WAV = CDX_PARSER_WAV, + CONTAINER_TYPE_APE = CDX_PARSER_APE, + CONTAINER_TYPE_AMR = CDX_PARSER_AMR, + CONTAINER_TYPE_ATRAC = CDX_PARSER_ATRAC, + CONTAINER_TYPE_MP3 = CDX_PARSER_MP3, + CONTAINER_TYPE_DTS = CDX_PARSER_DTS, + CONTAINER_TYPE_AC3 = CDX_PARSER_AC3, + CONTAINER_TYPE_AAC = CDX_PARSER_AAC, + CONTAINER_TYPE_WVM = CDX_PARSER_WVM, + //* multiple stream source of media es stream, such as rtsp. + CONTAINER_TYPE_RTSP = CDX_PARSER_REMUX, + CONTAINER_TYPE_MMSHTTP = CDX_PARSER_MMSHTTP, + + CONTAINER_TYPE_UNKNOWN = 0x100, + CONTAINER_TYPE_RAW = 0x101, +}; + +typedef struct MEDIAINFO +{ + int64_t nFileSize; + int64_t nDurationMs; + int nBitrate; + unsigned char cRotation[4]; + int64_t nFirstPts; + + enum ECONTAINER eContainerType; + int bSeekable; +#if VIDEO_SUPPORT + int nVideoStreamNum; +#endif + int nAudioStreamNum; +#if SUBTITLE_SUPPORT + int nSubtitleStreamNum; +#endif + +#if VIDEO_SUPPORT + VideoStreamInfo* pVideoStreamInfo; +#endif + AudioStreamInfo* pAudioStreamInfo; +#if SUBTITLE_SUPPORT + SubtitleStreamInfo* pSubtitleStreamInfo; +#endif +}MediaInfo; + +#endif + diff --git a/platform/mcu/xr871/src/cedarx/xplayer/include/xplayer.h b/platform/mcu/xr871/src/cedarx/xplayer/include/xplayer.h new file mode 100644 index 0000000000..994d25689c --- /dev/null +++ b/platform/mcu/xr871/src/cedarx/xplayer/include/xplayer.h @@ -0,0 +1,233 @@ +/* +* Copyright (c) 2008-2016 Allwinner Technology Co. Ltd. +* All rights reserved. +* +* File : xplayer.h +* Description : xplayer +* History : +* Author : AL3 +* Date : 2015/05/05 +* Comment : first version +* +*/ + +#ifndef XPLAYER_H +#define XPLAYER_H + +//#include +//#include +#include "pthread.h" +#include +#include "CdxKeyedVector.h" +//#include "cdx_config.h" //* configuration file in "LiBRARY/" +#include "mediaInfo.h" +#include + +#define AWPLAYER_CONFIG_DISABLE_VIDEO 1 +#define AWPLAYER_CONFIG_DISABLE_AUDIO 0 +#define AWPLAYER_CONFIG_DISABLE_SUBTITLE 1 +#define AWPLAYER_CONFIG_DISALBE_MULTI_AUDIO 0 + +#ifdef __cplusplus +extern "C" { +#endif + +/* Since xplayer is operating system independent, there is no benefit and no + * simple method to keep items in MediaEventType have the same value as items + * in media_event_type of Android. + */ +enum MediaEventType { + AWPLAYER_MEDIA_NOP = MEDIA_EVENT_VALID_RANGE_MIN, // = 0, interface test message + AWPLAYER_MEDIA_PREPARED, + AWPLAYER_MEDIA_PLAYBACK_COMPLETE, + AWPLAYER_MEDIA_BUFFERING_UPDATE, + AWPLAYER_MEDIA_SEEK_COMPLETE, + AWPLAYER_MEDIA_SET_VIDEO_SIZE, + AWPLAYER_MEDIA_STARTED, + AWPLAYER_MEDIA_PAUSED, + AWPLAYER_MEDIA_STOPPED, + AWPLAYER_MEDIA_SKIPPED, + AWPLAYER_MEDIA_TIMED_TEXT, + AWPLAYER_MEDIA_ERROR, + AWPLAYER_MEDIA_INFO, + AWPLAYER_MEDIA_SUBTITLE_DATA, + + AWPLAYER_MEDIA_LOG_RECORDER, + + AWPLAYER_EXTEND_MEDIA_INFO, + AWPLAYER_MEDIA_META_DATA, + AWPLAYER_MEDIA_EVENT_MAX, +}; +CHECK_MEDIA_EVENT_MAX_VALID(AWPLAYER_MEDIA_EVENT_MAX) + +// av/include/media/mediaplayer.h +enum MediaInfoType +{ + AW_MEDIA_INFO_UNKNOWN = 1, + AW_MEDIA_INFO_STARTED_AS_NEXT = 2, + AW_MEDIA_INFO_RENDERING_START = 3, + AW_MEDIA_INFO_BUFFERING_START = 701, + AW_MEDIA_INFO_BUFFERING_END = 702, + + AW_MEDIA_INFO_NOT_SEEKABLE = 801, + + AW_MEDIA_INFO_DOWNLOAD_START = 10086, + AW_MEDIA_INFO_DOWNLOAD_END = 10087, + AW_MEDIA_INFO_DOWNLOAD_ERROR = 10088, +}; + +enum ExMediaInfoType +{ + AW_EX_IOREQ_ACCESS = 1, + AW_EX_IOREQ_OPEN = 2, + AW_EX_IOREQ_OPENDIR = 3, + AW_EX_IOREQ_READDIR = 4, + AW_EX_IOREQ_CLOSEDIR = 5, +}; + + +// 0xx: Reserved +// 1xx: Android Player errors. Something went wrong inside the MediaPlayer. +// 2xx: Media errors (e.g Codec not supported). There is a problem with the +// media itself. +// 3xx: Runtime errors. Some extraordinary condition arose making the playback +// impossible. +// +enum MediaErrorType +{ + // 0xx + AW_MEDIA_ERROR_UNKNOWN = 1, + // 1xx + AW_MEDIA_ERROR_SERVER_DIED = 100, + // 2xx + AW_MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK = 200, + // 3xx + // 9xx + AW_MEDIA_ERROR_OUT_OF_MEMORY = 900, + // 4xx + AW_MEDIA_ERROR_IO = -1004, + AW_MEDIA_ERROR_MALFORMED = -1007, + AW_MEDIA_ERROR_UNSUPPORTED = -1010, + AW_MEDIA_ERROR_TIMED_OUT = -110, +}; + + +typedef enum AwApplicationType { + APP_DEFAULT, + APP_STREAMING, // for miracast and so on + APP_CMCC_WASU, + APP_CMCC_LOCAL, +} AwApplicationType; + +typedef int (*XPlayerNotifyCallback)(void* pUser, + int msg, int ext1, void* para); + +typedef struct XPlayerConfig_t { + AwApplicationType appType; + int livemode; +} XPlayerConfig_t; + +typedef enum XPlayerTimeoutType { + XPLAYER_PLAYBACK_TIMEOUT, + XPLAYER_PREPARE_TIMEOUT, +} XPlayerTimeoutType; + +typedef struct PlayerContext XPlayer; + +XPlayer* XPlayerCreate(); + +void XPlayerDestroy(XPlayer* p); + +int XPlayerConfig(XPlayer* p, const XPlayerConfig_t *config); + +int XPlayerSetNotifyCallback(XPlayer* p, + XPlayerNotifyCallback notifier, + void* pUserData); + +int XPlayerInitCheck(XPlayer* p); + +int XPlayerSetUID(XPlayer* p, int nUid); + +//* we must set hdcp ops before setDataSource +int XPlayerSetHdcpOps(XPlayer* p, struct HdcpOpsS* pHdcp); + +int XPlayerSetDataSourceUrl(XPlayer* p, const char* pUrl, + void* httpService, const CdxKeyedVectorT* pHeaders); + +int XPlayerSetDataSourceFd(XPlayer* p, int fd, + int64_t nOffset, int64_t nLength); + +// for IStreamSource in android +int XPlayerSetDataSourceStream(XPlayer* p, const char* pStreamUri); + +int XPlayerPrepare(XPlayer* p); + +int XPlayerPrepareAsync(XPlayer* p); + +int XPlayerStart(XPlayer* p); + +int XPlayerStop(XPlayer* p); + +int XPlayerPause(XPlayer* p); + +int XPlayerIsPlaying(XPlayer* p); + +int XPlayerSeekTo(XPlayer* p, int nSeekTimeMs); + +int XPlayerSetSpeed(XPlayer* p, int nSpeed); + +int XPlayerGetCurrentPosition(XPlayer* p, int* msec); + +int XPlayerGetDuration(XPlayer* p, int* msec); + +int XPlayerReset(XPlayer* p); + +int XPlayerSetLooping(XPlayer* p, int bLoop); + +/* + Recommand prepare timeout is multi-times to playback timeout, and + playback timeout is over 100ms. +*/ +int XPlayerSetTimeout(XPlayer* p, XPlayerTimeoutType type, int timeout_ms); + +#if SUBTITLE_SUPPORT +int XPlayerGetSubDelay(XPlayer* p); + +int XPlayerSetSubDelay(XPlayer* p, int nTimeMs); + +int XPlayerGetSubCharset(XPlayer* p, char *charset); + +int XPlayerSetSubCharset(XPlayer* p, const char* strFormat); + +void XPlayerSetSubCtrl(XPlayer* p, const SubCtrl* subctrl); + +int XPlayerSwitchSubtitle(XPlayer* pl, int nStreamIndex); + +int XPlayerSetExternalSubUrl(XPlayer* p, const char* fileName); + +int XPlayerSetExternalSubFd(XPlayer* p, int fd, int64_t offset, int64_t len, int fdSub); +#endif + +#if VIDEO_SUPPORT +int XPlayerSetVideoSurfaceTexture(XPlayer* p, const LayerCtrl* surfaceTexture); +#endif + +void XPlayerSetAudioSink(XPlayer* p, const SoundCtrl* audioSink); + +#if VIDEO_SUPPORT +void XPlayerSetDeinterlace(XPlayer* p, const Deinterlace* di); +#endif + +MediaInfo* XPlayerGetMediaInfo(XPlayer* p); + +int XPlayerSwitchAudio(XPlayer* p, int nStreamIndex); + +int XPlayerGetPlaybackSettings(XPlayer* p,XAudioPlaybackRate *rate); + +int XPlayerSetPlaybackSettings(XPlayer* p,const XAudioPlaybackRate *rate); + +#ifdef __cplusplus +} +#endif + +#endif // AWPLAYER diff --git a/platform/mcu/xr871/src/cedarx/xrecoder/include/xrecord.h b/platform/mcu/xr871/src/cedarx/xrecoder/include/xrecord.h new file mode 100644 index 0000000000..e97533fa2d --- /dev/null +++ b/platform/mcu/xr871/src/cedarx/xrecoder/include/xrecord.h @@ -0,0 +1,47 @@ +#ifndef XRECORD_H +#define XRECORD_H + +#include "CdxKeyedVector.h" +#include "CaptureControl.h" + + +#define AUDIO_INPUT (1) +#define VIDEO_INPUT (0) + +typedef struct RecoderContext XRecord; + +typedef enum XRECODER_AUDIO_ENCODE_TYPE +{ + XRECODER_AUDIO_ENCODE_AMR_TYPE, + XRECODER_AUDIO_ENCODE_PCM_TYPE, +} XRECODER_AUDIO_ENCODE_TYPE; + +typedef enum +{ + XRECODER_STATE_INIT, + XRECODER_STATE_PREPARED, + XRECODER_STATE_STARTED, + XRECODER_STATE_STOPED, + XRECODER_STATE_DESTROYED, +} XRECODER_STATE; + +XRecord* XRecordCreate(); + +int XRecordSetDataDstUrl(XRecord* p, + const char* pUrl, + void* arg, + const CdxKeyedVectorT* pHeaders); + +int XRecordDestroy(XRecord* p); + +void XRecordSetAudioCap(XRecord* p, const CaptureCtrl* audioSrc); + +int XRecordSetAudioEncodeType(XRecord* p, XRECODER_AUDIO_ENCODE_TYPE type, AudioEncodeConfig *config); + +int XRecordPrepare(XRecord* p); + +int XRecordStart(XRecord* p); + +int XRecordStop(XRecord* p); + +#endif /* XRECORD_H */ \ No newline at end of file diff --git a/platform/mcu/xr871/src/console/Makefile b/platform/mcu/xr871/src/console/Makefile new file mode 100644 index 0000000000..a9dfc86a0c --- /dev/null +++ b/platform/mcu/xr871/src/console/Makefile @@ -0,0 +1,24 @@ +# +# Rules for building library +# + +# ---------------------------------------------------------------------------- +# common rules +# ---------------------------------------------------------------------------- +ROOT_PATH := ../.. + +include $(ROOT_PATH)/gcc.mk + +# ---------------------------------------------------------------------------- +# library and objects +# ---------------------------------------------------------------------------- +LIBS := libconsole.a + +DIRS := . + +SRCS := $(basename $(foreach dir,$(DIRS),$(wildcard $(dir)/*.[csS]))) + +OBJS := $(addsuffix .o,$(SRCS)) + +# library make rules +include $(LIB_MAKE_RULES) diff --git a/platform/mcu/xr871/src/console/console.c b/platform/mcu/xr871/src/console/console.c new file mode 100644 index 0000000000..d7f9d8559f --- /dev/null +++ b/platform/mcu/xr871/src/console/console.c @@ -0,0 +1,396 @@ +/** + * @file console.c + * @author XRADIO IOT WLAN Team + */ + +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include "kernel/os/os.h" +#include "console/console.h" +#include "console_debug.h" + + +#define CONSOLE_EMPTY_CMD_SUPPORT 1 + +#define CONSOLE_CMD_LINE_MAX_LEN 256 +#define CONSOLE_CMD_LINE_BUF_NUM 2 + +typedef enum { + CONSOLE_STATE_STOP = 0, + CONSOLE_STATE_START, + CONSOLE_STATE_TERMINATE, /* to be terminated */ +} console_state; + +static uint8_t g_console_buf[CONSOLE_CMD_LINE_BUF_NUM * CONSOLE_CMD_LINE_MAX_LEN]; + +typedef struct console_priv { + UART_ID uart_id; + console_state state; + + uint8_t free_buf_bitmap[CONSOLE_CMD_LINE_BUF_NUM]; + uint8_t ready_buf_bitmap[CONSOLE_CMD_LINE_BUF_NUM]; + uint8_t last_cmd_buf_idx; + uint8_t rx_buf_idx; + uint32_t rx_data_cnt; + + uint8_t *buf[CONSOLE_CMD_LINE_BUF_NUM]; + + OS_Semaphore_t cmd_sem; + + console_cmd_exec_func cmd_exec; +} console_priv_t; + +static console_priv_t g_console; + +#define CONSOLE_SET_BUF_BITMAP_VALID(bitmap, idx) ((bitmap)[idx] = 1) +#define CONSOLE_SET_BUF_BITMAP_INVALID(bitmap, idx) ((bitmap)[idx] = 0) +#define CONSOLE_IS_BUF_BITMAP_VALID(bitmap, idx) ((bitmap)[idx] == 1) + +#define CONSOLE_INVALID_BUF_IDX CONSOLE_CMD_LINE_BUF_NUM + +#define CONSOLE_BUF(console, buf_idx) ((console)->buf[buf_idx]) + + +static uint8_t console_get_valid_buf_idx(uint8_t *bitmap, uint8_t last_idx) +{ + uint8_t loop = CONSOLE_CMD_LINE_BUF_NUM; + uint8_t idx = last_idx; + + while (loop > 0) { + idx = (idx + 1) % CONSOLE_CMD_LINE_BUF_NUM; + if (CONSOLE_IS_BUF_BITMAP_VALID(bitmap, idx)) { + CONSOLE_SET_BUF_BITMAP_INVALID(bitmap, idx); + return idx; + } + --loop; + } + + return CONSOLE_INVALID_BUF_IDX; +} + +/* called after receive a valid command line */ +static __inline void console_rx_cmdline(console_priv_t *console) +{ + /* buf state change: rx --> ready */ + CONSOLE_SET_BUF_BITMAP_VALID(console->ready_buf_bitmap, console->rx_buf_idx); + + /* try to get a new rx buf from free list */ + console->rx_buf_idx = console_get_valid_buf_idx(console->free_buf_bitmap, + console->rx_buf_idx); + console->rx_data_cnt = 0; + + /* notify console task that a new command is ready */ + OS_SemaphoreRelease(&console->cmd_sem); +} + +/* Note: only support line end with "\r\n" or "\n", not support "\r" */ +static void console_rx_callback(void *arg) +{ + console_priv_t *console; + UART_T *uart; + uint8_t *rx_buf; + uint32_t cnt; + uint8_t data; + + uart = (UART_T *)arg; + console = &g_console; + + if (console->rx_buf_idx != CONSOLE_INVALID_BUF_IDX) { +retry: + rx_buf = CONSOLE_BUF(console, console->rx_buf_idx); + cnt = console->rx_data_cnt; + rx_buf += cnt; + + while (cnt < CONSOLE_CMD_LINE_MAX_LEN) { + if (HAL_UART_IsRxReady(uart)) { + data = HAL_UART_GetRxData(uart); + if (data == '\n') { /* command line end */ + if (cnt > 0 && (*(rx_buf - 1)) == '\r') { /* skip last '\r' */ + --rx_buf; + --cnt; + } +#if (!CONSOLE_EMPTY_CMD_SUPPORT) + if (cnt > 0) +#endif + { /* valid command */ + *rx_buf = '\0'; /* C style string */ + CONS_DBG("rx cmd (%u char): '%s'\n", cnt, + CONSOLE_BUF(console, console->rx_buf_idx)); +#if CONS_CHECK_OVERFLOW + if (rx_buf - CONSOLE_BUF(console, console->rx_buf_idx) + >= CONSOLE_CMD_LINE_MAX_LEN) { + CONS_ERR("rx buf %d overflow\n", console->rx_buf_idx); + } +#endif + console_rx_cmdline(console); + return; + } + } else { + if (isprint(data) || (data == '\r')) { /* valid char */ + *rx_buf = data; +#if CONS_CHECK_OVERFLOW + if (rx_buf - CONSOLE_BUF(console, console->rx_buf_idx) + >= CONSOLE_CMD_LINE_MAX_LEN) { + CONS_ERR("rx buf %d overflow\n", console->rx_buf_idx); + } +#endif + ++rx_buf; + ++cnt; + } else { /* invalid char */ + CONS_DBG("rx illegal char 0x%x\n", data); + console->rx_data_cnt = 0; /* reset rx buffer */ + goto retry; + } + } + } else { + break; /* no more data */ + } + } + + if (cnt >= CONSOLE_CMD_LINE_MAX_LEN) { + /* rx buffer full but no valid command */ + console->rx_data_cnt = 0; /* reset rx buffer */ + CONS_DBG("cmd too long, max %d\n", CONSOLE_CMD_LINE_MAX_LEN - 1); + goto retry; + } + console->rx_data_cnt = cnt; + } else { + CONS_WARN("no buf for rx, discard received data\n"); + while (HAL_UART_IsRxReady(uart)) { + HAL_UART_GetRxData(uart); + } + } +} + +#define CONSOLE_THREAD_STACK_SIZE (2 * 1024) +static OS_Thread_t g_console_thread; + +static void console_task(void *arg) +{ + uint8_t cmd_buf_idx; + uint8_t *cmd_buf; + console_priv_t *console; + + CONS_DBG("%s() start...\n", __func__); + + console = &g_console; + + while (1) { + if (OS_SemaphoreWait(&console->cmd_sem, OS_WAIT_FOREVER) != OS_OK) + continue; + + if (console->state != CONSOLE_STATE_START) + break; + + arch_irq_disable(); + cmd_buf_idx = console_get_valid_buf_idx(console->ready_buf_bitmap, + console->last_cmd_buf_idx); + arch_irq_enable(); + + if (cmd_buf_idx != CONSOLE_INVALID_BUF_IDX) { + cmd_buf = CONSOLE_BUF(console, cmd_buf_idx); + + CONS_DBG("exec cmd (idx %d): '%s'\n", cmd_buf_idx, cmd_buf); + if (console->cmd_exec) + console->cmd_exec((char *)cmd_buf); + + console->last_cmd_buf_idx = cmd_buf_idx; + + arch_irq_disable(); + if (console->rx_buf_idx == CONSOLE_INVALID_BUF_IDX) { + /* no buf for rx, set the used one for rx directly */ + console->rx_buf_idx = cmd_buf_idx; + console->rx_data_cnt = 0; + } else { + /* buf state change: ready --> free */ + CONSOLE_SET_BUF_BITMAP_VALID(console->free_buf_bitmap, cmd_buf_idx); + } + arch_irq_enable(); + + } else { + CONS_WARN("no valid command\n"); + } + } + + CONS_DBG("%s() exit\n", __func__); + OS_ThreadDelete(&g_console_thread); +} + +/** + * @brief Start the console according to the specified parameters + * @param[in] param Pointer to console_param_t structure + * @return 0 on success, -1 on failure + * + * @note Before starting the console, the related UART specified by + * console_param_t::uart_id MUST be inited. + * @note After starting the console, all UART's input data are read and + * processed by the console. + */ +int console_start(console_param_t *param) +{ + int i; + UART_T *uart; + console_priv_t *console; + + console = &g_console; + if (console->state != CONSOLE_STATE_STOP) { + CONS_ERR("console state %d\n", console->state); + return -1; + } + + memset(console, 0, sizeof(*console)); + console->uart_id = param->uart_id; + console->cmd_exec = param->cmd_exec; + + for (i = 0; i < CONSOLE_CMD_LINE_BUF_NUM; ++i) { + console->buf[i] = &g_console_buf[i * CONSOLE_CMD_LINE_MAX_LEN]; + CONSOLE_SET_BUF_BITMAP_VALID(console->free_buf_bitmap, i); + CONSOLE_SET_BUF_BITMAP_INVALID(console->ready_buf_bitmap, i); + } + + console->last_cmd_buf_idx = CONSOLE_CMD_LINE_BUF_NUM - 1; + + /* set rx buf */ + console->rx_buf_idx = 0; + console->rx_data_cnt = 0; + CONSOLE_SET_BUF_BITMAP_INVALID(console->free_buf_bitmap, console->rx_buf_idx); + + if (OS_SemaphoreCreate(&console->cmd_sem, 0, OS_SEMAPHORE_MAX_COUNT) != OS_OK) { + CONS_ERR("create semaphore failed\n"); + return -1; + } + + /* start console task */ + if (OS_ThreadCreate(&g_console_thread, + "", + console_task, + NULL, + OS_THREAD_PRIO_CONSOLE, + CONSOLE_THREAD_STACK_SIZE) != OS_OK) { + CONS_ERR("create console task failed\n"); + return -1; + } + + + uart = HAL_UART_GetInstance(console->uart_id); + HAL_UART_EnableRxCallback(console->uart_id, console_rx_callback, uart); + console->state = CONSOLE_STATE_START; + + return 0; +} + +/** + * @brief Stop the console + * @return None + */ +void console_stop(void) +{ + console_priv_t *console; + + console = &g_console; + HAL_UART_DisableRxCallback(console->uart_id); + console->state = CONSOLE_STATE_TERMINATE; + OS_SemaphoreRelease(&console->cmd_sem); + + while (OS_ThreadIsValid(&g_console_thread)) { + OS_MSleep(1); /* wait for thread termination */ + } + + OS_SemaphoreDelete(&console->cmd_sem); + console->state = CONSOLE_STATE_STOP; +} + +/** + * @brief Write out an amount of data through the console's UART + * @param[in] buf Pointer to the data buffer + * @param[in] len Number of bytes to be written out + * @return Number of bytes written out, -1 on error + */ +int console_write(uint8_t *buf, int32_t len) +{ + console_priv_t *console; + + console = &g_console; + return HAL_UART_Transmit_Poll(console->uart_id, buf, len); +} + +/** + * @brief Disable the console's input function + * @note This function is used to disable the console's input function + * temporarily when the console is running. When the console is diable, + * all UART's input data are no long read or processed by the console. + * @return None + */ +void console_disable(void) +{ + console_priv_t *console; + + console = &g_console; + if (console->state == CONSOLE_STATE_START) { + HAL_UART_DisableRxCallback(console->uart_id); + } +} + +/** + * @brief Enable the console's input function + * @note This function is used to enable the console's input function if it is + * disabled before. The console's input function is enable by default + * after starting. + * @return None + */ +void console_enable(void) +{ + console_priv_t *console; + UART_T *uart; + + console = &g_console; + if (console->state == CONSOLE_STATE_START) { + uart = HAL_UART_GetInstance(console->uart_id); + HAL_UART_EnableRxCallback(console->uart_id, console_rx_callback, uart); + } +} + +/** + * @brief Get the console's related UART ID + * @retval UART_ID, UART_INVALID_ID on failure + */ +UART_ID console_get_uart_id(void) +{ + console_priv_t *console; + + console = &g_console; + if (console->state == CONSOLE_STATE_START) { + return console->uart_id; + } else { + return UART_INVALID_ID; + } +} diff --git a/platform/mcu/xr871/src/console/console.mk b/platform/mcu/xr871/src/console/console.mk new file mode 100644 index 0000000000..42aaf6a221 --- /dev/null +++ b/platform/mcu/xr871/src/console/console.mk @@ -0,0 +1,8 @@ + + +NAME := console + +$(NAME)_TYPE := kernel +$(NAME)_SOURCES := \ + console.c + diff --git a/platform/mcu/xr871/src/console/console_debug.h b/platform/mcu/xr871/src/console/console_debug.h new file mode 100644 index 0000000000..48ddb80805 --- /dev/null +++ b/platform/mcu/xr871/src/console/console_debug.h @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _CONSOLE_DEBUG_H_ +#define _CONSOLE_DEBUG_H_ + +#include +#include "sys/xr_util.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define CONS_DBG_ON 0 +#define CONS_WARN_ON 1 +#define CONS_ERR_ON 1 + +#define CONS_CHECK_OVERFLOW 1 + +#define CONS_SYSLOG printf +#define CONS_ABORT() do { } while (1) //sys_abort() + +#define CONS_LOG(flags, fmt, arg...) \ + do { \ + if (flags) \ + CONS_SYSLOG(fmt, ##arg); \ + } while (0) + +#define CONS_DBG(fmt, arg...) CONS_LOG(CONS_DBG_ON, "[console] "fmt, ##arg) +#define CONS_WARN(fmt, arg...) CONS_LOG(CONS_WARN_ON, "[console WARN] "fmt, ##arg) +#define CONS_ERR(fmt, arg...) \ + do { \ + CONS_LOG(CONS_ERR_ON, "[console ERR] %s():%d, "fmt, \ + __func__, __LINE__, ##arg); \ + if (CONS_ERR_ON) \ + CONS_ABORT(); \ + } while (0) + +#ifdef __cplusplus +} +#endif + +#endif /* _CONSOLE_DEBUG_H_ */ diff --git a/platform/mcu/xr871/src/driver/chip/chip.mk b/platform/mcu/xr871/src/driver/chip/chip.mk new file mode 100644 index 0000000000..b241b0b45f --- /dev/null +++ b/platform/mcu/xr871/src/driver/chip/chip.mk @@ -0,0 +1,44 @@ +NAME := chip + +$(NAME)_TYPE := kernel +$(NAME)_SOURCES := \ + codec/hal_ac101.c \ + codec/hal_codec.c \ + flashchip/flash_chip.c \ + flashchip/flash_default.c \ + hal_adc.c \ + hal_board.c \ + hal_ccm.c \ + hal_crypto.c \ + hal_csi.c \ + hal_dma.c \ + hal_dmic.c \ + hal_efuse.c \ + hal_flash.c \ + hal_flashcache.c \ + hal_flashctrl.c \ + hal_global.c \ + hal_gpio.c \ + hal_i2c.c \ + hal_i2s.c \ + hal_irrx.c \ + hal_irtx.c \ + hal_mbox.c \ + hal_nvic.c \ + hal_prcm.c \ + hal_pwm.c \ + hal_rtc.c \ + hal_spi.c \ + hal_spinlock.c \ + hal_timer.c \ + hal_uart.c \ + hal_util.c \ + hal_wakeup.c \ + hal_wdg.c \ + hal_xip.c \ + ir_nec.c \ + sdmmc/core.c \ + sdmmc/hal_sdhost.c \ + sdmmc/sd.c \ + system_chip.c + diff --git a/platform/mcu/xr871/src/driver/chip/codec/codec.h b/platform/mcu/xr871/src/driver/chip/codec/codec.h new file mode 100644 index 0000000000..112d00d5c5 --- /dev/null +++ b/platform/mcu/xr871/src/driver/chip/codec/codec.h @@ -0,0 +1,115 @@ +/** + * @file codec.h + * @author XRADIO IOT WLAN Team + */ + +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _DRIVER_CHIP_CODEC_H_ +#define _DRIVER_CHIP_CODEC_H_ + +#include "driver/chip/hal_codec.h" + +#ifdef __cplusplus + extern "C" { +#endif + +typedef struct { + uint8_t level; + uint8_t reg_val; +} Volume; + +typedef enum { + HAL_CODEC_INIT = 0, + HAL_CODEC_DEINIT +} CODEC_Req; + +/** + * @brief DAI pll/clock/format configuration, all optional.. + * Called by codec drivers. + */ +struct codec_dai_ops { + int32_t (*startup)(AUDIO_Device device); + int32_t (*setPll)(DAI_FmtParam *fmtParam); + int32_t (*setClkdiv)(DAI_FmtParam *fmtParam,uint32_t sampleRate); + int32_t (*setFormat)(DAI_FmtParam *fmtParam); + int32_t (*shutDown)(bool playOn, bool recordOn); +}; + +/** + * @brief Stream path/volume/trigger configuration, all optional. + * Called by codec drivers. + */ +struct codec_ctl_ops { + int32_t (*setRoute)(AUDIO_Device device); + int32_t (*setVolume)(AUDIO_Device dev, uint32_t volume); + int32_t (*setTrigger)(AUDIO_Device dev, uint32_t on); +}; + +/** + * @brief Stream path/volume/trigger configuration, all optional. + * Called by codec drivers. + */ +struct codec_ops { + int32_t (*setPower)(CODEC_Req req, void *arg); + int32_t (*setSysClk)(CODEC_Req req, void *arg); + int32_t (*setInitParam)(CODEC_Req req, void *arg); + int32_t (*jackDetect)(CODEC_Req req, void *arg); +}; + +/** + * @brief Codec info structure definition + */ +typedef struct { + uint8_t name[10]; /*!< Name of codec */ + uint8_t devAddr; /*!< Address of I2C device */ + uint8_t RegLength; /*!< I2C registers length */ + uint8_t RegValLength; /*!< I2C registers values length */ + struct codec_ops *ops; /*!< Init the low level hardware : GPIO, CLOCK, POWER, JACK...etc */ + struct codec_dai_ops *dai_ops; /*!< Init codec pcm interface */ + struct codec_ctl_ops *ctl_ops; /*!< Config stream volume and path */ +} CODEC,*CODECP; + +int32_t snd_soc_read(uint32_t reg); +int32_t snd_soc_write(uint32_t reg, uint32_t reg_val); +int32_t snd_soc_update_bits(uint32_t reg, uint32_t mask,uint32_t value); + +#define CODEC_I2C_REG_LENGTH8 1 +#define CODEC_I2C_REGVAL_LENGTH8 1 +#define CODEC_I2C_REGVAL_LENGTH16 2 + +#define MCLK1 1 +#define BCLK1 2 + +#ifdef __cplusplus +} +#endif + +#endif /* _DRIVER_CHIP_CODEC_H_ */ diff --git a/platform/mcu/xr871/src/driver/chip/codec/hal_ac101.c b/platform/mcu/xr871/src/driver/chip/codec/hal_ac101.c new file mode 100644 index 0000000000..f6b249469c --- /dev/null +++ b/platform/mcu/xr871/src/driver/chip/codec/hal_ac101.c @@ -0,0 +1,907 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "sys/io.h" +#include "kernel/os/os.h" +#include "../hal_base.h" +#include "codec.h" +#include "hal_ac101.h" + +#define AC101_DBG_ON 1 + +#if (AC101_DBG_ON == 1) +#define AC101_DEBUG(fmt, arg...) HAL_LOG(AC101_DBG_ON, "[AC101] "fmt, ##arg) +#else +#define AC101_DEBUG(fmt, arg...) +#endif +#define AC101_ERROR(fmt, arg...) HAL_LOG(1, "[AC101] "fmt, ##arg) + +/* PLL divisors */ +typedef struct { + uint32_t pll_in; + uint32_t pll_out; + uint32_t m; + uint32_t n_i; + uint32_t n_f; +} PLL_Div; + +typedef struct { + uint32_t samplerate; + uint32_t aif1_bclk_div; + uint32_t aif1_srbit; +} AIF1_Fs; + +typedef struct { + uint32_t aif1_lrlk_div; + uint32_t aif1_lrlk_bit; +} AIF1_Lrck; + +typedef struct { + uint32_t aif1_wsize_val; + uint32_t aif1_wsize_bit; +} AIF1_WordSize; + +#define AGC_ENABLE 0 +#define DRC_ENABLE 0 +#define AC101_I2C_ADDR 0x1a + +/* + * Note : pll code from original tdm/i2s driver. + * freq_out = freq_in * N/(m*(2k+1)) , k=1,N=N_i+N_f,N_f=factor*0.2; + */ +static const PLL_Div codec_pll_div[] = { + {128000, 22579200, 1, 529, 1}, + {192000, 22579200, 1, 352, 4}, + {256000, 22579200, 1, 264, 3}, + {384000, 22579200, 1, 176, 2},/*((176+2*0.2)*6000000)/(38*(2*1+1))*/ + {6000000, 22579200, 38, 429, 0},/*((429+0*0.2)*6000000)/(38*(2*1+1))*/ + {13000000, 22579200, 19, 99, 0}, + {19200000, 22579200, 25, 88, 1}, + {128000, 24576000, 1, 576, 0}, + {192000, 24576000, 1, 384, 0}, + {256000, 24576000, 1, 288, 0}, + {384000, 24576000, 1, 192, 0}, + {2048000, 24576000, 1, 36, 0}, + {6000000, 24576000, 25, 307, 1}, + {13000000, 24576000, 42, 238, 1}, + {19200000, 24576000, 25, 88, 1}, +}; + +static const AIF1_Fs codec_aif1_fs[] = { + {44100, 4, 7}, + {48000, 4, 8}, + {8000, 9, 0}, + {11025, 8, 1}, + {12000, 8, 2}, + {16000, 7, 3}, + {22050, 6, 4}, + {24000, 6, 5}, + {32000, 5, 6}, + {96000, 2, 9}, + {192000, 1, 10}, +}; + +static const AIF1_Lrck codec_aif1_lrck[] = { + {16, 0}, + {32, 1}, + {64, 2}, + {128, 3}, + {256, 4}, +}; + +static const AIF1_WordSize codec_aif1_wsize[] = { + {8, 0}, + {16, 1}, + {20, 2}, + {24, 3}, +}; + +static Volume spk_vol[] = { + {VOLUME_LEVEL0, 0}, + {VOLUME_LEVEL1, 1}, + {VOLUME_LEVEL2, 2}, + {VOLUME_LEVEL3, 3}, + {VOLUME_LEVEL4, 4}, + {VOLUME_LEVEL5, 5}, + {VOLUME_LEVEL6, 6}, + {VOLUME_LEVEL7, 7}, + {VOLUME_LEVEL8, 8}, + {VOLUME_LEVEL9, 9}, + {VOLUME_LEVEL10, 10}, + {VOLUME_LEVEL11, 11}, + {VOLUME_LEVEL12, 12}, + {VOLUME_LEVEL13, 13}, + {VOLUME_LEVEL14, 14}, + {VOLUME_LEVEL15, 15}, + {VOLUME_LEVEL16, 16}, + {VOLUME_LEVEL17, 17}, + {VOLUME_LEVEL18, 18}, + {VOLUME_LEVEL19, 19}, + {VOLUME_LEVEL20, 20}, + {VOLUME_LEVEL21, 21}, + {VOLUME_LEVEL22, 22}, + {VOLUME_LEVEL23, 23}, + {VOLUME_LEVEL24, 24}, + {VOLUME_LEVEL25, 25}, + {VOLUME_LEVEL26, 26}, + {VOLUME_LEVEL27, 27}, + {VOLUME_LEVEL28, 28}, + {VOLUME_LEVEL29, 29}, + {VOLUME_LEVEL30, 30}, + {VOLUME_LEVEL31, 31}, +}; + +static Volume phone_vol[] = { + {VOLUME_LEVEL0, 0}, + {VOLUME_LEVEL1, 2}, + {VOLUME_LEVEL2, 4}, + {VOLUME_LEVEL3, 6}, + {VOLUME_LEVEL4, 8}, + {VOLUME_LEVEL5, 10}, + {VOLUME_LEVEL6, 12}, + {VOLUME_LEVEL7, 14}, + {VOLUME_LEVEL8, 16}, + {VOLUME_LEVEL9, 18}, + {VOLUME_LEVEL10, 20}, + {VOLUME_LEVEL11, 22}, + {VOLUME_LEVEL12, 24}, + {VOLUME_LEVEL13, 26}, + {VOLUME_LEVEL14, 28}, + {VOLUME_LEVEL15, 30}, + {VOLUME_LEVEL16, 32}, + {VOLUME_LEVEL17, 34}, + {VOLUME_LEVEL18, 36}, + {VOLUME_LEVEL19, 38}, + {VOLUME_LEVEL20, 40}, + {VOLUME_LEVEL21, 42}, + {VOLUME_LEVEL22, 44}, + {VOLUME_LEVEL23, 46}, + {VOLUME_LEVEL24, 48}, + {VOLUME_LEVEL25, 50}, + {VOLUME_LEVEL26, 52}, + {VOLUME_LEVEL27, 54}, + {VOLUME_LEVEL28, 56}, + {VOLUME_LEVEL29, 58}, + {VOLUME_LEVEL30, 60}, + {VOLUME_LEVEL31, 63}, +}; + +static uint8_t speaker_double_used = 0; + +static void agc_config() +{ + snd_soc_update_bits(0xb4, (0x3<<6), (0x3<<6)); + snd_soc_update_bits(0x84, (0x3f<<8)|(0xff<<0), (0x31<<8)|(0x28<<0)); + snd_soc_update_bits(0x85, (0x3f<<8)|(0xff<<0), (0x31<<8)|(0x28<<0)); + snd_soc_update_bits(0x8a, (0x7fff<<0), (0x24<<0)); + snd_soc_update_bits(0x8b, (0x7fff<<0), 0x2<<0); + snd_soc_update_bits(0x8c, (0x7fff<<0), 0x24<<0); + snd_soc_update_bits(0x8d, (0x7fff<<0),(0x2<<0)); + snd_soc_update_bits(0x8e, (0x1f<<8)|(0x1f<<0),(0xf<<8)|(0xf<<0)); + snd_soc_update_bits(0x93, (0x7ff<<0),(0xfc<<0)); + snd_soc_write(0x94, 0xabb3); +} + +static void drc_config() +{ + snd_soc_update_bits(0xa3, (0x7ff<<0),(1<<0)); + snd_soc_write(0xa4, 0x2baf); + snd_soc_update_bits(0xa5, (0x7ff<<0),(1<<0)); + snd_soc_write(0xa6, 0x2baf); + snd_soc_update_bits(0xa7, (0x7ff<<0),(0<<0)); + snd_soc_write(0xa8, 0x44a); + snd_soc_update_bits(0xa9, (0x7ff<<0),(0<<0)); + snd_soc_write(0xaa, 0x1e06); + snd_soc_update_bits(0xab, (0x7ff<<0),(0x352<<0)); + snd_soc_write(0xac, 0x6910); + snd_soc_update_bits(0xad, (0x7ff<<0),(0x77a<<0)); + snd_soc_write(0xae, 0xaaaa); + snd_soc_update_bits(0xaf, (0x7ff<<0),(0x2de<<0)); + snd_soc_write(0xb0, 0xc982); + snd_soc_write(0x16, 0x9f9f); +} + +static void agc_enable(bool on) +{ + if (on) { + snd_soc_update_bits(MOD_CLK_ENA, (0x1<<7),(0x1<<7)); + snd_soc_update_bits(MOD_RST_CTRL, (0x1<<7),(0x1<<7)); + + snd_soc_update_bits(0x82, (0xf<<0)|(0x7<<12),(0x6<<0)|(0x7<<12)); + snd_soc_update_bits(0x83, (0xf<<0)|(0x7<<12),(0x6<<0)|(0x7<<12)); + } else { + snd_soc_update_bits(MOD_CLK_ENA, (0x1<<7),(0x0<<7)); + snd_soc_update_bits(MOD_RST_CTRL, (0x1<<7),(0x0<<7)); + + snd_soc_update_bits(0x82, (0xf<<0)|(0x7<<12),(0x0<<0)|(0x0<<12)); + snd_soc_update_bits(0x83, (0xf<<0)|(0x7<<12),(0x0<<0)|(0x0<<12)); + } +} + +static void drc_enable(bool on) +{ + if (on) { + snd_soc_write(0xb5, 0x80); + snd_soc_update_bits(MOD_CLK_ENA, (0x1<<6),(0x1<<6)); + snd_soc_update_bits(MOD_RST_CTRL, (0x1<<6),(0x1<<6)); + snd_soc_update_bits(0xa0, (0x7<<0),(0x7<<0)); + } else { + snd_soc_write(0xb5, 0x0); + snd_soc_update_bits(MOD_CLK_ENA, (0x1<<6),(0x0<<6)); + snd_soc_update_bits(MOD_RST_CTRL, (0x1<<6),(0x0<<6)); + snd_soc_update_bits(0xa0, (0x7<<0),(0x0<<0)); + } +} + +/* + * Set clock split ratio according to the pcm parameter. + */ +static int32_t AC101_SetClkdiv(DAI_FmtParam *fmtParam,uint32_t sampleRate) +{ + uint32_t i = 0; + uint32_t aif1_word_size = fmtParam->wordWidth; + uint32_t aif1_lrlk_div = 2*fmtParam->slotWidth; + + /*set BCLK/LRCK ratio*/ + for (i = 0; i < ARRAY_SIZE(codec_aif1_lrck); i++) { + if (codec_aif1_lrck[i].aif1_lrlk_div == aif1_lrlk_div) { + snd_soc_update_bits(AIF1_CLK_CTRL, (0x7<freqIn < 128000) || (fmtParam->freqIn > 24576000)) { + return HAL_INVALID; + } else if ((fmtParam->freqIn == 24576000) || (fmtParam->freqIn == 22579200)) { + switch (fmtParam->pllId) { + case MCLK1: + /*select aif1 clk source from mclk1 and enable aif1*/ + snd_soc_update_bits(SYSCLK_CTRL, (0x3<pllId) { + case MCLK1: + /*pll source from MCLK1*/ + snd_soc_update_bits(SYSCLK_CTRL, (0x3<freqIn) && (codec_pll_div[i].pll_out == fmtParam->freqOut)) { + m = codec_pll_div[i].m; + n_i = codec_pll_div[i].n_i; + n_f = codec_pll_div[i].n_f; + break; + } + } + /*config pll m*/ + snd_soc_update_bits(PLL_CTRL1, (0x3f<clkMode){ + case DAIFMT_CBM_CFM: /* codec clk & frm master, ap is slave*/ + reg_val |= (0x0<transferFormat){ + case DAIFMT_I2S: /* I2S1 mode */ + reg_val |= (0x0<signalInterval){ + case DAIFMT_NB_NF: /* normal bit clock + nor frame */ + reg_val &= ~(0x1<speaker_double_used) { + snd_soc_update_bits(SPKOUT_CTRL, (0x1f<double_speaker_val<single_speaker_val<speaker_double_used; + + snd_soc_update_bits(HPOUT_CTRL, (0x3f<headset_val<mainmic_val<headsetmic_val< VOLUME_MAX_LEVEL) + return -1; + uint32_t volume_val = 0; + switch (dev) { + case AUDIO_DEVICE_HEADPHONE: + volume_val = phone_vol[volume].reg_val; + snd_soc_update_bits(HPOUT_CTRL, (0x3f<RegValLength == CODEC_I2C_REGVAL_LENGTH16) + regValLength = 2; + else if (priv->RegValLength == CODEC_I2C_REGVAL_LENGTH8) + regValLength = 1; + else + return -1; + + if (priv->RegLength != CODEC_I2C_REG_LENGTH8) + return -1; + + ret = priv->read(priv->i2cId,priv->devAddr,reg,val,regValLength); + if (ret != regValLength) + return -1; + + uint16_t *reg_val = (uint16_t *)val; + if (regValLength == CODEC_I2C_REGVAL_LENGTH16) + return ((((*reg_val)&0xFF) << 8) |(((*reg_val)&0xFF00)>>8)); + else if(regValLength == CODEC_I2C_REGVAL_LENGTH8) + return ((*reg_val)&0xFF); + else + return -1; +} + +int32_t snd_soc_write(uint32_t reg, uint32_t reg_val) +{ + uint32_t ret; + CODEC_Priv *priv = &gCodecPriv; + uint8_t val[5]; + + int8_t regValLength = 0; + + if (priv->RegLength != CODEC_I2C_REG_LENGTH8) + return -1; + + if (priv->RegValLength == CODEC_I2C_REGVAL_LENGTH16) { + regValLength = 2; + val[0] = (reg_val & 0xFF00) >> 8; + val[1] = reg_val & 0xFF; + }else if (priv->RegValLength == CODEC_I2C_REGVAL_LENGTH8) { + regValLength = 1; + val[0] = reg_val & 0xFF; + } else + return -1; + + ret = priv->write(priv->i2cId,priv->devAddr, reg, val,regValLength); + if (ret != regValLength) + return -1; + else + return 0; +} + +/** + * @internal + * @brief update codec register bits. + * @param reg: codec register. + * @param mask: register mask. + * @param value: new value. + * @retval Returns 1 for change, 0 for no change, or negative error code. + */ +int32_t snd_soc_update_bits(uint32_t reg, uint32_t mask, uint32_t value) +{ + bool change; + uint32_t old, new; + int ret; + ret = snd_soc_read(reg); + if (ret < 0) + return ret; + old = ret; + new = (old & ~mask) | (value & mask); + change = old != new; + if (change) + ret = snd_soc_write(reg, new); + if (ret < 0) + return ret; + return change; +} + +/** + * @brief Enable or Disable output device. + * @param AUDIO_Device: audio device. + * @param on: enable or disable. + * @retval HAL state + */ +HAL_Status HAL_CODEC_Trigger(AUDIO_Device dev, uint32_t on) +{ + if (dev != AUDIO_DEVICE_HEADPHONE && dev != AUDIO_DEVICE_SPEAKER) + return HAL_ERROR; + CODEC_Priv *priv = &gCodecPriv; + + HAL_MutexLock(&priv->Lock, OS_WAIT_FOREVER); + if (priv->spk_cfg && dev == AUDIO_DEVICE_SPEAKER) { + HAL_GPIO_WritePin(priv->spk_cfg->ctrl_port, + priv->spk_cfg->ctrl_pin, + on ? priv->spk_cfg->ctrl_on_state : + priv->spk_cfg->ctrl_off_state); + } else {//headphone set in codec file + + if (priv->ctl_ops->setTrigger) + priv->ctl_ops->setTrigger(dev, on); + } + HAL_MutexUnlock(&priv->Lock); + + return HAL_OK; +} + +/** + * @brief Set output device mute or unmute. + * @param AUDIO_Device: audio device. + * @param mute: mute flag. + * @retval HAL state + */ +HAL_Status HAL_CODEC_Mute(AUDIO_Device dev, uint32_t mute) +{ + if (dev > AUDIO_DEVICE_MAINMIC || dev < AUDIO_DEVICE_HEADPHONE) + return HAL_ERROR; + + CODEC_Priv *priv = &gCodecPriv; + + HAL_MutexLock(&priv->Lock, OS_WAIT_FOREVER); + + if (dev == AUDIO_DEVICE_SPEAKER && priv->spk_cfg) { + HAL_GPIO_WritePin(priv->spk_cfg->ctrl_port, + priv->spk_cfg->ctrl_pin, + mute ? priv->spk_cfg->ctrl_off_state : + priv->spk_cfg->ctrl_on_state); + } + + if (dev == AUDIO_DEVICE_HEADPHONE) { + } + + HAL_MutexUnlock(&priv->Lock); + + return HAL_OK; +} + +/** + * @brief Set current stream output device or input device. + * @param AUDIO_Device: audio device. + * @retval HAL state + */ +HAL_Status HAL_CODEC_ROUTE_Set(AUDIO_Device dev) +{ + if (dev > AUDIO_DEVICE_MAINMIC || dev < AUDIO_DEVICE_HEADPHONE) + return HAL_ERROR; + + CODEC_Priv *priv = &gCodecPriv; + + HAL_MutexLock(&priv->Lock, OS_WAIT_FOREVER); + if (priv->spk_cfg && dev == AUDIO_DEVICE_HEADPHONE) { + HAL_GPIO_WritePin(priv->spk_cfg->ctrl_port, + priv->spk_cfg->ctrl_pin, + priv->spk_cfg->ctrl_off_state); + } + if (priv->ctl_ops->setRoute) + priv->ctl_ops->setRoute(dev); + + if (priv->spk_cfg && dev == AUDIO_DEVICE_SPEAKER) { + HAL_GPIO_WritePin(priv->spk_cfg->ctrl_port, + priv->spk_cfg->ctrl_pin, + priv->spk_cfg->ctrl_on_state); + } + HAL_MutexUnlock(&priv->Lock); + + return HAL_OK; +} + +/** + * @brief Set output device's volume gain when stream on. + * @param AUDIO_Device: audio output device. + * @param volume: the audio gain. + * @retval HAL state + */ +HAL_Status HAL_CODEC_VOLUME_LEVEL_Set(AUDIO_Device dev,int volume) +{ + CODEC_Priv *priv = &gCodecPriv; + if (volume > VOLUME_MAX_LEVEL) + return HAL_INVALID; + + HAL_MutexLock(&priv->Lock, OS_WAIT_FOREVER); + if (priv->ctl_ops->setVolume) + priv->ctl_ops->setVolume(dev, volume); + HAL_MutexUnlock(&priv->Lock); + + return HAL_OK; +} + +/** + * @brief Init output device's volume gain. + * @param AUDIO_Device: audio output device. + * @param volume: the audio gain. + * @retval HAL state + */ +HAL_Status HAL_CODEC_INIT_VOLUME_Set(AUDIO_Device dev,int volume) +{ + CODEC_Priv *priv = &gCodecPriv; + + if (volume > VOLUME_MAX_LEVEL) + return HAL_INVALID; + + HAL_MutexLock(&priv->Lock, OS_WAIT_FOREVER); + g_init_volume_value = volume; + HAL_MutexUnlock(&priv->Lock); + + return HAL_OK; +} + +/** + * @brief Init the codec mute state + * @param status: The status of output device. + * @retval HAL state + */ +HAL_Status HAL_CODEC_MUTE_STATUS_Init(int status) +{ + CODEC_Priv *priv = &gCodecPriv; + + HAL_MutexLock(&priv->Lock, OS_WAIT_FOREVER); + g_init_mute_status = status; + HAL_MutexUnlock(&priv->Lock); + + return HAL_OK; +} + +/** + * @brief Return the codec output device(mute or unmute) state + * @retval device state + */ +uint32_t HAL_CODEC_MUTE_STATUS_Get() +{ + return g_init_mute_status; +} + +/** + * @brief Open the Codec module according to the specified parameters + * in the DATA_Param. + * @param param: pointer to a DATA_Param structure that contains + * data format information + * @retval HAL status + */ +HAL_Status HAL_CODEC_Open(DATA_Param *param) +{ + if (!param) + return HAL_INVALID; + + CODEC_DEBUG("CODEC Open..\n"); + + CODEC_Priv *priv = &gCodecPriv; + + HAL_MutexLock(&priv->Lock, OS_WAIT_FOREVER); + if (!param->fmtParam) + priv->fmtParam = &gFmtParam; + else + priv->fmtParam = param->fmtParam; + + if (param->sampleRate) + priv->sampleRate = param->sampleRate; + else + priv->sampleRate = 48000; + + switch (param->audioDev) { + case AUDIO_DEVICE_HEADPHONE: + case AUDIO_DEVICE_SPEAKER: + priv->playBack = 1; + break; + case AUDIO_DEVICE_HEADPHONEMIC: + case AUDIO_DEVICE_MAINMIC: + priv->record = 1; + break; + default: + HAL_MutexUnlock(&priv->Lock); + return HAL_INVALID; + } + + if (priv->dai_ops->startup) + priv->dai_ops->startup(param->audioDev); + if (priv->dai_ops->setPll) + priv->dai_ops->setPll(priv->fmtParam); + if (priv->dai_ops->setClkdiv) + priv->dai_ops->setClkdiv(priv->fmtParam,priv->sampleRate); + if (priv->dai_ops->setFormat) + priv->dai_ops->setFormat(priv->fmtParam); + if (priv->ctl_ops->setRoute) + priv->ctl_ops->setRoute(param->audioDev); + if (priv->ctl_ops->setVolume && (param->audioDev < AUDIO_DEVICE_HEADPHONEMIC)) + priv->ctl_ops->setVolume(param->audioDev, g_init_volume_value); + HAL_MutexUnlock(&priv->Lock); + + return HAL_OK; +} + +/** + * @brief Close the Codec module + * + * @note The module is closed at the end of transaction to avoid power consumption + * @retval none + */ +HAL_Status HAL_CODEC_Close(uint32_t dir) +{ + CODEC_Priv *priv = &gCodecPriv; + + CODEC_DEBUG("CODEC Close..\n"); + + HAL_MutexLock(&priv->Lock, OS_WAIT_FOREVER); + if (dir == 0) { + priv->playBack = 0; + } else { + priv->record = 0; + } + if (priv->dai_ops->shutDown) + priv->dai_ops->shutDown(priv->playBack, priv->record); + HAL_MutexUnlock(&priv->Lock); + + return HAL_OK; +} + +#ifdef CONFIG_PM +static int codec_suspend(struct soc_device *dev, enum suspend_state_t state) +{ + CODEC_Priv *priv = &gCodecPriv; + + switch (state) { + case PM_MODE_SLEEP: + case PM_MODE_STANDBY: + case PM_MODE_HIBERNATION: + case PM_MODE_POWEROFF: + + HAL_MutexLock(&priv->Lock, OS_WAIT_FOREVER); + + if (priv->ops->setInitParam) + priv->ops->setInitParam(HAL_CODEC_DEINIT,NULL); + if (priv->ops->jackDetect) + priv->ops->jackDetect(HAL_CODEC_DEINIT,NULL); + if (priv->ops->setSysClk) + priv->ops->setSysClk(HAL_CODEC_DEINIT,NULL); + if (priv->ops->setPower) + priv->ops->setPower(HAL_CODEC_DEINIT,NULL); + HAL_BoardIoctl(HAL_BIR_PINMUX_DEINIT, + HAL_MKDEV(HAL_DEV_MAJOR_AUDIO_CODEC, 0), 0); + + HAL_MutexUnlock(&priv->Lock); + + break; + default: + break; + } + return 0; +} + +static int codec_resume(struct soc_device *dev, enum suspend_state_t state) +{ + CODEC_Priv *priv = &gCodecPriv; + switch (state) { + case PM_MODE_SLEEP: + case PM_MODE_STANDBY: + case PM_MODE_HIBERNATION: + + HAL_MutexLock(&priv->Lock, OS_WAIT_FOREVER); + + if (priv->ops->setPower) + priv->ops->setPower(HAL_CODEC_INIT,NULL); + if (priv->ops->setSysClk) + priv->ops->setSysClk(HAL_CODEC_INIT,NULL); + if (priv->ops->setInitParam) + priv->ops->setInitParam(HAL_CODEC_INIT,(void *)priv->initParam); + if (priv->ops->jackDetect) + priv->ops->jackDetect(HAL_CODEC_INIT,NULL); + HAL_BoardIoctl(HAL_BIR_PINMUX_INIT, + HAL_MKDEV(HAL_DEV_MAJOR_AUDIO_CODEC, 0), 0); + + HAL_MutexUnlock(&priv->Lock); + break; + + default: + break; + } + return 0; +} + +static struct soc_device_driver codec_drv = { + .name = "CODEC", + .suspend = codec_suspend, + .resume = codec_resume, +}; + +static struct soc_device codec_dev = { + .name = "CODEC", + .driver = &codec_drv, +}; + +#define CODEC_DEV (&codec_dev) +#else +#define CODEC_DEV NULL +#endif + +/** + * @brief Initializes the CODEC according to the specified parameters + * in the CODEC_Param. + * @param param: pointer to a CODEC_Param structure that contains + * the configuration information for CODEC module + * @retval HAL status + */ +HAL_Status HAL_CODEC_Init(const CODEC_Param *param) +{ + if (!param) + return HAL_INVALID; + + CODEC_DEBUG("CODEC INIT..\n"); + + CODEC_Priv *priv = &gCodecPriv; + memset(priv,0,sizeof(*priv)); + /* set i2s read/write interface */ + priv->read= param->read; + priv->write = param->write; + priv->i2cId = param->i2cId; + + if (!param->param) + priv->initParam = &gInitParam; + else + priv->initParam = param->param; + + int i = 0; + CODECP codec = NULL; + for (i = 0; strlen((char *)(codecs[i].name)); i++) { + if (strncmp((char *)(param->name), (char *)(codecs[i].name), + strlen((char *)(codecs[i].name))) == 0) { + codec = codecs[i].dev; + } + } + if (!codec) + return HAL_ERROR; + + HAL_MutexInit(&priv->Lock); + priv->ops = codec->ops; + priv->dai_ops = codec->dai_ops; + priv->ctl_ops = codec->ctl_ops; + priv->name = codec->name; + priv->devAddr = codec->devAddr; + priv->RegValLength = codec->RegValLength; + priv->RegLength = codec->RegLength; + + HAL_MutexLock(&priv->Lock, OS_WAIT_FOREVER); + if (priv->ops->setPower) + priv->ops->setPower(HAL_CODEC_INIT,NULL); + if (priv->ops->setSysClk) + priv->ops->setSysClk(HAL_CODEC_INIT,NULL); + + if (priv->ops->setInitParam) + priv->ops->setInitParam(HAL_CODEC_INIT,(void *)priv->initParam); + + if (priv->ops->jackDetect) + priv->ops->jackDetect(HAL_CODEC_INIT,NULL); +#ifdef CONFIG_PM + pm_register_ops(CODEC_DEV); +#endif + HAL_MutexUnlock(&priv->Lock); + + HAL_BoardIoctl(HAL_BIR_GET_CFG, + HAL_MKDEV(HAL_DEV_MAJOR_AUDIO_CODEC, 0), + (uint32_t)&priv->spk_cfg); + HAL_BoardIoctl(HAL_BIR_PINMUX_INIT, + HAL_MKDEV(HAL_DEV_MAJOR_AUDIO_CODEC, 0), 0); + return HAL_OK; +} + +/** + * @brief DeInitializes the CODEC peripheral(power, clk, jack) + * @retval HAL status + */ +HAL_Status HAL_CODEC_DeInit() +{ + CODEC_Priv *priv = &gCodecPriv; + + HAL_MutexLock(&priv->Lock, OS_WAIT_FOREVER); + if (priv->ops->setPower) + priv->ops->setPower(HAL_CODEC_DEINIT,NULL); + + if (priv->ops->setSysClk) + priv->ops->setSysClk(HAL_CODEC_DEINIT,NULL); + + if (priv->ops->setInitParam) + priv->ops->setInitParam(HAL_CODEC_DEINIT,NULL); + + if (priv->ops->jackDetect) + priv->ops->jackDetect(HAL_CODEC_DEINIT,NULL); + HAL_MutexUnlock(&priv->Lock); + + HAL_MutexDeinit(&priv->Lock); + return HAL_OK; +} diff --git a/platform/mcu/xr871/src/driver/chip/flashchip/flash_chip.c b/platform/mcu/xr871/src/driver/chip/flashchip/flash_chip.c new file mode 100644 index 0000000000..b9a30c46cd --- /dev/null +++ b/platform/mcu/xr871/src/driver/chip/flashchip/flash_chip.c @@ -0,0 +1,962 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include "driver/chip/flashchip/flash_chip.h" +#include "driver/chip/hal_flash.h" +#include "driver/chip/hal_xip.h" +#include "../hal_base.h" +#include "flash_default.h" + +#define FLASH_DEBUG(fmt, arg...) XR_DEBUG((DBG_OFF | XR_LEVEL_ALL), NOEXPAND, "[Flash chip debug] <%s : %d> " fmt "\n", __func__, __LINE__, ##arg) +#define FLASH_ALERT(fmt, arg...) XR_ALERT((DBG_ON | XR_LEVEL_ALL), NOEXPAND, "[Flash chip alert] <%s : %d> " fmt "\n", __func__, __LINE__, ##arg) +#define FLASH_ERROR(fmt, arg...) XR_ERROR((DBG_ON | XR_LEVEL_ALL), NOEXPAND, "[Flash chip error] <%s : %d> " fmt "\n", __func__, __LINE__, ##arg) +#define FLASH_NOWAY() XR_ERROR((DBG_ON | XR_LEVEL_ALL), NOEXPAND, "[Flash chip should not be here] <%s : %d> \n", __func__, __LINE__) +#define FLASH_NOTSUPPORT() FLASH_ALERT("not support command") + +typedef enum { + FLASH_INSTRUCTION_WREN = 0x06, /* write enable */ + FLASH_INSTRUCTION_WRDI = 0x04, /* write disable */ + FLASH_INSTRUCTION_RDID = 0x9F, /* jedec id */ + FLASH_INSTRUCTION_RDSR1 = 0x05, /* read status register-1 */ + FLASH_INSTRUCTION_WRSR1 = 0x01, /* write status register-1 */ + FLASH_INSTRUCTION_READ = 0x03, /* read data */ + FLASH_INSTRUCTION_FAST_READ = 0x0B, /* fast read */ + FLASH_INSTRUCTION_PP = 0x02, /* page program */ + FLASH_INSTRUCTION_ERASE_64KB = 0xD8, /* erase block(sector) 64k */ + FLASH_INSTRUCTION_ERASE_32KB = 0x52, /* erase block(sector) 32k */ + FLASH_INSTRUCTION_ERASE_4KB = 0x20, /* erase sector 4k */ + FLASH_INSTRUCTION_ERASE_CHIP = 0xC7, /* chip erase */ + FLASH_INSTRUCTION_WRSR = 0X01, /* write status register */ + FLASH_INSTRUCTION_FAST_READ_DO = 0x3B, /* fast read dual output */ + FLASH_INSTRUCTION_RDSR2 = 0x35, + FLASH_INSTRUCTION_RDSR3 = 0x15, + FLASH_INSTRUCTION_WRSR2 = 0x31, + FLASH_INSTRUCTION_WRSR3 = 0x11, + FLASH_INSTRUCTION_SRWREN = 0x50, + FLASH_INSTRUCTION_CE = 0x60, + FLASH_INSTRUCTION_EPSP = 0x75, + FLASH_INSTRUCTION_EPRS = 0x7A, + FLASH_INSTRUCTION_PWDN = 0xB9, + FLASH_INSTRUCTION_REL = 0xAB, + FLASH_INSTRUCTION_FAST_READ_DIO = 0xBB, + FLASH_INSTRUCTION_FAST_READ_QO = 0x6B, + FLASH_INSTRUCTION_FAST_READ_QIO = 0xEB, + FLASH_INSTRUCTION_EN_QPI = 0x38, + FLASH_INSTRUCTION_DIS_QPI = 0xFF, + FLASH_INSTRUCTION_RSEN = 0x66, + FLASH_INSTRUCTION_RESET = 0x99, + FLASH_INSTRUCTION_QPP = 0x32, + FLASH_INSTRUCTION_SRP = 0xC0, +} eSF_Instruction; + +FlashChipCtor *flashChipList[] = { + &DefaultFlashChip, /*default chip must be at the last*/ +}; + + +#define INSTRUCT_ZCREATE(cmd, addr, dummy, data) \ + InstructionField cmd, addr, dummy, data; \ + do { \ + HAL_Memset(&cmd, 0, sizeof(InstructionField)); \ + HAL_Memset(&addr, 0, sizeof(InstructionField)); \ + HAL_Memset(&dummy, 0, sizeof(InstructionField)); \ + HAL_Memset(&data, 0, sizeof(InstructionField)); \ + } while (0) + + +static uint32_t getJedecID(FlashDrvierBase *driver) +{ + int ret; + PCHECK(driver); + INSTRUCT_ZCREATE(cmd, addr, dummy, data); + + cmd.data = FLASH_INSTRUCTION_RDID; + cmd.len = 1; + cmd.line = 1; + data.pdata = (uint8_t *)&data.data; + data.line = 1; + data.len = 3; + + driver->open(driver); + ret = driver->read(driver, &cmd, NULL, NULL, &data); + if (ret != HAL_OK) + FLASH_ERROR("driver go some wrong: %d", ret); + driver->close(driver); + + return data.data; +} + +FlashChipBase *FlashChipCreate(FlashDrvierBase *driver) +{ + uint32_t list_size = sizeof(flashChipList) / sizeof(flashChipList[0]); + uint32_t jedec = getJedecID(driver); + FlashChipBase *base = NULL; + FlashChipCtor *ctor = NULL; + + FLASH_DEBUG("jedec: 0x%x, list_size: %d", jedec, list_size); + + while (list_size--) + { + ctor = flashChipList[list_size]; + if (ctor->mJedecId == jedec) + break; + } + + base = ctor->create(jedec); +/* base->writeEnable = defaultWriteEnable; + base->writeDisable = defaultWriteDisable; + base->readStatus = defaultReadStatus; + base->erase = defaultErase; + base->jedecID = defaultGetJedecID; + base->pageProgram = defaultPageProgram; + base->read = defaultRead; + + base->driverWrite = defaultDriverWrite; + base->driverRead = defaultdriverRead; + base->setFreq = defaultSetFreq; + base->switchReadMode = defaultSwitchReadMode; + base->enableXIP = defaultEnableXIP; + base->disableXIP = defaultDisableXIP; + base->isBusy = defaultIsBusy; + base->control = defaultControl; + base->minEraseSize = defaultGetMinEraseSize; + + base->writeStatus = NULL; + base->suspendErasePageprogram = NULL; + base->resumeErasePageprogram = NULL; + base->powerDown = NULL; + base->releasePowerDown = NULL; + base->enableQPIMode = NULL; + base->disableQPIMode = NULL; + base->enableReset = NULL; + base->reset = NULL; + base->uniqueID = NULL;*/ + ctor->init(base); + base->mDriver = driver; + + return base; +} + + +/* + Default Flash Chip Interface +*/ +typedef struct { + uint8_t SRP0: 1; + uint8_t SEC: 1; + uint8_t TB: 1; + uint8_t BP2: 1; + uint8_t BP1: 1; + uint8_t BP0: 1; + uint8_t WEL: 1; + uint8_t BUSY: 1; +} DefaultFlash_StatusRegister1; //this can be different in different flash chip. + +typedef struct { + union { + struct { + uint8_t SRP1: 1; + uint8_t QE: 1; + uint8_t RESERVED: 1; + uint8_t LB1: 1; + uint8_t LB2: 1; + uint8_t LB3: 1; + uint8_t CMP: 1; + uint8_t SUS: 1; + }; + uint8_t status; + }; +} DefaultFlash_StatusRegister2; //this can be different in different flash chip. + + +void defaultWriteEnable(FlashChipBase *base) +{ + PCHECK(base); + InstructionField cmd, addr, dummy, data; + + memset(&cmd, 0, sizeof(InstructionField)); + memset(&addr, 0, sizeof(InstructionField)); + memset(&dummy, 0, sizeof(InstructionField)); + memset(&data, 0, sizeof(InstructionField)); + + cmd.data = FLASH_INSTRUCTION_WREN; + cmd.line = 1; + + base->driverWrite(base, &cmd, NULL, NULL, NULL); +} + +void defaultWriteDisable(FlashChipBase *base) +{ + PCHECK(base); + InstructionField cmd, addr, dummy, data; + + memset(&cmd, 0, sizeof(InstructionField)); + memset(&addr, 0, sizeof(InstructionField)); + memset(&dummy, 0, sizeof(InstructionField)); + memset(&data, 0, sizeof(InstructionField)); + + cmd.data = FLASH_INSTRUCTION_WRDI; + cmd.line = 1; + + base->driverWrite(base, &cmd, NULL, NULL, NULL); +} + +int defaultReadStatus(FlashChipBase *base, FlashStatus reg, uint8_t *status) +{ + PCHECK(base); + INSTRUCT_ZCREATE(cmd, addr, dummy, data); + + if (!(reg & base->mReadStausSupport)) { + FLASH_NOTSUPPORT(); + return -1; + } + + if (reg == FLASH_STATUS1) + { + cmd.data = FLASH_INSTRUCTION_RDSR1; + } + else if (reg == FLASH_STATUS2) + { + cmd.data = FLASH_INSTRUCTION_RDSR2; + } + else if (reg == FLASH_STATUS3) + { + cmd.data = FLASH_INSTRUCTION_RDSR3; + } + else + FLASH_NOWAY(); + + data.pdata = (uint8_t *)status; + data.len = 1; + data.line = 1; + + return base->driverRead(base, &cmd, NULL, NULL, &data); +} + +int defaultWriteStatus(FlashChipBase *base, FlashStatus reg, uint8_t *status) +{ + PCHECK(base); + INSTRUCT_ZCREATE(cmd, addr, dummy, data); + + if (!(reg & base->mWriteStatusSupport)) { + FLASH_NOTSUPPORT(); + return HAL_INVALID; + } + + cmd.data = FLASH_INSTRUCTION_SRWREN; + cmd.line = 1; + + base->driverWrite(base, &cmd, NULL, NULL, NULL); + + + memset(&cmd, 0, sizeof(InstructionField)); + memset(&addr, 0, sizeof(InstructionField)); + memset(&dummy, 0, sizeof(InstructionField)); + memset(&data, 0, sizeof(InstructionField)); + + if (reg == FLASH_STATUS1) + { + cmd.data = FLASH_INSTRUCTION_WRSR1; + } + else if (reg == FLASH_STATUS2) + { + cmd.data = FLASH_INSTRUCTION_WRSR2; + } + else if (reg == FLASH_STATUS3) + { + cmd.data = FLASH_INSTRUCTION_WRSR3; + } + else + FLASH_NOWAY(); + + data.pdata = (uint8_t *)status; + data.len = 1; + data.line = 1; + + return base->driverWrite(base, &cmd, NULL, NULL, &data); +} + +int defaultErase(FlashChipBase *base, FlashEraseMode mode, uint32_t eaddr) +{ + PCHECK(base); + INSTRUCT_ZCREATE(cmd, addr, dummy, data); + + if (!(mode & base->mEraseSizeSupport)) { + FLASH_NOTSUPPORT(); + return HAL_INVALID; + } + + FLASH_DEBUG("mode: 0x%x; base->mEraseSizeSupport: 0x%x", mode, base->mEraseSizeSupport); + + if (mode == FLASH_ERASE_CHIP) + { + cmd.data = FLASH_INSTRUCTION_ERASE_CHIP; + base->driverWrite(base, &cmd, NULL, NULL, NULL); + return 0; + } + else if (mode == FLASH_ERASE_32KB) + { + cmd.data = FLASH_INSTRUCTION_ERASE_32KB; + } + else if (mode == FLASH_ERASE_64KB) + { + cmd.data = FLASH_INSTRUCTION_ERASE_64KB; + } + else if (mode == FLASH_ERASE_4KB) + { + cmd.data = FLASH_INSTRUCTION_ERASE_4KB; + } + else + FLASH_NOWAY(); + + addr.data = eaddr; + addr.line = 1; + + return base->driverWrite(base, &cmd, &addr, NULL, NULL); +} + +int defaultSuspendErasePageprogram(FlashChipBase *base) +{ + PCHECK(base); + INSTRUCT_ZCREATE(cmd, addr, dummy, data); + + cmd.data = FLASH_INSTRUCTION_EPSP; + return base->driverWrite(base, &cmd, NULL, NULL, NULL); +} + +int defaultResumeErasePageprogram(FlashChipBase *base) +{ + PCHECK(base); + INSTRUCT_ZCREATE(cmd, addr, dummy, data); + + cmd.data = FLASH_INSTRUCTION_EPRS; + return base->driverWrite(base, &cmd, NULL, NULL, NULL); +} + +int defaultPowerDown(FlashChipBase *base) +{ + PCHECK(base); + INSTRUCT_ZCREATE(cmd, addr, dummy, data); + + cmd.data = FLASH_INSTRUCTION_PWDN; + return base->driverWrite(base, &cmd, NULL, NULL, NULL); +} + +int defaultReleasePowerDown(FlashChipBase *base) +{ + PCHECK(base); + INSTRUCT_ZCREATE(cmd, addr, dummy, data); + + cmd.data = FLASH_INSTRUCTION_REL; + dummy.len = 3; + dummy.line = 1; + data.len = 1; + data.line = 1; + + return base->driverWrite(base, &cmd, NULL, &dummy, &data); +} + +int defaultGetJedecID(FlashChipBase *base, uint32_t *jedec) +{ + PCHECK(base); + INSTRUCT_ZCREATE(cmd, addr, dummy, data); + + cmd.data = FLASH_INSTRUCTION_RDID; + data.pdata = (uint8_t *)jedec; + data.line = 1; + data.len = 3; + return base->driverRead(base, &cmd, NULL, NULL, &data); +} + +int defaultEnableQPIMode(FlashChipBase *base) +{ + int ret; + uint32_t tmp; + PCHECK(base); + INSTRUCT_ZCREATE(cmd, addr, dummy, data); + + cmd.data = FLASH_INSTRUCTION_EN_QPI; + ret = base->driverWrite(base, &cmd, NULL, NULL, NULL); + + if (ret >= 0) + base->mFlashStatus |= FLASH_READ_QPI_MODE; + else + return ret; + + tmp = 0x03; + ret = base->control(base, DEFAULT_FLASH_SET_QPI_READ_P5P4, &tmp); + if (ret >= 0) + base->mDummyCount = 4; + + return ret; +} + +int defaultDisableQPIMode(FlashChipBase *base) +{ + int ret; + PCHECK(base); + INSTRUCT_ZCREATE(cmd, addr, dummy, data); + + cmd.data = FLASH_INSTRUCTION_DIS_QPI; + cmd.line = 4; + ret = base->driverWrite(base, &cmd, NULL, NULL, NULL); + if (ret == 0) + base->mFlashStatus &= ~FLASH_READ_QPI_MODE; + return ret; +} + +/* +int defaultEnableReset(FlashChipBase *base) +{ + PCHECK(base); + INSTRUCT_ZCREATE(cmd, addr, dummy, data); + + cmd.data = FLASH_INSTRUCTION_RSEN; + return base->driverWrite(base, &cmd, NULL, NULL, NULL); +} +*/ + +int defaultReset(FlashChipBase *base) +{ + int ret; + PCHECK(base); + INSTRUCT_ZCREATE(cmd, addr, dummy, data); + + cmd.data = FLASH_INSTRUCTION_RSEN; + ret = base->driverWrite(base, &cmd, NULL, NULL, NULL); + if (ret < 0) + return ret; + + HAL_Memset(&cmd, 0, sizeof(InstructionField)); + cmd.data = FLASH_INSTRUCTION_RESET; + return base->driverWrite(base, &cmd, NULL, NULL, NULL); +} + +int defaultGetUniqueID(FlashChipBase *base, uint8_t uid[8]) +{ + PCHECK(base); + INSTRUCT_ZCREATE(cmd, addr, dummy, data); + + cmd.data = FLASH_INSTRUCTION_RESET; + dummy.line = 1; + dummy.len = 4; + data.pdata = uid; + data.line = 1; + data.len = 8; + return base->driverRead(base, &cmd, NULL, &dummy, &data); +} + +int defaultPageProgram(FlashChipBase *base, FlashPageProgramMode mode, uint32_t waddr, uint8_t *wdata, uint32_t size) +{ + PCHECK(base); + INSTRUCT_ZCREATE(cmd, addr, dummy, data); + + if (size > base->mPageSize) + return -1; + + if (!(mode & base->mPageProgramSupport)) { + FLASH_NOTSUPPORT(); + return HAL_INVALID; + } + + if (((waddr + size) > base->mSize) || (size > base->mPageSize)) + return -1; + + if (mode == FLASH_PAGEPROGRAM) + { + cmd.data = FLASH_INSTRUCTION_PP; + data.line = 1; + } + else if (mode == FLASH_QUAD_PAGEPROGRAM) + { + cmd.data = FLASH_INSTRUCTION_QPP; + data.line = 4; + } + else + FLASH_NOWAY(); + + addr.data = waddr; + addr.line = 1; + data.pdata = wdata; + data.len = size; + return base->driverWrite(base, &cmd, &addr, NULL, &data); +} + +int defaultRead(FlashChipBase *base, FlashReadMode mode, uint32_t raddr, uint8_t *rdata, uint32_t size) +{ + PCHECK(base); + INSTRUCT_ZCREATE(cmd, addr, dummy, data); + + if (!(mode & base->mReadSupport)) { + FLASH_DEBUG("this flash chip not support read mode: %d", mode); + FLASH_NOTSUPPORT(); + return HAL_INVALID; + } + + if ((raddr + size) > base->mSize) + return -1; + + FLASH_DEBUG("size: %d; mode: %d", size, (uint32_t)mode); + + switch (mode) + { + /* !!! NOTICE: m7~m0 is count to dummy byte. !!! */ + case FLASH_READ_NORMAL_MODE: + cmd.data = FLASH_INSTRUCTION_READ; + addr.line = 1; + data.line = 1; + dummy.len = 0; + break; + case FLASH_READ_FAST_MODE: + cmd.data = FLASH_INSTRUCTION_FAST_READ; + addr.line = 1; + data.line = 1; + dummy.len = 1; + dummy.line = 1; + break; + case FLASH_READ_DUAL_O_MODE: + cmd.data = FLASH_INSTRUCTION_FAST_READ_DO; + addr.line = 1; + data.line = 2; + dummy.len = 1; + dummy.line = 1; + break; + case FLASH_READ_DUAL_IO_MODE: + cmd.data = FLASH_INSTRUCTION_FAST_READ_DIO; + addr.line = 2; + data.line = 2; + dummy.len = 1; + dummy.line = 2; + break; + case FLASH_READ_QUAD_O_MODE: + cmd.data = FLASH_INSTRUCTION_FAST_READ_QO; + addr.line = 1; + data.line = 4; + dummy.len = 1; + dummy.line = 1; + break; + case FLASH_READ_QUAD_IO_MODE: + cmd.data = FLASH_INSTRUCTION_FAST_READ_QIO; + addr.line = 4; + data.line = 4; + dummy.len = 3; + dummy.line = 4; + break; + case FLASH_READ_QPI_MODE: + cmd.data = FLASH_INSTRUCTION_FAST_READ_QIO; + cmd.line = 4; + data.line = 4; + dummy.len = base->mDummyCount; + dummy.line = 4; + break; + default: + return -1; + } + + addr.data = raddr; + data.pdata = rdata; + data.len = size; + return base->driverRead(base, &cmd, &addr, &dummy, &data); +} + +int defaultDriverWrite(FlashChipBase *base, InstructionField *cmd, InstructionField *addr, InstructionField *dummy, InstructionField *data) +{ + if (base == NULL || cmd == NULL) + return -1; + + if (!(base->mFlashStatus & FLASH_READ_QPI_MODE)) + { + cmd->len = 1; + cmd->line = 1; //not in QPI + + if (addr != NULL) + addr->len = 3; + if (data != NULL && data->pdata == NULL && data->len <= 4) + data->pdata = (uint8_t *)&data->data; + } + else + { + cmd->len = 1; + cmd->line = 4; //not in QPI + + if (addr != NULL) + { + addr->len = 3; + addr->line = 4; + } + if (dummy != NULL) + dummy->line = 4; + if (data != NULL) + data->line = 4; + if (data != NULL && data->pdata == NULL && data->len <= 4) + data->pdata = (uint8_t *)&data->data; + } + + if (base->mDriver == NULL || base->mDriver->write == NULL) + return -1; + + FLASH_DEBUG("cmd: 0x%x", cmd->data); + + return base->mDriver->write(base->mDriver, cmd, addr, dummy, data); +} + +int defaultDriverRead(FlashChipBase *base, InstructionField *cmd, InstructionField *addr, InstructionField *dummy, InstructionField *data) +{ + if (base == NULL || cmd == NULL) + return -1; + + if (!(base->mFlashStatus & FLASH_READ_QPI_MODE)) + { + cmd->len = 1; + cmd->line = 1; //not in QPI + + if (addr != NULL) + addr->len = 3; + if (data != NULL && data->pdata == NULL && data->len <= 4) + data->pdata = (uint8_t *)&data->data; + } + else /* in QPI mode */ + { + cmd->len = 1; + cmd->line = 4; //not in QPI + + if (addr != NULL) + { + addr->len = 3; + addr->line = 4; + } + if (dummy != NULL) + dummy->line = 4; + if (data != NULL) + data->line = 4; + if (data != NULL && data->pdata == NULL && data->len <= 4) + data->pdata = (uint8_t *)&data->data; + } + + if (base->mDriver == NULL || base->mDriver->read == NULL) + return -1; + + FLASH_DEBUG("cmd: 0x%x", cmd->data); + + return base->mDriver->read(base->mDriver, cmd, addr, dummy, data); +} + +int defaultXipDriverCfg(FlashChipBase *base, FlashReadMode mode) +{ + PCHECK(base); + INSTRUCT_ZCREATE(cmd, addr, dummy, data); + uint32_t continueMode = 0; /* flashc exit continue mode, needed a read with dummy */ + + if (base->mXip == NULL) + return -1; + + cmd.len = 1; + cmd.line = 1; //not in QPI + addr.len = 3; + switch (mode) + { + /* !!! NOTICE: m7~m0 is count to dummy byte. !!! */ + case FLASH_READ_NORMAL_MODE: + cmd.data = FLASH_INSTRUCTION_READ; + addr.line = 1; + data.line = 1; + dummy.len = 0; + dummy.line = 1; + continueMode = 0; + break; + case FLASH_READ_FAST_MODE: + cmd.data = FLASH_INSTRUCTION_FAST_READ; + addr.line = 1; + data.line = 1; + dummy.len = 1; + dummy.line = 1; + continueMode = 0; + break; + case FLASH_READ_DUAL_O_MODE: + cmd.data = FLASH_INSTRUCTION_FAST_READ_DO; + addr.line = 1; + data.line = 2; + dummy.len = 1; + dummy.line = 1; + continueMode = 0; + break; + case FLASH_READ_DUAL_IO_MODE: + cmd.data = FLASH_INSTRUCTION_FAST_READ_DIO; + addr.line = 2; + data.line = 2; + dummy.len = 1; + dummy.line = 2; + break; + case FLASH_READ_QUAD_O_MODE: + cmd.data = FLASH_INSTRUCTION_FAST_READ_QO; + addr.line = 1; + data.line = 4; + dummy.len = 1; + dummy.line = 1; + continueMode = 0; + break; + case FLASH_READ_QUAD_IO_MODE: + cmd.data = FLASH_INSTRUCTION_FAST_READ_QIO; + addr.line = 4; + data.line = 4; + dummy.len = 3; + dummy.line = 4; + break; + case FLASH_READ_QPI_MODE: + cmd.data = FLASH_INSTRUCTION_FAST_READ_QIO; + cmd.line = 4; + data.line = 4; + dummy.len = base->mDummyCount; + dummy.line = 4; + addr.line = 4; + break; + default: + return -1; + } + + base->mXip->setCmd(base->mXip, &cmd, &addr, &dummy, &data); + base->mXip->setContinue(base->mXip, continueMode, NULL); + /*TODO: xip set delay*/ + + return 0; +} + +int defaultSetFreq(FlashChipBase *base, uint32_t freq) +{ + PCHECK(base); + INSTRUCT_ZCREATE(cmd, addr, dummy, data); + + return base->mDriver->setFreq(base->mDriver, freq); +} + +int defaultSwitchReadMode(FlashChipBase *base, FlashReadMode mode) +{ + PCHECK(base); + uint8_t status; + int ret; + + if (!(mode & base->mReadSupport)) { + FLASH_NOTSUPPORT(); + return HAL_INVALID; + } + + if (mode == FLASH_READ_QUAD_O_MODE || mode == FLASH_READ_QUAD_IO_MODE || mode == FLASH_READ_QPI_MODE) + { + ret = base->readStatus(base, FLASH_STATUS2, &status); + if (ret < 0) + return -1; + status |= 1 << 1; + ret = base->writeStatus(base, FLASH_STATUS2, &status); + } + else + { + ret = base->readStatus(base, FLASH_STATUS2, &status); + if (ret < 0) + return -1; + status &= ~(1 << 1); + ret = base->writeStatus(base, FLASH_STATUS2, &status); + } + + return ret; +} + +int defaultEnableXIP(FlashChipBase *base) +{ + PCHECK(base); + INSTRUCT_ZCREATE(cmd, addr, dummy, data); + + /*TODO:*/ + + return 0; +} + +int defaultDisableXIP(FlashChipBase *base) +{ + PCHECK(base); + INSTRUCT_ZCREATE(cmd, addr, dummy, data); + + /*TODO:*/ + + return 0; +} + +int defaultIsBusy(FlashChipBase *base) +{ + PCHECK(base); + uint8_t status; + int ret; + + ret = base->readStatus(base, FLASH_STATUS1, &status); + if (ret < 0) + return -1; + + return !!(status & (1 << 0)); +} + +static int defaultSetReadParam(FlashChipBase *base, uint8_t param) +{ + PCHECK(base); + INSTRUCT_ZCREATE(cmd, addr, dummy, data); + + cmd.data = FLASH_INSTRUCTION_SRP; + data.pdata = (uint8_t *)¶m; + data.line = 4; + data.len = 1; + return base->driverWrite(base, &cmd, NULL, NULL, &data); +} + +int defaultControl(FlashChipBase *base, int op, void *param) +{ + PCHECK(base); + INSTRUCT_ZCREATE(cmd, addr, dummy, data); + + switch (op) + { + case DEFAULT_FLASH_SET_QPI_READ_P5P4: + if (*(uint8_t *)param > 0x03) + return -1; + defaultSetReadParam(base, (*(uint8_t *)param) << 4); + break; + } + + return 0; +} + +FlashEraseMode defaultGetMinEraseSize(FlashChipBase *base) +{ + PCHECK(base); + + if (base->mEraseSizeSupport & FLASH_ERASE_4KB) + return FLASH_ERASE_4KB; + else if (base->mEraseSizeSupport & FLASH_ERASE_32KB) + return FLASH_ERASE_32KB; + else if (base->mEraseSizeSupport & FLASH_ERASE_64KB) + return FLASH_ERASE_64KB; + else + return FLASH_ERASE_NOSUPPORT; +} + + +/* + Default Flash Chip, Only support basic Interface +*/ +#if 0 +typedef struct DefaultFlash +{ + FlashChipBase base; +} DefaultFlash; + +static int DefaultFlashInit(FlashChipBase * base) +{ + PCHECK(base); + + DefaultFlash *impl = __containerof(base, DefaultFlash, base); + impl->base.writeEnable = defaultWriteEnable; + impl->base.writeDisable = defaultWriteDisable; + impl->base.readStatus = defaultReadStatus; + impl->base.erase = defaultErase; + impl->base.jedecID = defaultGetJedecID; + impl->base.pageProgram = defaultPageProgram; + impl->base.read = defaultRead; + + impl->base.driverWrite = defaultDriverWrite; + impl->base.driverRead = defaultDriverRead; + impl->base.xipDriverCfg = defaultXipDriverCfg; + impl->base.setFreq = defaultSetFreq; + impl->base.switchReadMode = defaultSwitchReadMode; + impl->base.enableXIP = defaultEnableXIP; + impl->base.disableXIP = defaultDisableXIP; + impl->base.isBusy = defaultIsBusy; + impl->base.control = defaultControl; + impl->base.minEraseSize = defaultGetMinEraseSize; + + impl->base.writeStatus = NULL; + impl->base.suspendErasePageprogram = NULL; + impl->base.resumeErasePageprogram = NULL; + impl->base.powerDown = NULL; + impl->base.releasePowerDown = NULL; + impl->base.enableQPIMode = NULL; + impl->base.disableQPIMode = NULL; + impl->base.enableReset = NULL; + impl->base.reset = NULL; + impl->base.uniqueID = NULL; + /*TODO: a NULL interface for showing invalid interface*/ + + FLASH_DEBUG("default chip inited"); + + return 0; +} + +static int DefaultFlashDeinit(FlashChipBase * base) +{ + PCHECK(base); + + DefaultFlash *impl = __containerof(base, DefaultFlash, base); + free(impl); + + return 0; +} + +static FlashChipBase *DefaultFlashCtor(void) +{ + DefaultFlash *impl = malloc(sizeof(DefaultFlash)); + PCHECK(impl); + memset(impl, 0, sizeof(DefaultFlash)); + + FLASH_DEBUG("create default chip"); + + impl->base.mPageSize = 256; + impl->base.mSize = -1; + impl->base.mMaxFreq = -1; + impl->base.mMaxReadFreq = -1; +/* + impl->base.mEraseSizeSupport = FLASH_ERASE_64KB | FLASH_ERASE_CHIP; + impl->base.mPageProgramSupport = FLASH_PAGEPROGRAM; + impl->base.mReadStausSupport = FLASH_STATUS1; + impl->base.mWriteStatusSupport = FLASH_STATUS1; + impl->base.mReadSupport = FLASH_READ_NORMAL_MODE | FLASH_READ_FAST_MODE; +*/ + impl->base.mEraseSizeSupport = FLASH_ERASE_4KB | FLASH_ERASE_32KB | FLASH_ERASE_64KB | FLASH_ERASE_CHIP; + impl->base.mPageProgramSupport = FLASH_PAGEPROGRAM; + impl->base.mReadStausSupport = FLASH_STATUS1; + impl->base.mWriteStatusSupport = FLASH_STATUS1; + impl->base.mReadSupport = FLASH_READ_NORMAL_MODE | FLASH_READ_FAST_MODE | FLASH_READ_DUAL_O_MODE; + + return &impl->base; +} + +static FlashChipCtor DefaultFlashChip = { + .mJedecId = 0, + .create = DefaultFlashCtor, + .init = DefaultFlashInit, + .destory = DefaultFlashDeinit, +}; +#endif diff --git a/platform/mcu/xr871/src/driver/chip/flashchip/flash_default.c b/platform/mcu/xr871/src/driver/chip/flashchip/flash_default.c new file mode 100644 index 0000000000..58dadf73ae --- /dev/null +++ b/platform/mcu/xr871/src/driver/chip/flashchip/flash_default.c @@ -0,0 +1,275 @@ +/* + * flash_default.c + * + * Created on: 2017821 + * Author: lijunjie + */ + +#include +#include +#include "driver/chip/flashchip/flash_chip.h" +#include "flash_default.h" +#include "../hal_base.h" +#include "sys/xr_debug.h" + + +#define FLASH_DEBUG(fmt, arg...) XR_DEBUG((DBG_OFF | XR_LEVEL_ALL), NOEXPAND, "[Flash chip debug] <%s : %d> " fmt "\n", __func__, __LINE__, ##arg) +#define FLASH_ALERT(fmt, arg...) XR_ALERT((DBG_ON | XR_LEVEL_ALL), NOEXPAND, "[Flash chip alert] <%s : %d> " fmt "\n", __func__, __LINE__, ##arg) +#define FLASH_ERROR(fmt, arg...) XR_ERROR((DBG_ON | XR_LEVEL_ALL), NOEXPAND, "[Flash chip error] <%s : %d> " fmt "\n", __func__, __LINE__, ##arg) +#define FLASH_NOWAY() XR_ERROR((DBG_ON | XR_LEVEL_ALL), NOEXPAND, "[Flash chip should not be here] <%s : %d> \n", __func__, __LINE__) +#define FLASH_NOTSUPPORT() FLASH_ALERT("not support command") + + +typedef struct SimpleFlashChipCfg +{ + uint32_t mJedec; + uint32_t mSize; + + uint32_t mEraseSizeSupport; + uint16_t mReadStausSupport; + uint8_t mWriteStatusSupport; + uint8_t mPageProgramSupport; + uint16_t mReadSupport; + + uint32_t mMaxFreq; + uint32_t mMaxReadFreq; +} SimpleFlashChipCfg; + +static const SimpleFlashChipCfg simpleFlashChip[] = +{ +/* +* #ifdef FLASH_xxxxx +* { +* .mJedec = 0x ID7-ID0 ID15-ID8 M7-M0 from jedec id, +* .mSize = total flash memory size, +* .mEraseSizeSupport = the flash erase commands is supported, +* .mPageProgramSupport = the flash pageprogram commands is supported, +* .mReadStausSupport = the flash status registers can be read, +* .mWriteStatusSupport = the flash status registers can be written, +* .mReadSupport = the flash read commands (modes) is supported, +* .mMaxFreq = max operation frequency to flash, +* .mMaxReadFreq = max read command frequency(only read command: 0x03h), +* }, +* #endif +*/ + { + /* default config must be at first */ + .mJedec = 0, /* ID7-ID0 ID15-ID8 M7-M0 */ + .mSize = -1, + .mEraseSizeSupport = FLASH_ERASE_64KB | FLASH_ERASE_32KB | FLASH_ERASE_4KB | FLASH_ERASE_CHIP, + .mPageProgramSupport = FLASH_PAGEPROGRAM, + .mReadStausSupport = FLASH_STATUS1 | FLASH_STATUS2 | FLASH_STATUS3, + .mWriteStatusSupport = FLASH_STATUS1 | FLASH_STATUS2 | FLASH_STATUS3, + .mReadSupport = FLASH_READ_NORMAL_MODE | FLASH_READ_FAST_MODE | FLASH_READ_DUAL_O_MODE + | FLASH_READ_DUAL_IO_MODE | FLASH_READ_QUAD_O_MODE | FLASH_READ_QUAD_IO_MODE | FLASH_READ_QPI_MODE, + .mMaxFreq = -1, + .mMaxReadFreq = -1, + }, +#ifdef FLASH_PN25F16B + { + /* FLASH_PN25F16B */ + .mJedec = 0x15405E, + .mSize = 32 * 16 * 0x1000, + .mEraseSizeSupport = FLASH_ERASE_64KB | FLASH_ERASE_32KB | FLASH_ERASE_4KB | FLASH_ERASE_CHIP, + .mPageProgramSupport = FLASH_PAGEPROGRAM, + .mReadStausSupport = FLASH_STATUS1, + .mWriteStatusSupport = FLASH_STATUS1, + .mReadSupport = FLASH_READ_NORMAL_MODE | FLASH_READ_FAST_MODE | FLASH_READ_DUAL_O_MODE, + .mMaxFreq = 100 * 1000 * 1000, + .mMaxReadFreq = 55 * 1000 * 1000, + }, +#endif +#ifdef FLASH_M25P64 + { + /* FLASH_M25P64 */ + .mJedec = 0x172020, + .mSize = 128 * 0x10000, + .mEraseSizeSupport = FLASH_ERASE_64KB | FLASH_ERASE_CHIP, + .mPageProgramSupport = FLASH_PAGEPROGRAM, + .mReadStausSupport = FLASH_STATUS1, + .mWriteStatusSupport = 0, + .mReadSupport = FLASH_READ_NORMAL_MODE | FLASH_READ_FAST_MODE, + .mMaxFreq = 50 * 1000 * 1000, + .mMaxReadFreq = 20 * 1000 * 1000, + }, +#endif +#ifdef FLASH_W25Q16FW + { + /* FLASH_W25Q16FW */ + .mJedec = 0x1560EF, + .mSize = 32 *16 * 0x1000, + .mEraseSizeSupport = FLASH_ERASE_64KB | FLASH_ERASE_32KB | FLASH_ERASE_4KB | FLASH_ERASE_CHIP, + .mPageProgramSupport = FLASH_PAGEPROGRAM, + .mReadStausSupport = FLASH_STATUS1 | FLASH_STATUS2 | FLASH_STATUS3, + .mWriteStatusSupport = FLASH_STATUS1 | FLASH_STATUS2 | FLASH_STATUS3, + .mReadSupport = FLASH_READ_NORMAL_MODE | FLASH_READ_FAST_MODE | FLASH_READ_DUAL_O_MODE + | FLASH_READ_DUAL_IO_MODE | FLASH_READ_QUAD_O_MODE | FLASH_READ_QUAD_IO_MODE, + .mMaxFreq = 80 * 1000 * 1000, + .mMaxReadFreq = 50 * 1000 * 1000, + }, +#endif +#ifdef FLASH_PN25F08 + { + /* FLASH_PN25F08 */ + .mJedec = 0x1440E0, + .mSize = 16 * 16 * 0x1000, + .mEraseSizeSupport = FLASH_ERASE_64KB | FLASH_ERASE_32KB | FLASH_ERASE_4KB | FLASH_ERASE_CHIP, + .mPageProgramSupport = FLASH_PAGEPROGRAM, + .mReadStausSupport = FLASH_STATUS1 | FLASH_STATUS2, + .mWriteStatusSupport = FLASH_STATUS1 /* write status2 need to rewrite writeStatus */, + .mReadSupport = FLASH_READ_NORMAL_MODE | FLASH_READ_FAST_MODE | FLASH_READ_DUAL_O_MODE + | FLASH_READ_DUAL_IO_MODE | FLASH_READ_QUAD_O_MODE | FLASH_READ_QUAD_IO_MODE, + .mMaxFreq = 108 * 1000 * 1000, + .mMaxReadFreq = 55 * 1000 * 1000, + }, +#endif +#ifdef FLASH_PN25F16 + { + /* FLASH_PN25F16 */ + .mJedec = 0x1540E0, + .mSize = 32 * 16 * 0x1000, + .mEraseSizeSupport = FLASH_ERASE_64KB | FLASH_ERASE_32KB | FLASH_ERASE_4KB | FLASH_ERASE_CHIP, + .mPageProgramSupport = FLASH_PAGEPROGRAM, + .mReadStausSupport = FLASH_STATUS1 | FLASH_STATUS2, + .mWriteStatusSupport = FLASH_STATUS1 /* write status2 need to rewrite writeStatus */, + .mReadSupport = FLASH_READ_NORMAL_MODE | FLASH_READ_FAST_MODE | FLASH_READ_DUAL_O_MODE + | FLASH_READ_DUAL_IO_MODE | FLASH_READ_QUAD_O_MODE | FLASH_READ_QUAD_IO_MODE, + .mMaxFreq = 108 * 1000 * 1000, + .mMaxReadFreq = 55 * 1000 * 1000, + }, +#endif +#ifdef FLASH_MX25L1636E + { + /* FLASH_MX25L1636E */ + .mJedec = 0x1525C2, + .mSize = 32 * 16 * 0x1000, + .mEraseSizeSupport = FLASH_ERASE_64KB | FLASH_ERASE_4KB | FLASH_ERASE_CHIP, + .mPageProgramSupport = FLASH_PAGEPROGRAM /* QPP need to rewrite pageProgram */, + .mReadStausSupport = FLASH_STATUS1, + .mWriteStatusSupport = FLASH_STATUS1, + .mReadSupport = FLASH_READ_NORMAL_MODE | FLASH_READ_FAST_MODE | FLASH_READ_DUAL_O_MODE + | FLASH_READ_DUAL_IO_MODE | FLASH_READ_QUAD_IO_MODE, + .mMaxFreq = 108 * 1000 * 1000, + .mMaxReadFreq = 50 * 1000 * 1000, + }, +#endif +#ifdef FLASH_MX25L1633E + { + /* FLASH_MX25L1633E */ + .mJedec = 0x1524C2, + .mSize = 32 * 16 * 0x1000, + .mEraseSizeSupport = FLASH_ERASE_64KB | FLASH_ERASE_4KB | FLASH_ERASE_CHIP, + .mPageProgramSupport = FLASH_PAGEPROGRAM /* QPP need to rewrite pageProgram */, + .mReadStausSupport = FLASH_STATUS1, + .mWriteStatusSupport = FLASH_STATUS1, + .mReadSupport = FLASH_READ_NORMAL_MODE | FLASH_READ_FAST_MODE + | FLASH_READ_DUAL_IO_MODE | FLASH_READ_QUAD_IO_MODE, + .mMaxFreq = 85 * 1000 * 1000, + .mMaxReadFreq = 33 * 1000 * 1000, + }, +#endif +}; + + +/* + Default Flash Chip, Only support basic Interface +*/ +typedef struct DefaultFlash +{ + FlashChipBase base; +} DefaultFlash; + +static int DefaultFlashInit(FlashChipBase * base) +{ + PCHECK(base); + + DefaultFlash *impl = __containerof(base, DefaultFlash, base); + + impl->base.writeEnable = defaultWriteEnable; + impl->base.writeDisable = defaultWriteDisable; + impl->base.readStatus = defaultReadStatus; + impl->base.erase = defaultErase; + impl->base.jedecID = defaultGetJedecID; + impl->base.pageProgram = defaultPageProgram; + impl->base.read = defaultRead; + + impl->base.driverWrite = defaultDriverWrite; + impl->base.driverRead = defaultDriverRead; + impl->base.xipDriverCfg = defaultXipDriverCfg; + impl->base.setFreq = defaultSetFreq; + impl->base.switchReadMode = defaultSwitchReadMode; + impl->base.enableXIP = defaultEnableXIP; + impl->base.disableXIP = defaultDisableXIP; + impl->base.isBusy = defaultIsBusy; + impl->base.control = defaultControl; + impl->base.minEraseSize = defaultGetMinEraseSize; + impl->base.writeStatus = defaultWriteStatus; + impl->base.enableQPIMode = defaultEnableQPIMode; + impl->base.disableQPIMode = defaultDisableQPIMode; +// impl->base.enableReset = defaultEnableReset; + impl->base.reset = defaultReset; + + impl->base.suspendErasePageprogram = NULL; + impl->base.resumeErasePageprogram = NULL; + impl->base.powerDown = NULL; + impl->base.releasePowerDown = NULL; + impl->base.uniqueID = NULL; + /*TODO: a NULL interface for showing invalid interface*/ + + FLASH_DEBUG("default chip inited"); + + return 0; +} + +static int DefaultFlashDeinit(FlashChipBase * base) +{ + PCHECK(base); + + DefaultFlash *impl = __containerof(base, DefaultFlash, base); + free(impl); + + return 0; +} + +static FlashChipBase *DefaultFlashCtor(uint32_t arg) +{ + DefaultFlash *impl = malloc(sizeof(DefaultFlash)); +// SimpleFlashChipCfg *cfg = NULL; + uint32_t simpleChipVecSize = sizeof(simpleFlashChip) / sizeof(simpleFlashChip[0]); + uint32_t i = simpleChipVecSize; + + PCHECK(impl); + HAL_Memset(impl, 0, sizeof(DefaultFlash)); + + while (--i > 0) + { + if (simpleFlashChip[i].mJedec == arg) + break; + } + const SimpleFlashChipCfg *cfg = &simpleFlashChip[i]; + if (i == 0) + FLASH_ALERT("!!this chip is not in my support chip list, using default flash chip now!!"); + FLASH_DEBUG("create simple flash chip%d: 0x%x", i, cfg->mJedec); + + impl->base.mJedec = cfg->mJedec; + impl->base.mPageSize = 256; + impl->base.mSize = cfg->mSize; + impl->base.mMaxFreq = cfg->mMaxFreq; + impl->base.mMaxReadFreq = cfg->mMaxReadFreq; + impl->base.mEraseSizeSupport = cfg->mEraseSizeSupport; + impl->base.mPageProgramSupport = cfg->mPageProgramSupport; + impl->base.mReadStausSupport = cfg->mReadStausSupport; + impl->base.mWriteStatusSupport = cfg->mWriteStatusSupport; + impl->base.mReadSupport = cfg->mReadSupport; + impl->base.mFlashStatus = 0; + impl->base.mDummyCount = 1; + + return &impl->base; +} + +FlashChipCtor DefaultFlashChip = { + .mJedecId = 0, + .create = DefaultFlashCtor, + .init = DefaultFlashInit, + .destory = DefaultFlashDeinit, +}; diff --git a/platform/mcu/xr871/src/driver/chip/flashchip/flash_default.h b/platform/mcu/xr871/src/driver/chip/flashchip/flash_default.h new file mode 100644 index 0000000000..5f22cb27d7 --- /dev/null +++ b/platform/mcu/xr871/src/driver/chip/flashchip/flash_default.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef FLASH_DEFAULT_H_ +#define FLASH_DEFAULT_H_ + +#include "driver/chip/flashchip/flash_chip.h" + +#define FLASH_M25P64 + +#define FLASH_PN25F16B + +#define FLASH_W25Q16FW + +#define FLASH_PN25F08 + +#define FLASH_PN25F16 + + +extern FlashChipCtor DefaultFlashChip; + + +#endif diff --git a/platform/mcu/xr871/src/driver/chip/hal_adc.c b/platform/mcu/xr871/src/driver/chip/hal_adc.c new file mode 100644 index 0000000000..28cbbb2e70 --- /dev/null +++ b/platform/mcu/xr871/src/driver/chip/hal_adc.c @@ -0,0 +1,879 @@ +/** + * @file hal_adc.c + * @author XRADIO IOT WLAN Team + */ + +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "driver/chip/hal_adc.h" +#include "hal_base.h" +#include "pm/pm.h" + +typedef enum { + ADC_STATE_INVALID = 0, + ADC_STATE_INIT = 1, /* Initializing */ + ADC_STATE_DEINIT = 2, /* Deinitializing */ + ADC_STATE_READY = 3, + ADC_STATE_BUSY = 4 +} ADC_State; + +typedef struct { + uint16_t chanPinMux; + ADC_State state; + + uint32_t lowPending; + uint32_t highPending; + uint32_t dataPending; + + ADC_IRQCallback IRQCallback[ADC_CHANNEL_NUM]; + void *arg[ADC_CHANNEL_NUM]; +} ADC_Private; + +static ADC_Private gADCPrivate; + +#define ADC_ASSERT_CHANNEL(chan) HAL_ASSERT_PARAM((chan) < ADC_CHANNEL_NUM) + +#ifdef CONFIG_PM +static ADC_InitParam hal_adc_param; +static struct adc_chan_config{ + uint8_t is_config; + uint8_t select; + uint8_t mode; + uint32_t lowValue; + uint32_t highValue; +} hal_adc_chan_config[ADC_CHANNEL_NUM]; +static uint8_t hal_adc_suspending = 0; + +static int adc_suspend(struct soc_device *dev, enum suspend_state_t state) +{ + hal_adc_suspending = 1; + + switch (state) { + case PM_MODE_SLEEP: + break; + case PM_MODE_STANDBY: + case PM_MODE_HIBERNATION: + case PM_MODE_POWEROFF: + HAL_ADC_DeInit(); + break; + default: + break; + } + + return 0; +} + +static int adc_resume(struct soc_device *dev, enum suspend_state_t state) +{ + static ADC_Channel chan; + + switch (state) { + case PM_MODE_SLEEP: + break; + case PM_MODE_STANDBY: + case PM_MODE_HIBERNATION: + HAL_ADC_Init(&hal_adc_param); + for (chan = ADC_CHANNEL_0; chan < ADC_CHANNEL_NUM; chan++) { + if (hal_adc_chan_config[chan].is_config) + HAL_ADC_ConfigChannel(chan, + (ADC_Select)hal_adc_chan_config[chan].select, + (ADC_IRQMode)hal_adc_chan_config[chan].mode, + hal_adc_chan_config[chan].lowValue, + hal_adc_chan_config[chan].highValue); + } + break; + default: + break; + } + + hal_adc_suspending = 0; + + return 0; +} + +static struct soc_device_driver adc_drv = { + .name = "adc", + .suspend_noirq = adc_suspend, + .resume_noirq = adc_resume, +}; + +static struct soc_device adc_dev = { + .name = "adc", + .driver = &adc_drv, +}; + +#define ADC_DEV (&adc_dev) +#else +#define ADC_DEV NULL +#endif + +__STATIC_INLINE void ADC_SetChanPinMux(ADC_Channel chan) +{ + HAL_SET_BIT(gADCPrivate.chanPinMux, HAL_BIT(chan)); +} + +__STATIC_INLINE void ADC_ClrChanPinMux(ADC_Channel chan) +{ + HAL_CLR_BIT(gADCPrivate.chanPinMux, HAL_BIT(chan)); +} + +__STATIC_INLINE uint8_t ADC_GetChanPinMux(ADC_Channel chan) +{ + return !!HAL_GET_BIT(gADCPrivate.chanPinMux, HAL_BIT(chan)); +} + +__STATIC_INLINE void ADC_SetSampleRate(uint32_t fsDiv, uint32_t tAcq) +{ + ADC->SAMPLE_RATE = (HAL_GET_BIT(fsDiv << ADC_FS_DIV_SHIFT, ADC_FS_DIV_MASK) + | HAL_GET_BIT(tAcq << ADC_TACQ_SHIFT, ADC_TACQ_MASK)); +} + +__STATIC_INLINE void ADC_SetFirstDelay(uint32_t firstDelay) +{ + HAL_MODIFY_REG(ADC->CTRL, ADC_FIRST_DELAY_MASK, + HAL_GET_BIT(firstDelay << ADC_FIRST_DELAY_SHIFT, ADC_FIRST_DELAY_MASK)); +} + +__STATIC_INLINE void ADC_SetOPBias(uint32_t opBias) +{ + HAL_MODIFY_REG(ADC->CTRL, ADC_OP_BIAS_MASK, + HAL_GET_BIT(opBias << ADC_OP_BIAS_SHIFT, ADC_OP_BIAS_MASK)); +} + +__STATIC_INLINE void ADC_SetWorkMode(ADC_WorkMode workMode) +{ + HAL_MODIFY_REG(ADC->CTRL, ADC_WORK_MODE_MASK, + HAL_GET_BIT(workMode << ADC_WORK_MODE_SHIFT, ADC_WORK_MODE_MASK)); +} + +__STATIC_INLINE void ADC_EnableCalib(void) +{ + HAL_SET_BIT(ADC->CTRL, ADC_CALIB_EN_BIT); +} + +__STATIC_INLINE uint32_t ADC_GetCalibState(void) +{ + return !!HAL_GET_BIT(ADC->CTRL, ADC_CALIB_EN_BIT); +} + +__STATIC_INLINE void ADC_EnableADC(void) +{ + HAL_SET_BIT(ADC->CTRL, ADC_EN_BIT); +} + +__STATIC_INLINE void ADC_DisableADC(void) +{ + HAL_CLR_BIT(ADC->CTRL, ADC_EN_BIT); +} + +__STATIC_INLINE void ADC_EnableVbatDetec(void) +{ + HAL_SET_BIT(ADC->CTRL, ADC_VBAT_EN_BIT); +} + +__STATIC_INLINE void ADC_DisableVbatDetec(void) +{ + HAL_CLR_BIT(ADC->CTRL, ADC_VBAT_EN_BIT); +} + +__STATIC_INLINE void ADC_EnableLDO(void) +{ + HAL_SET_BIT(ADC->CTRL, ADC_LDO_EN_BIT); +} + +__STATIC_INLINE void ADC_DisableLDO(void) +{ + HAL_CLR_BIT(ADC->CTRL, ADC_LDO_EN_BIT); +} + +__STATIC_INLINE void ADC_EnableChanCmp(ADC_Channel chan) +{ + HAL_SET_BIT(ADC->CMP_SEL_EN, HAL_BIT(ADC_CMP_EN_SHIFT + chan)); +} + +__STATIC_INLINE void ADC_DisableChanCmp(ADC_Channel chan) +{ + HAL_CLR_BIT(ADC->CMP_SEL_EN, HAL_BIT(ADC_CMP_EN_SHIFT + chan)); +} + +__STATIC_INLINE void ADC_DisableAllChanCmp(void) +{ + HAL_CLR_BIT(ADC->CMP_SEL_EN, ADC_CMP_EN_MASK); +} + +__STATIC_INLINE void ADC_EnableChanSel(ADC_Channel chan) +{ + HAL_SET_BIT(ADC->CMP_SEL_EN, HAL_BIT(ADC_SEL_EN_SHIFT + chan)); +} + +__STATIC_INLINE void ADC_DisableChanSel(ADC_Channel chan) +{ + HAL_CLR_BIT(ADC->CMP_SEL_EN, HAL_BIT(ADC_SEL_EN_SHIFT + chan)); +} + +__STATIC_INLINE void ADC_DisableAllChanSel(void) +{ + HAL_CLR_BIT(ADC->CMP_SEL_EN, ADC_SEL_EN_MASK); +} + +__STATIC_INLINE void ADC_EnableChanLowIRQ(ADC_Channel chan) +{ + HAL_SET_BIT(ADC->LOW_CONFIG, HAL_BIT(chan)); +} + +__STATIC_INLINE void ADC_DisableChanLowIRQ(ADC_Channel chan) +{ + HAL_CLR_BIT(ADC->LOW_CONFIG, HAL_BIT(chan)); +} + +__STATIC_INLINE uint8_t ADC_GetChanLowIRQ(ADC_Channel chan) +{ + return !!HAL_GET_BIT(ADC->LOW_CONFIG, HAL_BIT(chan)); +} + +__STATIC_INLINE void ADC_EnableChanHighIRQ(ADC_Channel chan) +{ + HAL_SET_BIT(ADC->HIGH_CONFIG, HAL_BIT(chan)); +} + +__STATIC_INLINE void ADC_DisableChanHighIRQ(ADC_Channel chan) +{ + HAL_CLR_BIT(ADC->HIGH_CONFIG, HAL_BIT(chan)); +} + +__STATIC_INLINE uint8_t ADC_GetChanHighIRQ(ADC_Channel chan) +{ + return !!HAL_GET_BIT(ADC->HIGH_CONFIG, HAL_BIT(chan)); +} + +__STATIC_INLINE void ADC_EnableChanDataIRQ(ADC_Channel chan) +{ + HAL_SET_BIT(ADC->DATA_CONFIG, HAL_BIT(chan)); +} + +__STATIC_INLINE void ADC_DisableChanDataIRQ(ADC_Channel chan) +{ + HAL_CLR_BIT(ADC->DATA_CONFIG, HAL_BIT(chan)); +} + +__STATIC_INLINE uint8_t ADC_GetChanDataIRQ(ADC_Channel chan) +{ + return !!HAL_GET_BIT(ADC->DATA_CONFIG, HAL_BIT(chan)); +} + +__STATIC_INLINE void ADC_DisableAllChanIRQ(void) +{ + HAL_CLR_BIT(ADC->LOW_CONFIG, ADC_LOW_IRQ_MASK); + HAL_CLR_BIT(ADC->HIGH_CONFIG, ADC_HIGH_IRQ_MASK); + HAL_CLR_BIT(ADC->DATA_CONFIG, ADC_DATA_IRQ_MASK); +} + +__STATIC_INLINE uint32_t ADC_GetLowPending(void) +{ + return HAL_GET_BIT(ADC->LOW_STATUS, ADC_LOW_PENDING_MASK); +} + +__STATIC_INLINE void ADC_ClrLowPending(uint32_t lowPending) +{ + ADC->LOW_STATUS = lowPending; +} + +__STATIC_INLINE uint32_t ADC_GetHighPending(void) +{ + return HAL_GET_BIT(ADC->HIGH_STATUS, ADC_HIGH_PENDING_MASK); +} + +__STATIC_INLINE void ADC_ClrHighPending(uint32_t highPending) +{ + ADC->HIGH_STATUS = highPending; +} + +__STATIC_INLINE uint32_t ADC_GetDataPending(void) +{ + return HAL_GET_BIT(ADC->DATA_STATUS, ADC_DATA_PENDING_MASK); +} + +__STATIC_INLINE void ADC_ClrDataPending(uint32_t dataPending) +{ + ADC->DATA_STATUS = dataPending; +} + +__STATIC_INLINE void ADC_SetHighValue(ADC_Channel chan, uint32_t value) +{ + HAL_MODIFY_REG(ADC->CMP_DATA[chan], ADC_HIGH_DATA_MASK, + HAL_GET_BIT(value << ADC_HIGH_DATA_SHIFT, ADC_HIGH_DATA_MASK)); +} + +__STATIC_INLINE void ADC_SetLowValue(ADC_Channel chan, uint32_t value) +{ + HAL_MODIFY_REG(ADC->CMP_DATA[chan], ADC_LOW_DATA_MASK, + HAL_GET_BIT(value << ADC_LOW_DATA_SHIFT, ADC_LOW_DATA_MASK)); +} + +__STATIC_INLINE uint32_t ADC_GetValue(ADC_Channel chan) +{ + return HAL_GET_BIT(ADC->DATA[chan], ADC_DATA_MASK); +} + +void GPADC_IRQHandler(void) +{ + uint32_t i; + + gADCPrivate.lowPending = ADC_GetLowPending(); + gADCPrivate.highPending = ADC_GetHighPending(); + gADCPrivate.dataPending = ADC_GetDataPending(); + + ADC_ClrLowPending(gADCPrivate.lowPending); + ADC_ClrHighPending(gADCPrivate.highPending); + ADC_ClrDataPending(gADCPrivate.dataPending); + + for (i = ADC_CHANNEL_0; i < ADC_CHANNEL_NUM; i++) { + if (((HAL_GET_BIT(gADCPrivate.dataPending, HAL_BIT(i)) && ADC_GetChanDataIRQ(i)) + || (HAL_GET_BIT(gADCPrivate.lowPending, HAL_BIT(i)) && ADC_GetChanLowIRQ(i)) + || (HAL_GET_BIT(gADCPrivate.highPending, HAL_BIT(i)) && ADC_GetChanHighIRQ(i))) + && (gADCPrivate.IRQCallback[i])) { + gADCPrivate.IRQCallback[i](gADCPrivate.arg[i]); + } + } +} + +/** + * @brief Initialize the ADC according to the specified parameters + * @param[in] initParam Pointer to ADC_InitParam structure + * @retval HAL_Status, HAL_OK on success + */ +HAL_Status HAL_ADC_Init(ADC_InitParam *initParam) +{ + unsigned long flags; + ADC_Private *priv; + uint32_t hoscClk; + uint32_t fsDiv; + uint32_t tAcq; + + if ((initParam->freq < 1000) | (initParam->freq > 1000000)) { + HAL_ERR("invalid parameter, freq: %d\n", initParam->freq); + return HAL_ERROR; + } + + flags = HAL_EnterCriticalSection(); + priv = &gADCPrivate; + if (priv->state == ADC_STATE_INVALID) + priv->state = ADC_STATE_INIT; + else + priv = NULL; + HAL_ExitCriticalSection(flags); + + if (priv == NULL) { + HAL_WRN("ADC state: %d\n", gADCPrivate.state); + return HAL_BUSY; + } + +#ifdef CONFIG_PM + if (!hal_adc_suspending) { + pm_register_ops(ADC_DEV); + HAL_Memcpy(&hal_adc_param, initParam, sizeof(ADC_InitParam)); + HAL_Memset(hal_adc_chan_config, 0, ADC_CHANNEL_NUM * sizeof(struct adc_chan_config)); + } +#endif + + priv->chanPinMux = 0; + priv->lowPending = 0; + priv->highPending = 0; + priv->dataPending = 0; +#ifdef CONFIG_PM + if (!hal_adc_suspending) { + HAL_Memset(priv->IRQCallback, 0, ADC_CHANNEL_NUM * sizeof(ADC_IRQCallback)); + HAL_Memset(priv->arg, 0, ADC_CHANNEL_NUM * sizeof(void *)); + } +#else + HAL_Memset(priv->IRQCallback, 0, ADC_CHANNEL_NUM * sizeof(ADC_IRQCallback)); + HAL_Memset(priv->arg, 0, ADC_CHANNEL_NUM * sizeof(void *)); +#endif + + /* enable ADC clock and release reset */ + HAL_CCM_BusEnablePeriphClock(CCM_BUS_PERIPH_BIT_GPADC); + HAL_CCM_BusReleasePeriphReset(CCM_BUS_PERIPH_BIT_GPADC); + HAL_CCM_GPADC_SetMClock(CCM_APB_PERIPH_CLK_SRC_LFCLK, CCM_PERIPH_CLK_DIV_N_1, CCM_PERIPH_CLK_DIV_M_1); + HAL_CCM_GPADC_EnableMClock(); + + hoscClk = HAL_GetHFClock(); + fsDiv = hoscClk / initParam->freq - 1; + if (initParam->freq <= 300000) + tAcq = hoscClk / 500000 - 1; + else if (initParam->freq <= 600000) + tAcq = hoscClk / 1000000 - 1; + else + tAcq = hoscClk / 2000000 - 1; + ADC_SetSampleRate(fsDiv, tAcq); + + ADC_SetFirstDelay(initParam->delay); + ADC_SetWorkMode(ADC_CONTI_CONV); + ADC_EnableLDO(); + + ADC_DisableAllChanSel(); + ADC_DisableAllChanCmp(); + ADC_DisableAllChanIRQ(); + + ADC_EnableCalib(); + while (ADC_GetCalibState()) + ; + + /* enable NVIC IRQ */ + HAL_NVIC_SetPriority(GPADC_IRQn, NVIC_PERIPHERAL_PRIORITY_DEFAULT); + HAL_NVIC_EnableIRQ(GPADC_IRQn); + + flags = HAL_EnterCriticalSection(); + priv->state = ADC_STATE_READY; + HAL_ExitCriticalSection(flags); + + return HAL_OK; +} + +/** + * @brief DeInitialize the ADC + * @retval HAL_Status, HAL_OK on success + */ +HAL_Status HAL_ADC_DeInit(void) +{ + unsigned long flags; + ADC_Private *priv; + ADC_Channel chan; + + flags = HAL_EnterCriticalSection(); + priv = &gADCPrivate; + if (priv->state == ADC_STATE_READY) + priv->state = ADC_STATE_DEINIT; + else + priv = NULL; + HAL_ExitCriticalSection(flags); + + if (priv == NULL) { + HAL_WRN("ADC state: %d\n", gADCPrivate.state); + return HAL_ERROR; + } + +#ifdef CONFIG_PM + if (!hal_adc_suspending) { + pm_unregister_ops(ADC_DEV); + } +#endif + + HAL_NVIC_DisableIRQ(GPADC_IRQn); + + ADC_DisableLDO(); + + /* disable ADC clock and force reset */ + HAL_CCM_GPADC_DisableMClock(); + HAL_CCM_BusForcePeriphReset(CCM_BUS_PERIPH_BIT_GPADC); + HAL_CCM_BusDisablePeriphClock(CCM_BUS_PERIPH_BIT_GPADC); + + if (gADCPrivate.chanPinMux) { + for (chan = ADC_CHANNEL_0; chan < ADC_CHANNEL_NUM; chan++) { + if (ADC_GetChanPinMux(chan) && (chan != ADC_CHANNEL_8)) { + HAL_BoardIoctl(HAL_BIR_PINMUX_DEINIT, HAL_MKDEV(HAL_DEV_MAJOR_ADC, chan), 0); + ADC_ClrChanPinMux(chan); + if (!gADCPrivate.chanPinMux) + break; + } + } + } + + flags = HAL_EnterCriticalSection(); + priv->state = ADC_STATE_INVALID; + HAL_ExitCriticalSection(flags); + + return HAL_OK; +} + +/** + * @brief The specified ADC channel convert once in polling mode + * @param[in] chan The specified ADC channel + * @param[in] data Pointer to the output data + * @param[in] msec Timeout value in millisecond of conversion + * HAL_WAIT_FOREVER for no timeout + * @retval HAL_Status, HAL_OK on success + */ +HAL_Status HAL_ADC_Conv_Polling(ADC_Channel chan, uint32_t *data, uint32_t msec) +{ + unsigned long flags; + ADC_Private *priv; + uint32_t stopTime; + uint8_t isTimeout; + + ADC_ASSERT_CHANNEL(chan); + + flags = HAL_EnterCriticalSection(); + priv = &gADCPrivate; + if (priv->state == ADC_STATE_READY) + priv->state = ADC_STATE_BUSY; + else + priv = NULL; + HAL_ExitCriticalSection(flags); + + if (priv == NULL) { + HAL_WRN("ADC state: %d\n", gADCPrivate.state); + return HAL_ERROR; + } + + if ((!ADC_GetChanPinMux(chan)) && (chan != ADC_CHANNEL_8)) { + HAL_BoardIoctl(HAL_BIR_PINMUX_INIT, HAL_MKDEV(HAL_DEV_MAJOR_ADC, chan), 0); + ADC_SetChanPinMux(chan); + } + + ADC_DisableAllChanSel(); + ADC_DisableAllChanCmp(); + ADC_DisableAllChanIRQ(); + + if (chan == ADC_CHANNEL_8) + ADC_EnableVbatDetec(); + + ADC_EnableChanSel(chan); + + if (msec == HAL_WAIT_FOREVER) + stopTime = 0xFFFFFFFF; + else + stopTime = HAL_TicksToMSecs(HAL_Ticks()) + msec; + + if (stopTime < msec) { + HAL_ERR("stopTime overflow.\n"); + return HAL_ERROR; + } + + isTimeout = 1; + ADC_EnableADC(); + while (HAL_TicksToMSecs(HAL_Ticks()) <= stopTime) { + if (HAL_GET_BIT(ADC_GetDataPending(), HAL_BIT(chan))) { + *data = ADC_GetValue(chan); + ADC_ClrDataPending(ADC_GetDataPending()); + isTimeout = 0; + break; + } + } + ADC_DisableADC(); + ADC_DisableChanSel(chan); + + if (chan == ADC_CHANNEL_8) + ADC_DisableVbatDetec(); + + if (ADC_GetChanPinMux(chan) && (chan != ADC_CHANNEL_8)) { + HAL_BoardIoctl(HAL_BIR_PINMUX_DEINIT, HAL_MKDEV(HAL_DEV_MAJOR_ADC, chan), 0); + ADC_ClrChanPinMux(chan); + } + + flags = HAL_EnterCriticalSection(); + priv->state = ADC_STATE_READY; + HAL_ExitCriticalSection(flags); + + if (isTimeout) { + HAL_WRN("ADC timeout.\n"); + return HAL_TIMEOUT; + } else { + return HAL_OK; + } +} + +/** + * @brief Enable interrupt callback function for the specified ADC channel + * @param[in] chan The specified ADC channel + * @param[in] cb The interrupt callback function + * @param[in] arg Argument of the interrupt callback function + * @retval HAL_Status, HAL_OK on success + */ +HAL_Status HAL_ADC_EnableIRQCallback(ADC_Channel chan, ADC_IRQCallback cb, void *arg) +{ + unsigned long flags; + ADC_Private *priv; + + ADC_ASSERT_CHANNEL(chan); + + flags = HAL_EnterCriticalSection(); + priv = &gADCPrivate; + if ((priv->state == ADC_STATE_READY) || (priv->state == ADC_STATE_BUSY)) { + priv->arg[chan] = arg; + priv->IRQCallback[chan] = cb; + } else { + priv = NULL; + } + HAL_ExitCriticalSection(flags); + + if (priv == NULL) { + HAL_WRN("ADC state: %d\n", gADCPrivate.state); + return HAL_ERROR; + } + + return HAL_OK; +} + +/** + * @brief Disable interrupt callback function for the specified ADC channel + * @param[in] chan The specified ADC channel + * @retval HAL_Status, HAL_OK on success + */ +HAL_Status HAL_ADC_DisableIRQCallback(ADC_Channel chan) +{ + unsigned long flags; + ADC_Private *priv; + + ADC_ASSERT_CHANNEL(chan); + + flags = HAL_EnterCriticalSection(); + priv = &gADCPrivate; + if ((priv->state == ADC_STATE_READY) || (priv->state == ADC_STATE_BUSY)) { + priv->IRQCallback[chan] = NULL; + priv->arg[chan] = NULL; + } else { + priv = NULL; + } + HAL_ExitCriticalSection(flags); + + if (priv == NULL) { + HAL_WRN("ADC state: %d\n", gADCPrivate.state); + return HAL_ERROR; + } + + return HAL_OK; +} + +/** + * @brief Configure the specified ADC channel for conversion in interrupt mode + * @param[in] chan The specified ADC channel + * @param[in] select ADC channel selected state + * @param[in] mode ADC interrupt mode + * @param[in] lowValue lower limit value in interrupt mode of ADC_IRQ_LOW, + * ADC_IRQ_LOW_DATA, ADC_IRQ_LOW_HIGH or ADC_IRQ_LOW_HIGH_DATA + * @param[in] highValue Upper limit value in interrupt mode of ADC_IRQ_HIGH, + * ADC_IRQ_HIGH_DATA, ADC_IRQ_LOW_HIGH or ADC_IRQ_LOW_HIGH_DATA + * @retval HAL_Status, HAL_OK on success + */ +HAL_Status HAL_ADC_ConfigChannel(ADC_Channel chan, ADC_Select select, ADC_IRQMode mode, uint32_t lowValue, uint32_t highValue) +{ + unsigned long flags; + + ADC_ASSERT_CHANNEL(chan); + + if (((mode == ADC_IRQ_LOW_HIGH_DATA) || (mode == ADC_IRQ_LOW_HIGH)) && (lowValue > highValue)) { + HAL_ERR("lowValue greater than highValue.\n"); + return HAL_ERROR; + } + +#ifdef CONFIG_PM + hal_adc_chan_config[chan].is_config = 1; + hal_adc_chan_config[chan].select = select; + hal_adc_chan_config[chan].mode = mode; + hal_adc_chan_config[chan].lowValue = lowValue; + hal_adc_chan_config[chan].highValue = highValue; +#endif + + flags = HAL_EnterCriticalSection(); + if ((gADCPrivate.state == ADC_STATE_READY) || (gADCPrivate.state == ADC_STATE_BUSY)) { + if (gADCPrivate.state == ADC_STATE_BUSY) + ADC_DisableADC(); + if (select == ADC_SELECT_DISABLE) { + ADC_DisableChanSel(chan); + if (chan == ADC_CHANNEL_8) + ADC_DisableVbatDetec(); + ADC_DisableChanDataIRQ(chan); + ADC_DisableChanCmp(chan); + ADC_DisableChanLowIRQ(chan); + ADC_DisableChanHighIRQ(chan); + if (ADC_GetChanPinMux(chan) && (chan != ADC_CHANNEL_8)) { + HAL_BoardIoctl(HAL_BIR_PINMUX_DEINIT, HAL_MKDEV(HAL_DEV_MAJOR_ADC, chan), 0); + ADC_ClrChanPinMux(chan); + } + } else { + if ((!ADC_GetChanPinMux(chan)) && (chan != ADC_CHANNEL_8)) { + HAL_BoardIoctl(HAL_BIR_PINMUX_INIT, HAL_MKDEV(HAL_DEV_MAJOR_ADC, chan), 0); + ADC_SetChanPinMux(chan); + } + if (chan == ADC_CHANNEL_8) + ADC_EnableVbatDetec(); + ADC_EnableChanSel(chan); + switch (mode) { + case ADC_IRQ_NONE: + ADC_DisableChanDataIRQ(chan); + ADC_DisableChanCmp(chan); + ADC_DisableChanLowIRQ(chan); + ADC_DisableChanHighIRQ(chan); + break; + case ADC_IRQ_DATA: + ADC_EnableChanDataIRQ(chan); + ADC_DisableChanCmp(chan); + ADC_DisableChanLowIRQ(chan); + ADC_DisableChanHighIRQ(chan); + break; + case ADC_IRQ_LOW: + ADC_DisableChanDataIRQ(chan); + ADC_EnableChanCmp(chan); + ADC_EnableChanLowIRQ(chan); + ADC_SetLowValue(chan, lowValue); + ADC_DisableChanHighIRQ(chan); + break; + case ADC_IRQ_HIGH: + ADC_DisableChanDataIRQ(chan); + ADC_EnableChanCmp(chan); + ADC_DisableChanLowIRQ(chan); + ADC_EnableChanHighIRQ(chan); + ADC_SetHighValue(chan, highValue); + break; + case ADC_IRQ_LOW_DATA: + ADC_EnableChanDataIRQ(chan); + ADC_EnableChanCmp(chan); + ADC_EnableChanLowIRQ(chan); + ADC_SetLowValue(chan, lowValue); + ADC_DisableChanHighIRQ(chan); + break; + case ADC_IRQ_HIGH_DATA: + ADC_EnableChanDataIRQ(chan); + ADC_EnableChanCmp(chan); + ADC_DisableChanLowIRQ(chan); + ADC_EnableChanHighIRQ(chan); + ADC_SetHighValue(chan, highValue); + break; + case ADC_IRQ_LOW_HIGH: + ADC_DisableChanDataIRQ(chan); + ADC_EnableChanCmp(chan); + ADC_EnableChanLowIRQ(chan); + ADC_SetLowValue(chan, lowValue); + ADC_EnableChanHighIRQ(chan); + ADC_SetHighValue(chan, highValue); + break; + case ADC_IRQ_LOW_HIGH_DATA: + ADC_EnableChanDataIRQ(chan); + ADC_EnableChanCmp(chan); + ADC_EnableChanLowIRQ(chan); + ADC_SetLowValue(chan, lowValue); + ADC_EnableChanHighIRQ(chan); + ADC_SetHighValue(chan, highValue); + break; + } + } + if (gADCPrivate.state == ADC_STATE_BUSY) + ADC_EnableADC(); + HAL_ExitCriticalSection(flags); + return HAL_OK; + } else { + HAL_ExitCriticalSection(flags); + HAL_WRN("ADC state: %d\n", gADCPrivate.state); + return HAL_ERROR; + } +} + +/** + * @brief Start the ADC conversion in interrupt mode + * @retval HAL_Status, HAL_OK on success + */ +HAL_Status HAL_ADC_Start_Conv_IT(void) +{ + unsigned long flags; + ADC_Private *priv; + + flags = HAL_EnterCriticalSection(); + priv = &gADCPrivate; + if (priv->state == ADC_STATE_READY) { + ADC_EnableADC(); + priv->state = ADC_STATE_BUSY; + } else { + priv = NULL; + } + HAL_ExitCriticalSection(flags); + + if (priv == NULL) { + HAL_WRN("ADC state: %d\n", gADCPrivate.state); + return HAL_ERROR; + } + + return HAL_OK; +} + +/** + * @brief Stop the ADC conversion in interrupt mode + * @retval HAL_Status, HAL_OK on success + */ +HAL_Status HAL_ADC_Stop_Conv_IT(void) +{ + unsigned long flags; + ADC_Private *priv; + + flags = HAL_EnterCriticalSection(); + priv = &gADCPrivate; + if (priv->state == ADC_STATE_BUSY) { + ADC_DisableADC(); + priv->state = ADC_STATE_READY; + } else { + priv = NULL; + } + HAL_ExitCriticalSection(flags); + + if (priv == NULL) { + HAL_WRN("ADC state: %d\n", gADCPrivate.state); + return HAL_ERROR; + } + + return HAL_OK; +} + +/** + * @brief Get interrupt mode of the specified ADC channel + * @param[in] chan The specified ADC channel + * @retval HAL_Status, HAL_OK on success + */ +ADC_IRQState HAL_ADC_GetIRQState(ADC_Channel chan) +{ + ADC_ASSERT_CHANNEL(chan); + + if (HAL_GET_BIT(gADCPrivate.dataPending, HAL_BIT(chan)) && ADC_GetChanDataIRQ(chan)) { + if (HAL_GET_BIT(gADCPrivate.lowPending, HAL_BIT(chan)) && ADC_GetChanLowIRQ(chan)) + return ADC_LOW_DATA_IRQ; + if (HAL_GET_BIT(gADCPrivate.highPending, HAL_BIT(chan)) && ADC_GetChanHighIRQ(chan)) + return ADC_HIGH_DATA_IRQ; + else + return ADC_DATA_IRQ; + } else { + if (HAL_GET_BIT(gADCPrivate.lowPending, HAL_BIT(chan)) && ADC_GetChanLowIRQ(chan)) + return ADC_LOW_IRQ; + if (HAL_GET_BIT(gADCPrivate.highPending, HAL_BIT(chan)) && ADC_GetChanHighIRQ(chan)) + return ADC_HIGH_IRQ; + else + return ADC_NO_IRQ; + } +} + +/** + * @brief Get digital value of the specified ADC channel + * @param[in] chan The specified ADC channel + * @return Digital value converted by the specified ADC channel + */ +uint32_t HAL_ADC_GetValue(ADC_Channel chan) +{ + ADC_ASSERT_CHANNEL(chan); + + return ADC_GetValue(chan); +} + diff --git a/platform/mcu/xr871/src/driver/chip/hal_base.h b/platform/mcu/xr871/src/driver/chip/hal_base.h new file mode 100644 index 0000000000..08c453f5a3 --- /dev/null +++ b/platform/mcu/xr871/src/driver/chip/hal_base.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _DRIVER_CHIP_HAL_BASE_H_ +#define _DRIVER_CHIP_HAL_BASE_H_ + +#include "driver/chip/hal_def.h" +#include "driver/chip/hal_nvic.h" +#include "driver/chip/hal_clock.h" +#include "driver/chip/hal_prcm.h" +#include "driver/chip/hal_ccm.h" +#include "driver/chip/hal_util.h" + +#include "driver/hal_board.h" +#include "driver/hal_dev.h" + +#include "hal_debug.h" +#include "hal_os.h" + +#endif /* _DRIVER_CHIP_HAL_BASE_H_ */ diff --git a/platform/mcu/xr871/src/driver/chip/hal_board.c b/platform/mcu/xr871/src/driver/chip/hal_board.c new file mode 100644 index 0000000000..5e875ed70e --- /dev/null +++ b/platform/mcu/xr871/src/driver/chip/hal_board.c @@ -0,0 +1,65 @@ +/** + * @file hal_board.c + * @author XRADIO IOT WLAN Team + */ + +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "driver/hal_board.h" + +static HAL_BoardIoctlCb gBoardIoctlCb; + +/** + * @brief Register HAL board I/O control callback function + * @param[in] cb Callback function to be registered + * @return The previous callback function + */ +HAL_BoardIoctlCb HAL_BoardIoctlCbRegister(HAL_BoardIoctlCb cb) +{ + HAL_BoardIoctlCb old = gBoardIoctlCb; + gBoardIoctlCb = cb; + return old; +} + +/** + * @brief Execute HAL board I/O control request + * @note The registered callback function is called actually + * @param[in] req HAL board I/O control request + * @param[in] param0 Defined by HAL board I/O control request and driver + * @param[in] param1 Defined by HAL board I/O control request and driver + * @retval HAL_Status, HAL_OK on success + */ +HAL_Status HAL_BoardIoctl(HAL_BoardIoctlReq req, uint32_t param0, uint32_t param1) +{ + if (gBoardIoctlCb) + return gBoardIoctlCb(req, param0, param1); + else + return HAL_ERROR; +} diff --git a/platform/mcu/xr871/src/driver/chip/hal_ccm.c b/platform/mcu/xr871/src/driver/chip/hal_ccm.c new file mode 100644 index 0000000000..38cf8b0e99 --- /dev/null +++ b/platform/mcu/xr871/src/driver/chip/hal_ccm.c @@ -0,0 +1,661 @@ +/** + * @file hal_ccmc + * @author XRADIO IOT WLAN Team + */ + +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "hal_base.h" +#include "pm/pm.h" +#include "sys/xr_debug.h" + +/** + * @brief Configure AHB2 and APB clock + * @param[in] AHB2Div AHB2 clock divider + * @param[in] APBSrc APB clock source + * @param[in] APBDiv APB clock divider + * @return None + */ +void HAL_CCM_BusSetClock(CCM_AHB2ClkDiv AHB2Div, CCM_APBClkSrc APBSrc, CCM_APBClkDiv APBDiv) +{ + CCM->CPU_BUS_CLKCFG = AHB2Div | APBSrc | APBDiv; +} + +/** + * @brief Get AHB1 clock + * @return AHB1 clock in Hz + */ +uint32_t HAL_CCM_BusGetAHB1Clock(void) +{ + return HAL_PRCM_GetCPUAClk(); +} + +/** + * @brief Get AHB2 clock + * @return AHB2 clock in Hz + */ +uint32_t HAL_CCM_BusGetAHB2Clock(void) +{ + uint32_t div = ((CCM->CPU_BUS_CLKCFG & CCM_AHB2_CLK_DIV_MASK) >> CCM_AHB2_CLK_DIV_SHIFT) + 1; + return HAL_PRCM_GetCPUAClk() / div; +} + +/** + * @brief Get APB clock + * @return APB clock in Hz + */ +uint32_t HAL_CCM_BusGetAPBClock(void) +{ + uint32_t reg = CCM->CPU_BUS_CLKCFG; + uint32_t freq, div; + + switch (reg & CCM_APB_CLK_SRC_MASK) { + case CCM_APB_CLK_SRC_HFCLK: + freq = HAL_GetHFClock(); + break; + case CCM_APB_CLK_SRC_LFCLK: + freq = HAL_GetLFClock(); + break; + case CCM_APB_CLK_SRC_AHB2CLK: + default: + freq = HAL_GetAHB2Clock(); + break; + } + + div = ((reg & CCM_APB_CLK_DIV_MASK) >> CCM_APB_CLK_DIV_SHIFT); + return (freq >> div); +} + +/** + * @brief Enable peripheral clock + * @param[in] periphMask Bitmask of peripherals, refer to CCM_BusPeriphBit + * @return None + */ +void HAL_CCM_BusEnablePeriphClock(uint32_t periphMask) +{ + HAL_SET_BIT(CCM->BUS_PERIPH_CLK_CTRL, periphMask); +} + +/** + * @brief Disable peripheral clock + * @param[in] periphMask Bitmask of peripherals, refer to CCM_BusPeriphBit + * @return None + */ +void HAL_CCM_BusDisablePeriphClock(uint32_t periphMask) +{ + HAL_CLR_BIT(CCM->BUS_PERIPH_CLK_CTRL, periphMask); +} + +/** + * @brief Disable all peripherals clock + * @return None + */ +void HAL_CCM_BusDisableAllPeriphClock(void) +{ + CCM->BUS_PERIPH_CLK_CTRL = 0U; +} + +/** + * @brief Force peripheral reset + * @param[in] periphMask Bitmask of peripherals, refer to CCM_BusPeriphBit + * @return None + */ +void HAL_CCM_BusForcePeriphReset(uint32_t periphMask) +{ + HAL_CLR_BIT(CCM->BUS_PERIPH_RST_CTRL, periphMask); +} + +/** + * @brief Release peripheral reset + * @param[in] periphMask Bitmask of peripherals, refer to CCM_BusPeriphBit + * @return None + */ +void HAL_CCM_BusReleasePeriphReset(uint32_t periphMask) +{ + HAL_SET_BIT(CCM->BUS_PERIPH_RST_CTRL, periphMask); +} + +/** + * @brief Force all peripherals reset + * @return None + */ +void HAL_CCM_BusForceAllPeriphReset(void) +{ + CCM->BUS_PERIPH_RST_CTRL = 0U; +} + +/** + * @brief Configure SPI0 module clock + * @param[in] src SPI0 module clock source + * @param[in] divN SPI0 module clock divider N + * @param[in] divM SPI0 module clock divider M + * @return None + */ +void HAL_CCM_SPI0_SetMClock(CCM_AHBPeriphClkSrc src, CCM_PeriphClkDivN divN, CCM_PeriphClkDivM divM) +{ + HAL_MODIFY_REG(CCM->SPI0_MCLK_CTRL, CCM_PERIPH_CLK_PARAM_MASK, src | divN | divM); +} + +/** + * @brief Enable SPI0 module clock + * @return None + */ +void HAL_CCM_SPI0_EnableMClock(void) +{ + HAL_SET_BIT(CCM->SPI0_MCLK_CTRL, CCM_PERIPH_CLK_EN_BIT); +} + +/** + * @brief Disable SPI0 module clock + * @return None + */ +void HAL_CCM_SPI0_DisableMClock(void) +{ + HAL_CLR_BIT(CCM->SPI0_MCLK_CTRL, CCM_PERIPH_CLK_EN_BIT); +} + +/** + * @brief Configure SPI1 module clock + * @param[in] src SPI1 module clock source + * @param[in] divN SPI1 module clock divider N + * @param[in] divM SPI1 module clock divider M + * @return None + */ +void HAL_CCM_SPI1_SetMClock(CCM_AHBPeriphClkSrc src, CCM_PeriphClkDivN divN, CCM_PeriphClkDivM divM) +{ + HAL_MODIFY_REG(CCM->SPI1_MCLK_CTRL, CCM_PERIPH_CLK_PARAM_MASK, src | divN | divM); +} + +/** + * @brief Enable SPI1 module clock + * @return None + */ +void HAL_CCM_SPI1_EnableMClock(void) +{ + HAL_SET_BIT(CCM->SPI1_MCLK_CTRL, CCM_PERIPH_CLK_EN_BIT); +} + +/** + * @brief Disable SPI1 module clock + * @return None + */ +void HAL_CCM_SPI1_DisableMClock(void) +{ + HAL_CLR_BIT(CCM->SPI1_MCLK_CTRL, CCM_PERIPH_CLK_EN_BIT); +} + +/** + * @brief Configure SD controller module clock + * @param[in] src SD controller module clock source + * @param[in] divN SD controller module clock divider N + * @param[in] divM SD controller module clock divider M + * @return None + */ +void HAL_CCM_SDC_SetMClock(CCM_AHBPeriphClkSrc src, CCM_PeriphClkDivN divN, CCM_PeriphClkDivM divM) +{ + HAL_MODIFY_REG(CCM->SDC_MCLK_CTRL, CCM_PERIPH_CLK_PARAM_MASK, src | divN | divM); +} + +/** + * @brief Enable SD controller module clock + * @return None + */ +void HAL_CCM_SDC_EnableMClock(void) +{ + HAL_SET_BIT(CCM->SDC_MCLK_CTRL, CCM_PERIPH_CLK_EN_BIT); +} + +/** + * @brief Disable SD controller module clock + * @return None + */ +void HAL_CCM_SDC_DisableMClock(void) +{ + HAL_CLR_BIT(CCM->SDC_MCLK_CTRL, CCM_PERIPH_CLK_EN_BIT); +} + +/** + * @brief Configure Crypto Engine module clock + * @param[in] src Crypto Engine module clock source + * @param[in] divN Crypto Engine module clock divider N + * @param[in] divM Crypto Engine module clock divider M + * @return None + */ +void HAL_CCM_CE_SetMClock(CCM_AHBPeriphClkSrc src, CCM_PeriphClkDivN divN, CCM_PeriphClkDivM divM) +{ + HAL_MODIFY_REG(CCM->CE_MCLK_CTRL, CCM_PERIPH_CLK_PARAM_MASK, src | divN | divM); +} + +/** + * @brief Enable Crypto Engine module clock + * @return None + */ +void HAL_CCM_CE_EnableMClock(void) +{ + HAL_SET_BIT(CCM->CE_MCLK_CTRL, CCM_PERIPH_CLK_EN_BIT); +} + +/** + * @brief Disable Crypto Engine module clock + * @return None + */ +void HAL_CCM_CE_DisableMClock(void) +{ + HAL_CLR_BIT(CCM->CE_MCLK_CTRL, CCM_PERIPH_CLK_EN_BIT); +} + +/** + * @brief Configure CSI output clock + * @param[in] src CSI output clock source + * @param[in] divN CSI output clock divider N + * @param[in] divM CSI output clock divider M + * @return None + */ +void HAL_CCM_CSI_SetOutClock(CCM_AHBPeriphClkSrc src, CCM_PeriphClkDivN divN, CCM_PeriphClkDivM divM) +{ + HAL_MODIFY_REG(CCM->CSI_OCLK_CTRL, CCM_PERIPH_CLK_PARAM_MASK, src | divN | divM); +} + +/** + * @brief Enable CSI output clock + * @return None + */ +void HAL_CCM_CSI_EnableOutClock(void) +{ + HAL_SET_BIT(CCM->CSI_OCLK_CTRL, CCM_PERIPH_CLK_EN_BIT); +} + +/** + * @brief Disable CSI output clock + * @return None + */ +void HAL_CCM_CSI_DisableOutClock(void) +{ + HAL_CLR_BIT(CCM->CSI_OCLK_CTRL, CCM_PERIPH_CLK_EN_BIT); +} + +/** + * @brief Configure Digital audio module clock + * @param[in] src Digital audio module source + * @return None + */ +void HAL_CCM_DAUDIO_SetMClock(CCM_DAudioClkSrc src) +{ + HAL_MODIFY_REG(CCM->DAUDIO_MCLK_CTRL, CCM_DAUDIO_MCLK_SRC_MASK, src); +} + +/** + * @brief Enable Digital audio module clock + * @return None + */ +void HAL_CCM_DAUDIO_EnableMClock(void) +{ + HAL_SET_BIT(CCM->DAUDIO_MCLK_CTRL, CCM_PERIPH_CLK_EN_BIT); +} + +/** + * @brief Disable Digital audio module clock + * @return None + */ +void HAL_CCM_DAUDIO_DisableMClock(void) +{ + HAL_CLR_BIT(CCM->DAUDIO_MCLK_CTRL, CCM_PERIPH_CLK_EN_BIT); +} + +/** + * @brief Configure IR RX module clock + * @param[in] src IR RX module clock source + * @param[in] divN IR RX module clock divider N + * @param[in] divM IR RX module clock divider M + * @return None + */ +void HAL_CCM_IRRX_SetMClock(CCM_APBPeriphClkSrc src, CCM_PeriphClkDivN divN, CCM_PeriphClkDivM divM) +{ + HAL_MODIFY_REG(CCM->IRRX_MCLK_CTRL, CCM_PERIPH_CLK_PARAM_MASK, src | divN | divM); +} + +/** + * @brief Enable IR RX module clock + * @return None + */ +void HAL_CCM_IRRX_EnableMClock(void) +{ + HAL_SET_BIT(CCM->IRRX_MCLK_CTRL, CCM_PERIPH_CLK_EN_BIT); +} + +/** + * @brief Disable IR RX module clock + * @return None + */ +void HAL_CCM_IRRX_DisableMClock(void) +{ + HAL_CLR_BIT(CCM->IRRX_MCLK_CTRL, CCM_PERIPH_CLK_EN_BIT); +} + +/** + * @brief Configure IR TX module clock + * @param[in] src IR TX module clock source + * @param[in] divN IR TX module clock divider N + * @param[in] divM IR TX module clock divider M + * @return None + */ +void HAL_CCM_IRTX_SetMClock(CCM_APBPeriphClkSrc src, CCM_PeriphClkDivN divN, CCM_PeriphClkDivM divM) +{ + HAL_MODIFY_REG(CCM->IRTX_MCLK_CTRL, CCM_PERIPH_CLK_PARAM_MASK, src | divN | divM); +} + +/** + * @brief Enable IR TX module clock + * @return None + */ +void HAL_CCM_IRTX_EnableMClock(void) +{ + HAL_SET_BIT(CCM->IRTX_MCLK_CTRL, CCM_PERIPH_CLK_EN_BIT); +} + +/** + * @brief Disable IR TX module clock + * @return None + */ +void HAL_CCM_IRTX_DisableMClock(void) +{ + HAL_CLR_BIT(CCM->IRTX_MCLK_CTRL, CCM_PERIPH_CLK_EN_BIT); +} + +/** + * @brief Configure system tick reference clock + * @param[in] src system tick reference clock source + * @param[in] divN system tick reference clock divider N + * @param[in] divM system tick reference clock divider M + * @return None + */ +void HAL_CCM_SYSTICK_SetRefClock(CCM_APBPeriphClkSrc src, CCM_PeriphClkDivN divN, CCM_PeriphClkDivM divM) +{ + HAL_MODIFY_REG(CCM->SYSTICK_REFCLK_CTRL, CCM_PERIPH_CLK_PARAM_MASK, src | divN | divM); +} + +/** + * @brief Enable system tick reference clock + * @return None + */ +void HAL_CCM_SYSTICK_EnableRefClock(void) +{ + HAL_SET_BIT(CCM->SYSTICK_REFCLK_CTRL, CCM_PERIPH_CLK_EN_BIT); +} + +/** + * @brief Disable system tick reference clock + * @return None + */ +void HAL_CCM_SYSTICK_DisableRefClock(void) +{ + HAL_CLR_BIT(CCM->SYSTICK_REFCLK_CTRL, CCM_PERIPH_CLK_EN_BIT); +} + +/** + * @brief Enable system tick reference + * @return None + */ +void HAL_CCM_SYSTICK_EnableRef(void) +{ + HAL_CLR_BIT(CCM->SYSTICK_CALIB_CTRL, CCM_SYSTICK_NOREF_BIT); +} + +/** + * @brief Disable system tick reference + * @return None + */ +void HAL_CCM_SYSTICK_DisableRef(void) +{ + HAL_SET_BIT(CCM->SYSTICK_CALIB_CTRL, CCM_SYSTICK_NOREF_BIT); +} + +/** + * @brief Enable system tick skew, which means calibration value is not exactly 10 ms + * @return None + */ +void HAL_CCM_SYSTICK_EnableSkew(void) +{ + HAL_SET_BIT(CCM->SYSTICK_CALIB_CTRL, CCM_SYSTICK_SKEW_BIT); +} + +/** + * @brief Disable system tick skew, which means calibration value is accurate + * @return None + */ +void HAL_CCM_SYSTICK_DisableSkew(void) +{ + HAL_CLR_BIT(CCM->SYSTICK_CALIB_CTRL, CCM_SYSTICK_SKEW_BIT); +} + +/** + * @brief Set calibration value for 10 ms + * @return None + */ +void HAL_CCM_SYSTICK_SetTENMS(uint32_t cnt) +{ + HAL_MODIFY_REG(CCM->SYSTICK_CALIB_CTRL, + CCM_SYSTICK_TENMS_MASK, + (cnt << CCM_SYSTICK_TENMS_SHIFT) & CCM_SYSTICK_TENMS_MASK); +} + +/** + * @brief Enable DMIC module clock + * @return None + */ +void HAL_CCM_DMIC_EnableMClock(void) +{ + HAL_SET_BIT(CCM->DMIC_MCLK_CTRL, CCM_PERIPH_CLK_EN_BIT); +} + +/** + * @brief Disable DMIC module clock + * @return None + */ +void HAL_CCM_DMIC_DisableMClock(void) +{ + HAL_CLR_BIT(CCM->DMIC_MCLK_CTRL, CCM_PERIPH_CLK_EN_BIT); +} + +/** + * @brief Configure GPADC module clock + * @param[in] src GPADC module clock source + * @param[in] divN GPADC module clock divider N + * @param[in] divM GPADC module clock divider M + * @return None + */ +void HAL_CCM_GPADC_SetMClock(CCM_APBPeriphClkSrc src, CCM_PeriphClkDivN divN, CCM_PeriphClkDivM divM) +{ + HAL_MODIFY_REG(CCM->GPADC_MCLK_CTRL, CCM_PERIPH_CLK_PARAM_MASK, src | divN | divM); +} + +/** + * @brief Enable GPADC module clock + * @return None + */ +void HAL_CCM_GPADC_EnableMClock(void) +{ + HAL_SET_BIT(CCM->GPADC_MCLK_CTRL, CCM_PERIPH_CLK_EN_BIT); +} + +/** + * @brief Disable GPADC module clock + * @return None + */ +void HAL_CCM_GPADC_DisableMClock(void) +{ + HAL_CLR_BIT(CCM->GPADC_MCLK_CTRL, CCM_PERIPH_CLK_EN_BIT); +} + +/** + * @brief Configure CSI module clock + * @param[in] src CSI module clock source + * @param[in] divN CSI module clock divider N + * @param[in] divM CSI module clock divider M + * @return None + */ +void HAL_CCM_CSI_SetMClock(CCM_AHBPeriphClkSrc src, CCM_PeriphClkDivN divN, CCM_PeriphClkDivM divM) +{ + HAL_MODIFY_REG(CCM->CSI_MCLK_CTRL, CCM_PERIPH_CLK_PARAM_MASK, src | divN | divM); +} + +/** + * @brief Enable CSI module clock + * @return None + */ +void HAL_CCM_CSI_EnableMClock(void) +{ + HAL_SET_BIT(CCM->CSI_MCLK_CTRL, CCM_PERIPH_CLK_EN_BIT); +} + +/** + * @brief Disable CSI module clock + * @return None + */ +void HAL_CCM_CSI_DisableMClock(void) +{ + HAL_CLR_BIT(CCM->CSI_MCLK_CTRL, CCM_PERIPH_CLK_EN_BIT); +} + +/** + * @brief Configure Flash controller module clock + * @param[in] src Flash controller module clock source + * @param[in] divN Flash controller module clock divider N + * @param[in] divM Flash controller module clock divider M + * @return None + */ +void HAL_CCM_FLASHC_SetMClock(CCM_AHBPeriphClkSrc src, CCM_PeriphClkDivN divN, CCM_PeriphClkDivM divM) +{ + HAL_MODIFY_REG(CCM->FLASHC_MCLK_CTRL, CCM_PERIPH_CLK_PARAM_MASK, src | divN | divM); +} + +/** + * @brief Enable Flash controller module clock + * @return None + */ +void HAL_CCM_FLASHC_EnableMClock(void) +{ + HAL_SET_BIT(CCM->FLASHC_MCLK_CTRL, CCM_PERIPH_CLK_EN_BIT); +} + +/** + * @brief Disable Flash controller module clock + * @return None + */ +void HAL_CCM_FLASHC_DisableMClock(void) +{ + HAL_CLR_BIT(CCM->FLASHC_MCLK_CTRL, CCM_PERIPH_CLK_EN_BIT); +} + +#ifdef CONFIG_PM +struct ccm_regs { + uint32_t cpu_bus_clkcfg; /* cpu bus clock configure register */ + uint32_t bus_clk_gating; /* bus clock gating control Register */ + uint32_t bus_reset_ctrl; /* bus device reset control register */ + uint32_t systick_refclk_ctrl; /* system tick reference clock register */ + uint32_t systick_calib_ctrl; /* system tick clock calibration register */ +}; +static struct ccm_regs ccm_reg_store; + +static int ccm_suspend(struct soc_device *dev, enum suspend_state_t state) +{ + switch (state) { + case PM_MODE_SLEEP: + break; + case PM_MODE_STANDBY: + case PM_MODE_HIBERNATION: + ccm_reg_store.bus_clk_gating = CCM->BUS_PERIPH_CLK_CTRL; /* it is better to do in every devices driver */ + ccm_reg_store.bus_reset_ctrl = CCM->BUS_PERIPH_RST_CTRL; + + ccm_reg_store.cpu_bus_clkcfg = CCM->CPU_BUS_CLKCFG; + ccm_reg_store.systick_refclk_ctrl = CCM->SYSTICK_REFCLK_CTRL; + ccm_reg_store.systick_calib_ctrl = CCM->SYSTICK_CALIB_CTRL; + HAL_DBG("%s ok\n", __func__); + break; + default: + break; + } + + return 0; +} + +static int ccm_resume(struct soc_device *dev, enum suspend_state_t state) +{ + switch (state) { + case PM_MODE_SLEEP: + break; + case PM_MODE_STANDBY: + case PM_MODE_HIBERNATION: + CCM->CPU_BUS_CLKCFG = ccm_reg_store.cpu_bus_clkcfg; /* write directly is ok for cpu clk has been restored after resume */ + CCM->SYSTICK_REFCLK_CTRL = ccm_reg_store.systick_refclk_ctrl; + CCM->SYSTICK_CALIB_CTRL = ccm_reg_store.systick_calib_ctrl; + + CCM->BUS_PERIPH_RST_CTRL = ccm_reg_store.bus_reset_ctrl; + CCM->BUS_PERIPH_CLK_CTRL = ccm_reg_store.bus_clk_gating; + __DSB(); + __ISB(); + HAL_DBG("%s ok\n", __func__); + break; + default: + break; + } + + return 0; +} + +void ccm_print_regs(void) +{ + hex_dump_bytes(&ccm_reg_store, sizeof(ccm_reg_store)); +} + +static struct soc_device_driver ccm_drv = { + .name = "ccm", + .suspend_noirq = ccm_suspend, + .resume_noirq = ccm_resume, +}; + +static struct soc_device ccm_dev = { + .name = "ccm", + .driver = &ccm_drv, +}; + +#define CCM_DEV (&ccm_dev) +#endif /* CONFIG_PM */ + +/** + * @brief Initializes CCM module + * @return None + */ +void HAL_CCM_Init(void) +{ +#ifdef CONFIG_PM + pm_register_ops(CCM_DEV); +#endif +} diff --git a/platform/mcu/xr871/src/driver/chip/hal_crypto.c b/platform/mcu/xr871/src/driver/chip/hal_crypto.c new file mode 100644 index 0000000000..1398af149e --- /dev/null +++ b/platform/mcu/xr871/src/driver/chip/hal_crypto.c @@ -0,0 +1,1932 @@ +/** + * @file hal_crypto.c + * @author XRADIO IOT WLAN Team + */ + +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include "driver/chip/hal_crypto.h" +#include "hal_base.h" +#include "sys/endian.h" +#include "pm/pm.h" + + +/*************************************** Debug *****************************************/ +#define CE_MODULE (DBG_OFF | XR_LEVEL_DEBUG) +#define CE_REG_DEBUG + +#define CE_ASSERT(condition) XR_ASSERT(condition, CE_MODULE, #condition " failed\n") + +#define CE_DEBUG(msg, arg...) XR_DEBUG(CE_MODULE, NOEXPAND, "[CE Debug] " msg, ##arg) + +#define CE_ALERT(msg, arg...) XR_ALERT(CE_MODULE, NOEXPAND, "[CE Alert] " msg, ##arg) + +#define CE_ENTRY() XR_ENTRY(CE_MODULE, "[CE Entry]") + +#define CE_EXIT(val) XR_RET(CE_MODULE, "[CE Exit]", val) + +extern bool reg_show; + +#ifdef CE_REG_DEBUG +#define CE_REG(reg) { \ + if (reg_show) \ + CE_DEBUG("register " #reg ": 0x%x.\n", reg); \ + } + +#define CE_REG_ALL(ce) { \ + CE_REG(ce->CTL); \ + CE_REG(ce->KEY[0]); \ + CE_REG(ce->KEY[1]); \ + CE_REG(ce->KEY[2]); \ + CE_REG(ce->KEY[3]); \ + CE_REG(ce->KEY[4]); \ + CE_REG(ce->KEY[5]); \ + CE_REG(ce->KEY[6]); \ + CE_REG(ce->KEY[7]); \ + CE_REG(ce->IV[0]); \ + CE_REG(ce->IV[1]); \ + CE_REG(ce->IV[2]); \ + CE_REG(ce->IV[3]); \ + CE_REG(ce->CNT[0]); \ + CE_REG(ce->CNT[1]); \ + CE_REG(ce->CNT[2]); \ + CE_REG(ce->CNT[3]); \ + CE_REG(ce->FCSR); \ + CE_REG(ce->ICSR); \ + CE_REG(ce->MD0); \ + CE_REG(ce->MD1); \ + CE_REG(ce->MD2); \ + CE_REG(ce->MD3); \ + CE_REG(ce->MD4); \ + CE_REG(ce->CTS_LEN); \ + CE_REG(ce->CRC_POLY); \ + CE_REG(ce->CRC_RESULT); \ + CE_REG(ce->MD5); \ + CE_REG(ce->MD6); \ + CE_REG(ce->MD7); \ + } + +#else +#define CE_REG_ALL(ce) + +#define CE_REG(reg) + +#endif + + +#define __CE_STATIC_INLINE__ static inline + +bool reg_show; + +/* + * @brief CE + */ +__CE_STATIC_INLINE__ +void CE_Enable(CE_T *ce) +{ + HAL_SET_BIT(ce->CTL, CE_CTL_ENABLE_MASK); +} + +__CE_STATIC_INLINE__ +void CE_Disable(CE_T *ce) +{ + HAL_CLR_BIT(ce->CTL, CE_CTL_ENABLE_MASK); +} + +__CE_STATIC_INLINE__ +uint8_t CE_GetInputAvailCnt(CE_T *ce) +{ + return HAL_GET_BIT_VAL(ce->FCSR, CE_FCSR_RXFIFO_EMP_CNT_SHIFT, CE_FCSR_RXFIFO_EMP_CNT_VMASK); +} + +__CE_STATIC_INLINE__ +uint8_t CE_GetOutputAvailCnt(CE_T *ce) +{ + return HAL_GET_BIT_VAL(ce->FCSR, CE_FCSR_TXFIFO_AVA_CNT_SHIFT, CE_FCSR_TXFIFO_AVA_CNT_VMASK); +} + +__CE_STATIC_INLINE__ +void CE_SetInputThreshold(CE_T *ce, uint8_t threshold) +{ + CE_ASSERT((threshold & ~CE_FCSR_TXFIFO_INT_TRIG_LEVEL_MASK) == 0); //rx vmask = tx vmask = tx mask + HAL_MODIFY_REG(ce->FCSR, CE_FCSR_RXFIFO_INT_TRIG_LEVEL_MASK, threshold << CE_FCSR_RXFIFO_INT_TRIG_LEVEL_SHIFT); +} + +__CE_STATIC_INLINE__ +void CE_SetOutputThreshold(CE_T *ce, uint8_t threshold) +{ + CE_ASSERT((threshold & ~CE_FCSR_TXFIFO_INT_TRIG_LEVEL_MASK) == 0); + HAL_MODIFY_REG(ce->FCSR, CE_FCSR_TXFIFO_INT_TRIG_LEVEL_MASK, threshold << CE_FCSR_TXFIFO_INT_TRIG_LEVEL_SHIFT); +} + +typedef enum { + CE_INT_TPYE_TXFIFO_AVA = CE_ICSR_TXFIFO_AVA_INT_EN_SHIFT, + CE_INT_TPYE_RXFIFO_EMP = CE_ICSR_RXFIFO_EMP_INT_EN_SHIFT, + CE_INT_TPYE_HASH_CRC_END = CE_ICSR_HASH_CRC_END_INT_EN_SHIFT +} CE_Int_Type; + +__CE_STATIC_INLINE__ +void CE_EnableInt(CE_T *ce, CE_Int_Type type) +{ + HAL_SET_BIT(ce->ICSR, 1 << type); +} + +__CE_STATIC_INLINE__ +void CE_DisableInt(CE_T *ce, CE_Int_Type type) +{ + HAL_CLR_BIT(ce->ICSR, 1 << type); +} + +__CE_STATIC_INLINE__ +bool CE_Status(CE_T *ce, CE_Int_Type type) +{ + return (bool)HAL_GET_BIT_VAL(ce->ICSR, CE_ICSR_TXFIFO_AVA_SHIFT + type, 1); +} + +__CE_STATIC_INLINE__ +void CE_EnableDMA(CE_T *ce) +{ + HAL_SET_BIT(ce->ICSR, CE_ICSR_DRQ_ENABLE_MASK); +} + +__CE_STATIC_INLINE__ +void CE_DisableDMA(CE_T *ce) +{ + HAL_CLR_BIT(ce->ICSR, CE_ICSR_DRQ_ENABLE_MASK); +} + +__CE_STATIC_INLINE__ +void CE_SetLength(CE_T *ce, uint32_t len) +{ + ce->CTS_LEN = len; +} + +/* + * @brief CRC + */ +__CE_STATIC_INLINE__ +HAL_Status CE_CRC_Init(CE_T *ce, + bool init_val, + bool xor_out, + bool ref_in, + bool ref_out, + CE_CTL_CRC_Width width, + uint32_t poly) +{ + bool last_pckt = 0; + + HAL_CLR_BIT(ce->CTL, CE_CTL_END_BIT_MASK); + HAL_MODIFY_REG(ce->CTL, + CE_CTL_CRC_INIT_MASK + | CE_CTL_CRC_REF_IN_MASK + | CE_CTL_CRC_REF_OUT_MASK + | CE_CTL_CRC_XOR_OUT_MASK + | CE_CTL_CRC_WIDTH_MASK + | CE_CTL_CRC_CONT_MASK + | CE_CTL_OP_DIR_MASK + | CE_CTL_METHOD_MASK, + (init_val << CE_CTL_CRC_INIT_SHIFT) + | (ref_in << CE_CTL_CRC_REF_IN_SHIFT) + | (ref_out << CE_CTL_CRC_REF_OUT_SHIFT) + | (xor_out << CE_CTL_CRC_XOR_OUT_SHIFT) + | (width) + | (last_pckt << CE_CTL_CRC_CONT_SHIFT) /* not the last packet */ + | CE_CRYPT_OP_ENCRYPTION + | CE_CTL_METHOD_CRC); + ce->CRC_POLY = poly; + return HAL_OK; +} + +__CE_STATIC_INLINE__ +void CE_CRC_Fill(CE_T *ce, uint32_t data) +{ + while (CE_GetInputAvailCnt(ce) == 0); + ce->RXFIFO = data; +} + +__CE_STATIC_INLINE__ +void CE_CRC_Finish(CE_T *ce) +{ + HAL_SET_BIT(ce->CTL, CE_CTL_END_BIT_MASK); +} + +__CE_STATIC_INLINE__ +void CE_CRC_Calc(CE_T *ce, uint32_t *crc) //_____________16bit mode is it generate a 16bits crc? +{ + *crc = ce->CRC_RESULT; +} + +__CE_STATIC_INLINE__ +void CE_CRC_Deinit(CE_T *ce) +{ + /*CE_Disable(ce);*/ +} + +__CE_STATIC_INLINE__ +void CE_CRC_SetResult(CE_T *ce, uint32_t crc) +{ + ce->CRC_RESULT = crc; +} + +/* +void CE_CRC_SetLength(CE_T *ce, uint32_t len) +{ + CE_SetLength(ce, len); +} +*/ + + +/* + * @brief AES/DES/3DES + */ +__CE_STATIC_INLINE__ +void CE_Crypto_Init(CE_T *ce, CE_CTL_Method algo, CE_CTL_Crypto_Mode mode, CE_Crypto_Op op) +{ + CE_ASSERT(algo > CE_CTL_METHOD_3DES); + + HAL_MODIFY_REG(ce->CTL, + CE_CTL_METHOD_MASK + | CE_CTL_OP_MODE_MASK + | CE_CTL_OP_DIR_MASK, + algo + | mode + | op); /* ce enable */ +} + +__CE_STATIC_INLINE__ +void CE_AES_SetKey(CE_T *ce, CE_CTL_KeySource src, CE_CTL_AES_KeySize size, uint32_t *key) +{ + uint8_t key_cnt; + uint8_t i = 0; + HAL_MODIFY_REG(ce->CTL, CE_CTL_KEY_SEL_MASK | CE_CTL_AES_KEY_SIZE_MASK, src | size); + if (size == CE_CTL_AES_KEYSIZE_128BITS) + key_cnt = 4; + else if (size == CE_CTL_AES_KEYSIZE_192BITS) + key_cnt = 6; + else if (size == CE_CTL_AES_KEYSIZE_256BITS) + key_cnt = 8; + else { + CE_ALERT("param is out of range\n"); + return; + } + + while (i < key_cnt) { + ce->KEY[i] = *(key++); + i++; + } +} + +__CE_STATIC_INLINE__ +void CE_DES_SetKey(CE_T *ce, CE_CTL_KeySource src, uint32_t key[2]) //56bit key? 64bit key? = 2 * 32bit reg? +{ + HAL_MODIFY_REG(ce->CTL, CE_CTL_KEY_SEL_MASK, src); + ce->KEY[0] = key[0]; + ce->KEY[1] = key[1]; +} + +__CE_STATIC_INLINE__ +void CE_3DES_SetKey(CE_T *ce, CE_CTL_KeySource src, uint32_t key[6]) //3 * 64bit key? = 6 * 32bit reg? +{ + uint8_t i = 0; + HAL_MODIFY_REG(ce->CTL, CE_CTL_KEY_SEL_MASK, src); + + while (i < CE_3DES_KEY_COUNT) { + ce->KEY[i] = *(key++); + i++; + } +} + +#ifdef CE_CTR_MODE +__CE_STATIC_INLINE__ +HAL_Status CE_Crypto_SetCounter(CE_T *ce, uint32_t *cnt, CE_CTL_CtrWidth size) +{ + HAL_MODIFY_REG(ce->CTL, CE_CTL_CTR_WIDTH_MASK, size); + switch (size) { + case CE_CTL_CTRWITDH_128BITS: + ce->CNT[3] = *(cnt + 3); + ce->CNT[2] = *(cnt + 2); + case CE_CTL_CTRWITDH_64BITS: + ce->CNT[1] = *(cnt + 1); + case CE_CTL_CTRWITDH_32BITS: + ce->CNT[0] = *(cnt + 0); + break; + case CE_CTL_CTRWITDH_16BITS: + ce->CNT[0] = *(cnt + 0) & 0xFFFF; + break; + default: + return HAL_INVALID; + } + return HAL_OK; +} +#endif + +__CE_STATIC_INLINE__ +void CE_Crypto_SetLength(CE_T *ce, uint32_t len) +{ + CE_SetLength(ce, len); +} + +__CE_STATIC_INLINE__ +void CE_Crypto_SetIV(CE_T *ce, uint32_t iv[4], uint8_t size) +{ + uint8_t i = size / 4; + while (i--) + ce->IV[i] = iv[i]; +} + +__CE_STATIC_INLINE__ +void CE_Crypto_Input(CE_T *ce, uint32_t data) +{ + while (CE_GetInputAvailCnt(ce) == 0); + ce->RXFIFO = data; +} + +__CE_STATIC_INLINE__ +void CE_Crypto_Output(CE_T *ce, uint32_t *data) +{ + while (CE_GetOutputAvailCnt(ce) == 0); + *data = ce->TXFIFO; +} + +__CE_STATIC_INLINE__ +void CE_Crypto_Deinit(CE_T *ce) +{ + CE_Disable(ce); +} + +__CE_STATIC_INLINE__ +volatile uint32_t *CE_Crypto_GetInputAddr(CE_T *ce) +{ + return &ce->RXFIFO; +} + +__CE_STATIC_INLINE__ +volatile const uint32_t *CE_Crypto_GetOutputAddr(CE_T *ce) +{ + return &ce->TXFIFO; +} + +/*HAL_Status CE_Crypto_GetOutput(CE_T *ce, uint32_t *data) +{} + +HAL_Status CE_Crypto_GetInput(CE_T *ce, uint32_t *data) +{}*/ + +__CE_STATIC_INLINE__ +void CE_Crypto_GetIV(CE_T *ce, uint32_t iv[4], uint8_t size) +{ + uint8_t i = size / 4; + while (i--) + iv[i] = ce->IV[i]; +} + +#ifdef CE_CTR_MODE +__CE_STATIC_INLINE__ +HAL_Status CE_Crypto_GetCounter(CE_T *ce, uint32_t cnt[4], CE_CTL_CtrWidth size) +{ + switch (size) { + case CE_CTL_CTRWITDH_128BITS: + cnt[2] = ce->CNT[2]; + cnt[3] = ce->CNT[3]; + case CE_CTL_CTRWITDH_64BITS: + cnt[1] = ce->CNT[1]; + case CE_CTL_CTRWITDH_32BITS: + case CE_CTL_CTRWITDH_16BITS: + cnt[0] = ce->CNT[0]; + break; + default: + return HAL_INVALID; + } + return HAL_OK; +} +#endif + +/* + * @brief MD5/sha1/sha256 + */ +void CE_Hash_Init(CE_T *ce, CE_CTL_Method algo) +{ + CE_ASSERT((algo != CE_CTL_METHOD_SHA1) && (algo != CE_CTL_METHOD_MD5) && (algo != CE_CTL_METHOD_SHA256)); + + HAL_CLR_BIT(ce->CTL, CE_CTL_END_BIT_MASK); + HAL_MODIFY_REG(ce->CTL, + CE_CTL_METHOD_MASK | CE_CTL_ENABLE_MASK | CE_CTL_OP_DIR_MASK, + algo | (1 << CE_CTL_ENABLE_SHIFT) | CE_CRYPT_OP_ENCRYPTION); +} + +void CE_Hash_SetIV(CE_T *ce, CE_CTL_IvMode_SHA_MD5 iv_src, uint32_t *iv, uint32_t iv_size) +{ + HAL_MODIFY_REG(ce->CTL, CE_CTL_IV_MODE_MASK, iv_src); + + if (iv_src == CE_CTL_IVMODE_SHA_MD5_FIPS180) { //necessary? + ce->IV[0] = 0; + ce->IV[1] = 0; + ce->IV[2] = 0; + ce->IV[3] = 0; + return; + } + + ce->IV[0] = iv[0]; + ce->IV[1] = iv[1]; + ce->IV[2] = iv[2]; + ce->IV[3] = iv[3]; + if (iv_size == 5) + ce->CNT[0] = iv[4]; + else if (iv_size == 8) { + ce->CNT[0] = iv[4]; + ce->CNT[1] = iv[5]; + ce->CNT[2] = iv[6]; + ce->CNT[3] = iv[7]; + } + +} + +__CE_STATIC_INLINE__ +void CE_Hash_Finish(CE_T *ce) +{ + HAL_SET_BIT(ce->CTL, CE_CTL_END_BIT_MASK); +} + +__CE_STATIC_INLINE__ +void CE_Hash_Fill(CE_T *ce, uint32_t data) +{ + while (CE_GetInputAvailCnt(ce) == 0); + ce->RXFIFO = data; +} + +void CE_Hash_Calc(CE_T *ce, CE_CTL_Method algo, uint32_t *digest) // md5[5] sha1[5] sha256[8] +{ + if (algo == CE_CTL_METHOD_SHA256) { + *(digest + 7) = ce->MD7; + *(digest + 6) = ce->MD6; + *(digest + 5) = ce->MD5; + *(digest + 4) = ce->MD4; + } else if (algo == CE_CTL_METHOD_SHA1) + *(digest + 4) = ce->MD4; + *(digest + 3) = ce->MD3; + *(digest + 2) = ce->MD2; + *(digest + 1) = ce->MD1; + *(digest + 0) = ce->MD0; +} + +__CE_STATIC_INLINE__ +void CE_Hash_Deinit(CE_T *ce) +{ + CE_Disable(ce); +} + + +/* + * @brief PRNG + */ +__CE_STATIC_INLINE__ +void CE_PRNG_Init(CE_T *ce, bool continue_mode) +{ + HAL_MODIFY_REG(ce->CTL, + CE_CTL_PRNG_MODE_MASK | CE_CTL_METHOD_MASK | CE_CTL_ENABLE_MASK, + (continue_mode << CE_CTL_PRNG_MODE_SHIFT) | CE_CTL_METHOD_PRNG | (1 << CE_CTL_ENABLE_SHIFT)); + +} + +__CE_STATIC_INLINE__ +void CE_PRNG_Seed(CE_T *ce, uint32_t seed[6]) +{ + ce->KEY[0] = seed[0]; + ce->KEY[1] = seed[1]; + ce->KEY[2] = seed[2]; + ce->KEY[3] = seed[3]; + ce->KEY[4] = seed[4]; + ce->KEY[5] = seed[5]; +} + +__CE_STATIC_INLINE__ +void CE_PRNG_Generate(CE_T *ce, uint32_t random[5]) +{ + HAL_SET_BIT(ce->CTL, CE_CTL_PRNG_START_MASK); + + while (HAL_GET_BIT(CE->CTL, CE_CTL_PRNG_START_MASK) != 0); + + random[0] = ce->MD0; + random[1] = ce->MD1; + random[2] = ce->MD2; + random[3] = ce->MD3; + random[4] = ce->MD4; +} + +__CE_STATIC_INLINE__ +void CE_PRNG_Deinit(CE_T *ce) +{ + CE_Disable(ce); +} + + +/************************ AES DES 3DES private **************************************/ +static HAL_Mutex ce_lock; +static HAL_Semaphore ce_block; + +__CE_STATIC_INLINE__ +void HAL_CE_EnableCCMU() +{ + HAL_CCM_BusEnablePeriphClock(CCM_BUS_PERIPH_BIT_CE); + HAL_CCM_CE_EnableMClock(); +} + +__CE_STATIC_INLINE__ +void HAL_CE_DisableCCMU() +{ + HAL_CCM_CE_DisableMClock(); + HAL_CCM_BusDisablePeriphClock(CCM_BUS_PERIPH_BIT_CE); +} + +/*static void HAL_CE_InputCmpl(void *arg) +{ + CE_DEBUG("input by dma had been finished.\n"); +} + +static void HAL_CE_OutputCmpl(void *arg) +{ + HAL_SemaphoreRelease(&ce_block); + CE_DEBUG("output by dma had been finished.\n"); +}*/ + +static void HAL_CE_DMACmpl(void *arg) +{ + if (arg != NULL) + HAL_SemaphoreRelease(&ce_block); + CE_DEBUG("Transfer by dma had been finished.\n"); +} + +static HAL_Status HAL_Crypto_InitDMA(DMA_Channel *input, DMA_Channel *output) +{ + HAL_Status ret = HAL_OK; + DMA_ChannelInitParam Output_param; + DMA_ChannelInitParam Input_param; + memset(&Output_param, 0, sizeof(Output_param)); + memset(&Input_param, 0, sizeof(Input_param)); + CE_ENTRY(); + + if ((*output = HAL_DMA_Request()) == DMA_CHANNEL_INVALID) { + CE_ALERT("DMA request failed \n"); + ret = HAL_INVALID; + goto out; + } + if ((*input = HAL_DMA_Request()) == DMA_CHANNEL_INVALID) { + CE_ALERT("DMA request failed \n"); + HAL_DMA_Release(*output); + ret = HAL_INVALID; + goto out; + } + + Output_param.cfg = HAL_DMA_MakeChannelInitCfg(DMA_WORK_MODE_SINGLE, + DMA_WAIT_CYCLE_16, + DMA_BYTE_CNT_MODE_REMAIN, + DMA_DATA_WIDTH_8BIT, + DMA_BURST_LEN_1, + DMA_ADDR_MODE_INC, + DMA_PERIPH_SRAM, + DMA_DATA_WIDTH_32BIT, + DMA_BURST_LEN_1, + DMA_ADDR_MODE_FIXED, + DMA_PERIPH_CE); + Output_param.irqType = DMA_IRQ_TYPE_END; + Output_param.endCallback = HAL_CE_DMACmpl; + Output_param.endArg = &ce_block; + + Input_param.cfg = HAL_DMA_MakeChannelInitCfg(DMA_WORK_MODE_SINGLE, + DMA_WAIT_CYCLE_16, + DMA_BYTE_CNT_MODE_REMAIN, + DMA_DATA_WIDTH_32BIT, + DMA_BURST_LEN_1, + DMA_ADDR_MODE_FIXED, + DMA_PERIPH_CE, + DMA_DATA_WIDTH_8BIT, + DMA_BURST_LEN_1, + DMA_ADDR_MODE_INC, + DMA_PERIPH_SRAM); + Input_param.irqType = DMA_IRQ_TYPE_END; + Input_param.endCallback = HAL_CE_DMACmpl; + Input_param.endArg = NULL; + + HAL_DMA_Init(*input, &Input_param); + HAL_DMA_Init(*output, &Output_param); + +out: + CE_EXIT(ret); + return ret; +} + +static inline void HAL_Crypto_DenitDMA(DMA_Channel input, DMA_Channel output) +{ + HAL_DMA_DeInit(input); + HAL_DMA_DeInit(output); + HAL_DMA_Release(input); + HAL_DMA_Release(output); +} + +static HAL_Status HAL_Crypto_Convey(uint8_t *input, uint8_t *output, uint32_t size, uint8_t block_size) +{ + HAL_Status ret = HAL_OK; + uint32_t align_len = size & (~(block_size - 1)); + uint32_t remain_len = size & (block_size - 1); + uint8_t padding[16] = {0}; + CE_ENTRY(); +// CE_DEBUG("align_len = %d, remain_len = %d, len = %d\n", align_len, remain_len, remain_len ? (align_len + block_size) : align_len); + + DMA_Channel Output_channel = DMA_CHANNEL_INVALID; + DMA_Channel Input_channel = DMA_CHANNEL_INVALID; + + /* data preprocess */ + memcpy(padding, &input[align_len], remain_len); + CE_Crypto_SetLength(CE, remain_len ? (align_len + block_size) : align_len); + CE_REG_ALL(CE); + + /* data transfer by DMA */ + if ((ret = HAL_Crypto_InitDMA(&Input_channel, &Output_channel)) != HAL_OK){ + CE_ALERT("DMA Request failed\n"); + goto out; + } + + CE_SetInputThreshold(CE, 0); + CE_SetOutputThreshold(CE, 0); + CE_EnableDMA(CE); + CE_Enable(CE); + + CE_REG_ALL(CE); + + if (align_len != 0) { + HAL_DMA_Start(Input_channel, (uint32_t)input, (uint32_t)CE_Crypto_GetInputAddr(CE), align_len); + HAL_DMA_Start(Output_channel, (uint32_t)CE_Crypto_GetOutputAddr(CE), (uint32_t)output, align_len); + if ((ret = HAL_SemaphoreWait(&ce_block, CE_WAIT_TIME)) != HAL_OK) { + CE_ALERT("DMA 1st transfer failed\n"); + HAL_DMA_Stop(Input_channel); + HAL_DMA_Stop(Output_channel); + goto failed; + } + HAL_DMA_Stop(Input_channel); + HAL_DMA_Stop(Output_channel); + } + + if (remain_len != 0) { + HAL_DMA_Start(Input_channel, (uint32_t)padding, (uint32_t)CE_Crypto_GetInputAddr(CE), block_size); + CE_REG_ALL(CE); + + HAL_DMA_Start(Output_channel, (uint32_t)CE_Crypto_GetOutputAddr(CE), (uint32_t)&output[align_len], block_size); + if ((ret = HAL_SemaphoreWait(&ce_block, CE_WAIT_TIME)) != HAL_OK) { + CE_ALERT("DMA 2nd transfer failed\n"); + CE_REG_ALL(CE); + HAL_DMA_Stop(Input_channel); + HAL_DMA_Stop(Output_channel); + goto failed; + } + CE_REG_ALL(CE); + HAL_DMA_Stop(Input_channel); + HAL_DMA_Stop(Output_channel); + } + +failed: + CE_Disable(CE); + CE_DisableDMA(CE); + HAL_Crypto_DenitDMA(Input_channel, Output_channel); + +out: + CE_EXIT(ret); + return ret; +} + +int ce_running = 0; +#ifdef CONFIG_PM +static int hal_ce_suspending = 0; + +static int ce_suspend(struct soc_device *dev, enum suspend_state_t state) +{ + if (ce_running) + return -1; + hal_ce_suspending = 1; + + return 0; +} + +static int ce_resume(struct soc_device *dev, enum suspend_state_t state) +{ + switch (state) { + case PM_MODE_SLEEP: + break; + case PM_MODE_STANDBY: + case PM_MODE_HIBERNATION: + HAL_CE_Init(); + break; + default: + break; + } + + hal_ce_suspending = 0; + + return 0; +} + +static struct soc_device_driver ce_drv = { + .name = "ce", + .suspend_noirq = ce_suspend, + .resume_noirq = ce_resume, +}; + +static struct soc_device ce_dev = { + .name = "ce", + .driver = &ce_drv, +}; + +#define CE_DEV (&ce_dev) +#else +#define CE_DEV NULL +#endif + + +/************************ public **************************************/ + +/** + * @brief Initialize the Crypto Engine Module. + * @param None + * @retval HAL_Status: The result status of initializtion + */ +HAL_Status HAL_CE_Init() +{ + HAL_Status ret = HAL_OK; + uint32_t clk; + uint32_t div; + CE_ENTRY(); + + HAL_CE_DisableCCMU(); + HAL_CCM_BusForcePeriphReset(CCM_BUS_PERIPH_BIT_CE); + HAL_CCM_BusReleasePeriphReset(CCM_BUS_PERIPH_BIT_CE); + clk = HAL_GetDevClock(); + div = (clk - 1) / (80 * 1000 * 1000) + 1; + CE_DEBUG("CCMU src clk is %d, div is %d\n", clk, div); + + if (div > (8 * 16)) + return HAL_ERROR; + else if (div > (4 * 16)) + HAL_CCM_CE_SetMClock(CCM_AHB_PERIPH_CLK_SRC_DEVCLK, CCM_PERIPH_CLK_DIV_N_8, (CCM_PeriphClkDivM)((div >> 3) - 1)); + else if (div > (2 * 16)) + HAL_CCM_CE_SetMClock(CCM_AHB_PERIPH_CLK_SRC_DEVCLK, CCM_PERIPH_CLK_DIV_N_4, (CCM_PeriphClkDivM)((div >> 2) - 1)); + else if (div > (1 & 16)) + HAL_CCM_CE_SetMClock(CCM_AHB_PERIPH_CLK_SRC_DEVCLK, CCM_PERIPH_CLK_DIV_N_2, (CCM_PeriphClkDivM)((div >> 1) - 1)); + else + HAL_CCM_CE_SetMClock(CCM_AHB_PERIPH_CLK_SRC_DEVCLK, CCM_PERIPH_CLK_DIV_N_1, (CCM_PeriphClkDivM)((div >> 0) - 1)); + +#ifdef CONFIG_PM + if (!hal_ce_suspending) { +#endif + + if ((ret = HAL_MutexInit(&ce_lock)) != HAL_OK) + goto out; + if ((ret = HAL_SemaphoreInit(&ce_block, 0, 1)) != HAL_OK) { + HAL_MutexDeinit(&ce_lock); + goto out; + } + + ce_running = 0; + +#ifdef CONFIG_PM + pm_register_ops(CE_DEV); + } +#endif + +out: + CE_EXIT(ret); + return ret; +} + +/** + * @brief Deinitializes the Crypto Engine Module. + * @param None + * @retval HAL_Status: The status of driver + */ +HAL_Status HAL_CE_Deinit() +{ + HAL_Status ret = HAL_OK; + CE_ENTRY(); + +#ifdef CONFIG_PM + if (!hal_ce_suspending) + pm_unregister_ops(CE_DEV); +#endif + + if ((ret = HAL_MutexDeinit(&ce_lock)) != HAL_OK) + goto out; + if ((ret = HAL_SemaphoreDeinit(&ce_block)) != HAL_OK) + goto out; +out: + CE_EXIT(ret); + return ret; +} + + +/** + * @brief Encrypt data by AES. + * @note AES128 & AES192 & AES256 limit the key size, oversize of key will be cut + * off. AES block size is 16 bytes. + * @param aes: + * @arg aes->mode: CE_CRYPT_MODE_ECB or CE_CRYPT_MODE_CBC + * @arg aes->iv[16]: initialization value, is same size as block size. + * CE_CRYPT_MODE_CBC mode need this param. + * @arg aes->src: CE_CTL_KEYSOURCE_INPUT: key is set from aes->key. + * CE_CTL_KEYSOURCE_SID: is not support for now. + * CE_CTL_KEYSOURCE_INTERNAL0~7: is not support for now. + * @arg aes->key[32]: store the key. please notice the size should same as + * the algorithm. example: AES128 should only use + * key[0]~key[15]. + * @arg aes->keysize: CE_CTL_AES_KEYSIZE_128BITS: algorithm of AES128. + * CE_CTL_AES_KEYSIZE_192BITS: algorithm of AES192. + * CE_CTL_AES_KEYSIZE_256BITS: algorithm of AES256. + * @param plain: plain data which is need to be encrypt. + * @param cipher: cipher data which is store the encrpyted data. the cipher data + * size is multiple of 16 bytes and no less than plain data size. + * @param size: size of plain data. if encrypt a segment of data, size should + * be multiple of 16 bytes. + * @retval HAL_Status: The status of driver + */ +HAL_Status HAL_AES_Encrypt(CE_AES_Config *aes, uint8_t *plain, uint8_t *cipher, uint32_t size) +{ + HAL_Status ret = HAL_OK; + CE_ENTRY(); + + if (size == 0) { + ret = HAL_INVALID; + goto out; + } + + ce_running = 1; + if ((ret = HAL_MutexLock(&ce_lock, CE_WAIT_TIME)) != HAL_OK) + goto out; + + HAL_CE_EnableCCMU(); + + /* CE config */ + CE_Crypto_Init(CE, CE_CTL_METHOD_AES, aes->mode, CE_CRYPT_OP_ENCRYPTION); + CE_AES_SetKey(CE, aes->src, aes->keysize, (uint32_t *)aes->key); + if ((CE_CTL_Crypto_Mode)aes->mode == CE_CTL_CRYPT_MODE_CBC) + CE_Crypto_SetIV(CE, (uint32_t *)aes->iv, 16); +#ifdef CE_CTR_MODE + else if ((CE_CTL_Crypto_Mode)aes->mode == CE_CTL_CRYPT_MODE_CTR) + CE_Crypto_SetCounter(CE, (uint32_t *)aes->cnt, aes->width); +#endif +#ifdef CE_CTS_MODE + else if ((CE_CTL_Crypto_Mode)aes->mode == CE_CTL_CRYPT_MODE_CTS) + CE_Crypto_SetIV(CE, (uint32_t *)aes->iv, 16); +#endif + + CE_REG_ALL(CE); + + /* plain data convey to CE and cipher data convey from CE */ + HAL_Crypto_Convey(plain, cipher, size, AES_BLOCK_SIZE); + + CE_REG_ALL(CE); + /* CE state storage */ + if ((CE_CTL_Crypto_Mode)aes->mode == CE_CTL_CRYPT_MODE_CBC) + CE_Crypto_GetIV(CE, (uint32_t *)aes->iv, 16); +#ifdef CE_CTR_MODE + else if ((CE_CTL_Crypto_Mode)aes->mode == CE_CTL_CRYPT_MODE_CTR) { + CE_Crypto_GetCounter(CE, (uint32_t *)aes->cnt, aes->width); + aes->cnt[15] += (size + 15) / 16; + } +#endif +#ifdef CE_CTS_MODE + else if ((CE_CTL_Crypto_Mode)aes->mode == CE_CTL_CRYPT_MODE_CTS) + CE_Crypto_GetIV(CE, (uint32_t *)aes->iv, 16); +#endif + + CE_Crypto_Deinit(CE); + HAL_CE_DisableCCMU(); + HAL_MutexUnlock(&ce_lock); +out: + ce_running = 0; + CE_EXIT(ret); + return ret; +} + +/** + * @brief Decrypt data by AES. + * @note AES128 & AES192 & AES256 limit the key size, oversize of key will be cut + * off. AES block size is 16 bytes. and please reset the aes config if doing + * a loopback(HAL_AES_Encrypt then HAL_AES_Decrypt). + * @param aes: + * @arg aes->mode: CE_CRYPT_MODE_ECB or CE_CRYPT_MODE_CBC + * @arg aes->iv[16]: initialization value, is same size as block size. + * CE_CRYPT_MODE_CBC mode need this param, and same as + * the encrypt side. + * @arg aes->src: CE_CTL_KEYSOURCE_INPUT: key is set from aes->key. + * CE_CTL_KEYSOURCE_SID: is not support for now. + * CE_CTL_KEYSOURCE_INTERNAL0~7: is not support for now. + * @arg aes->key[32]: store the key. please notice the size should same as + * the algorithm. example: AES128 should only use + * key[0]~key[15]. + * @arg aes->keysize: CE_CTL_AES_KEYSIZE_128BITS: algorithm of AES128. + * CE_CTL_AES_KEYSIZE_192BITS: algorithm of AES192. + * CE_CTL_AES_KEYSIZE_256BITS: algorithm of AES256. + * @param cipher: cipher data which is needed to be decrypted. the cipher data + * size must be multiple of 16 bytes. + * @param plain: plain data which store the decrypted data. the plain data size + * will be same as cipher data. + * @param size: size of cipher data. size must be multiple of 16 bytes. + * @retval HAL_Status: The status of driver + */ +HAL_Status HAL_AES_Decrypt(CE_AES_Config *aes, uint8_t *cipher, uint8_t *plain, uint32_t size) +{ + HAL_Status ret = HAL_OK; + CE_ENTRY(); + + if (size == 0) { + ret = HAL_INVALID; + goto out; + } + + ce_running = 1; + if ((ret = HAL_MutexLock(&ce_lock, CE_WAIT_TIME)) != HAL_OK) + goto out; + + HAL_CE_EnableCCMU(); + + /* CE config */ + CE_Crypto_Init(CE, CE_CTL_METHOD_AES, aes->mode, CE_CRYPT_OP_DECRYPTION); + CE_AES_SetKey(CE, aes->src, aes->keysize, (uint32_t *)aes->key); + if ((CE_CTL_Crypto_Mode)aes->mode == CE_CTL_CRYPT_MODE_CBC) + CE_Crypto_SetIV(CE, (uint32_t *)aes->iv, 16); +#ifdef CE_CTR_MODE + else if ((CE_CTL_Crypto_Mode)aes->mode == CE_CTL_CRYPT_MODE_CTR) + CE_Crypto_SetCounter(CE, (uint32_t *)aes->cnt, aes->width); +#endif +#ifdef CE_CTS_MODE + else if ((CE_CTL_Crypto_Mode)aes->mode == CE_CTL_CRYPT_MODE_CTS) + CE_Crypto_SetIV(CE, (uint32_t *)aes->iv, 16); +#endif + + CE_REG_ALL(CE); + + /* plain data convey to CE and cipher data convey from CE */ + HAL_Crypto_Convey(cipher, plain, size, AES_BLOCK_SIZE); + + CE_REG_ALL(CE); + + /* CE state storage */ + if ((CE_CTL_Crypto_Mode)aes->mode == CE_CTL_CRYPT_MODE_CBC) + CE_Crypto_GetIV(CE, (uint32_t *)aes->iv, 16); +#ifdef CE_CTR_MODE + else if ((CE_CTL_Crypto_Mode)aes->mode == CE_CTL_CRYPT_MODE_CTR) { + CE_Crypto_GetCounter(CE, (uint32_t *)aes->cnt, aes->width); + aes->cnt[15] += (size + 15) / 16; + } +#endif +#ifdef CE_CTS_MODE + else if ((CE_CTL_Crypto_Mode)aes->mode == CE_CTL_CRYPT_MODE_CTS) + CE_Crypto_GetIV(CE, (uint32_t *)aes->iv, 16); +#endif + + CE_Crypto_Deinit(CE); + HAL_CE_DisableCCMU(); + HAL_MutexUnlock(&ce_lock); +out: + ce_running = 0; + CE_EXIT(ret); + return ret; +} + +/** + * @brief Encrypt data by DES. + * @note DES key size is 8 bytes. DES block size is 8 bytes. + * @param des: + * @arg des->mode: CE_CRYPT_MODE_ECB or CE_CRYPT_MODE_CBC + * @arg des->iv[8]: initialization value, is same size as block size. + * CE_CRYPT_MODE_CBC mode need this param. + * @arg des->src: CE_CTL_KEYSOURCE_INPUT: key is set from des->key. + * CE_CTL_KEYSOURCE_SID: is not support for now. + * CE_CTL_KEYSOURCE_INTERNAL0~7: is not support for now. + * @arg des->key[8]: store the key. + * @param plain: plain data which is need to be encrypt. + * @param cipher: cipher data which is store the encrpyted data. the cipher data + * size is multiple of 8 bytes and no less than plain data size. + * @param size: size of plain data. + * @retval HAL_Status: The status of driver + */ +HAL_Status HAL_DES_Encrypt(CE_DES_Config *des, uint8_t *plain, uint8_t *cipher, uint32_t size) +{ + HAL_Status ret = HAL_OK; + CE_ENTRY(); + + if (size == 0) { + ret = HAL_INVALID; + goto out; + } + + ce_running = 1; + if ((ret = HAL_MutexLock(&ce_lock, CE_WAIT_TIME)) != HAL_OK) + goto out; + + HAL_CE_EnableCCMU(); + + /* CE config */ + CE_Crypto_Init(CE, CE_CTL_METHOD_DES, des->mode, CE_CRYPT_OP_ENCRYPTION); + CE_DES_SetKey(CE, des->src, (uint32_t *)des->key); + if ((CE_CTL_Crypto_Mode)des->mode == CE_CTL_CRYPT_MODE_CBC) + CE_Crypto_SetIV(CE, (uint32_t *)des->iv, 8); +#ifdef CE_CTR_MODE + else if ((CE_CTL_Crypto_Mode)des->mode == CE_CTL_CRYPT_MODE_CTR) + CE_Crypto_SetCounter(CE, (uint32_t *)des->cnt, des->width); +#endif +#ifdef CE_CTS_MODE + else if ((CE_CTL_Crypto_Mode)des->mode == CE_CTL_CRYPT_MODE_CTS) + CE_Crypto_SetIV(CE, (uint32_t *)des->iv, 8); +#endif + + /* plain data convey to CE and cipher data convey from CE */ + HAL_Crypto_Convey(plain, cipher, size, DES_BLOCK_SIZE); + + /* CE state storage */ + if ((CE_CTL_Crypto_Mode)des->mode == CE_CTL_CRYPT_MODE_CBC) + CE_Crypto_GetIV(CE, (uint32_t *)des->iv, 8); +#ifdef CE_CTR_MODE + else if ((CE_CTL_Crypto_Mode)des->mode == CE_CTL_CRYPT_MODE_CTR) + CE_Crypto_GetCounter(CE, (uint32_t *)des->cnt, des->width); +#endif +#ifdef CE_CTS_MODE + else if ((CE_CTL_Crypto_Mode)des->mode == CE_CTL_CRYPT_MODE_CTS) + CE_Crypto_GetIV(CE, (uint32_t *)des->iv, 8); +#endif + + CE_Crypto_Deinit(CE); + HAL_CE_DisableCCMU(); + HAL_MutexUnlock(&ce_lock); +out: + ce_running = 0; + CE_EXIT(ret); + return ret; +} + +/** + * @brief Encrypt data by DES. + * @note DES key size is 8 bytes. DES block size is 8 bytes. + * @param des: + * @arg aes->mode: CE_CRYPT_MODE_ECB or CE_CRYPT_MODE_CBC + * @arg aes->iv[8]: initialization value, is same size as block size. + * CE_CRYPT_MODE_CBC mode need this param, and same as + * the encrypt side. + * @arg aes->src: CE_CTL_KEYSOURCE_INPUT: key is set from aes->key. + * CE_CTL_KEYSOURCE_SID: is not support for now. + * CE_CTL_KEYSOURCE_INTERNAL0~7: is not support for now. + * @arg aes->key[8]: store the key. + * @param cipher: cipher data which is needed to be decrypted. the cipher data + * size must be multiple of 8 bytes. + * @param plain: plain data which store the decrypted data. the plain data size + * will be same as cipher data. + * @param size: size of cipher data. size must be multiple of 8 bytes. + * @retval HAL_Status: The status of driver + */ +HAL_Status HAL_DES_Decrypt(CE_DES_Config *des, uint8_t *cipher, uint8_t *plain, uint32_t size) +{ + HAL_Status ret = HAL_OK; + CE_ENTRY(); + + if (size == 0) { + ret = HAL_INVALID; + goto out; + } + + ce_running = 1; + if ((ret = HAL_MutexLock(&ce_lock, CE_WAIT_TIME)) != HAL_OK) + goto out; + + HAL_CE_EnableCCMU(); + + /* CE config */ + CE_Crypto_Init(CE, CE_CTL_METHOD_DES, des->mode, CE_CRYPT_OP_DECRYPTION); + CE_DES_SetKey(CE, des->src, (uint32_t *)des->key); + if ((CE_CTL_Crypto_Mode)des->mode == CE_CTL_CRYPT_MODE_CBC) + CE_Crypto_SetIV(CE, (uint32_t *)des->iv, 8); +#ifdef CE_CTR_MODE + else if ((CE_CTL_Crypto_Mode)des->mode == CE_CTL_CRYPT_MODE_CTR) + CE_Crypto_SetCounter(CE, (uint32_t *)des->cnt, des->width); +#endif +#ifdef CE_CTS_MODE + else if ((CE_CTL_Crypto_Mode)des->mode == CE_CTL_CRYPT_MODE_CTS) + CE_Crypto_SetIV(CE, (uint32_t *)des->iv, 8); +#endif + + /* plain data convey to CE and cipher data convey from CE */ + HAL_Crypto_Convey(cipher, plain, size, DES_BLOCK_SIZE); + + /* CE state storage */ + if ((CE_CTL_Crypto_Mode)des->mode == CE_CTL_CRYPT_MODE_CBC) + CE_Crypto_GetIV(CE, (uint32_t *)des->iv, 8); +#ifdef CE_CTR_MODE + else if ((CE_CTL_Crypto_Mode)des->mode == CE_CTL_CRYPT_MODE_CTR) + CE_Crypto_GetCounter(CE, (uint32_t *)des->cnt, des->width); +#endif +#ifdef CE_CTS_MODE + else if ((CE_CTL_Crypto_Mode)des->mode == CE_CTL_CRYPT_MODE_CTS) + CE_Crypto_GetIV(CE, (uint32_t *)des->iv, 8); +#endif + + CE_Crypto_Deinit(CE); + HAL_CE_DisableCCMU(); + HAL_MutexUnlock(&ce_lock); +out: + ce_running = 0; + CE_EXIT(ret); + return ret; +} + +/** + * @brief Encrypt data by 3DES. + * @note 3DES key size is 24 bytes, or 3 * 8bytes(8bytes key). 3DES block size is 8 bytes. + * @param des: + * @arg des->mode: CE_CRYPT_MODE_ECB or CE_CRYPT_MODE_CBC + * @arg des->iv[8]: initialization value, is same size as block size. + * CE_CRYPT_MODE_CBC mode need this param. + * @arg des->src: CE_CTL_KEYSOURCE_INPUT: key is set from des->key. + * CE_CTL_KEYSOURCE_SID: is not support for now. + * CE_CTL_KEYSOURCE_INTERNAL0~7: is not support for now. + * @arg des->key[24]: store the key. some 3DES tool key size may be 8 bytes, is 3 + * copy of the key. + * @param plain: plain data which is need to be encrypt. + * @param cipher: cipher data which is store the encrpyted data. the cipher data + * size is multiple of 8 bytes and no less than plain data size. + * @param size: size of plain data. + * @retval HAL_Status: The status of driver + */ +HAL_Status HAL_3DES_Encrypt(CE_3DES_Config *des, uint8_t *plain, uint8_t *cipher, uint32_t size) +{ + HAL_Status ret = HAL_OK; + CE_ENTRY(); + + if (size == 0) { + ret = HAL_INVALID; + goto out; + } + + ce_running = 1; + if ((ret = HAL_MutexLock(&ce_lock, CE_WAIT_TIME)) != HAL_OK) + goto out; + + HAL_CE_EnableCCMU(); + + /* CE config */ + CE_Crypto_Init(CE, CE_CTL_METHOD_3DES, des->mode, CE_CRYPT_OP_ENCRYPTION); + CE_3DES_SetKey(CE, des->src, (uint32_t *)des->key); + if ((CE_CTL_Crypto_Mode)des->mode == CE_CTL_CRYPT_MODE_CBC) + CE_Crypto_SetIV(CE, (uint32_t *)des->iv, 8); +#ifdef CE_CTR_MODE + else if ((CE_CTL_Crypto_Mode)des->mode == CE_CTL_CRYPT_MODE_CTR) + CE_Crypto_SetCounter(CE, (uint32_t *)des->cnt, des->width); +#endif +#ifdef CE_CTS_MODE + else if ((CE_CTL_Crypto_Mode)des->mode == CE_CTL_CRYPT_MODE_CTS) + CE_Crypto_SetIV(CE, (uint32_t *)des->iv, 8); +#endif + + /* plain data convey to CE and cipher data convey from CE */ + HAL_Crypto_Convey(plain, cipher, size, DES3_BLOCK_SIZE); + + /* CE state storage */ + if ((CE_CTL_Crypto_Mode)des->mode == CE_CTL_CRYPT_MODE_CBC) + CE_Crypto_GetIV(CE, (uint32_t *)des->iv, 8); +#ifdef CE_CTR_MODE + else if ((CE_CTL_Crypto_Mode)des->mode == CE_CTL_CRYPT_MODE_CTR) + CE_Crypto_GetCounter(CE, (uint32_t *)des->cnt, des->width); +#endif +#ifdef CE_CTS_MODE + else if ((CE_CTL_Crypto_Mode)des->mode == CE_CTL_CRYPT_MODE_CTS) + CE_Crypto_GetIV(CE, (uint32_t *)des->iv, 8); +#endif + + CE_Crypto_Deinit(CE); + HAL_CE_DisableCCMU(); + HAL_MutexUnlock(&ce_lock); +out: + ce_running = 0; + CE_EXIT(ret); + return ret; +} + +/** + * @brief Encrypt data by 3DES. + * @note 3DES key size is 24 bytes, or 3 * 8bytes(8bytes key). 3DES block size + * is 8 bytes. + * @param des: + * @arg aes->mode: CE_CRYPT_MODE_ECB or CE_CRYPT_MODE_CBC + * @arg aes->iv[8]: initialization value, is same size as block size. + * CE_CRYPT_MODE_CBC mode need this param, and same as + * the encrypt side. + * @arg aes->src: CE_CTL_KEYSOURCE_INPUT: key is set from aes->key. + * CE_CTL_KEYSOURCE_SID: is not support for now. + * CE_CTL_KEYSOURCE_INTERNAL0~7: is not support for now. + * @arg des->key[24]: store the key. some 3DES tool key size may be 8 + * bytes, is 3 copy of the key. + * @param cipher: cipher data which is needed to be decrypted. the cipher data + * size must be multiple of 8 bytes. + * @param plain: plain data which store the decrypted data. the plain data size + * will be same as cipher data. + * @param size: size of cipher data. size must be multiple of 8 bytes. + * @retval HAL_Status: The status of driver + */ +HAL_Status HAL_3DES_Decrypt(CE_3DES_Config *des, uint8_t *cipher, uint8_t *plain, uint32_t size) +{ + HAL_Status ret = HAL_OK; + CE_ENTRY(); + + if (size == 0) { + ret = HAL_INVALID; + goto out; + } + + ce_running = 1; + if ((ret = HAL_MutexLock(&ce_lock, CE_WAIT_TIME)) != HAL_OK) + goto out; + + HAL_CE_EnableCCMU(); + + /* CE config */ + CE_Crypto_Init(CE, CE_CTL_METHOD_3DES, des->mode, CE_CRYPT_OP_DECRYPTION); + CE_3DES_SetKey(CE, des->src, (uint32_t *)des->key); + if ((CE_CTL_Crypto_Mode)des->mode == CE_CTL_CRYPT_MODE_CBC) + CE_Crypto_SetIV(CE, (uint32_t *)des->iv, 8); +#ifdef CE_CTR_MODE + else if ((CE_CTL_Crypto_Mode)des->mode == CE_CTL_CRYPT_MODE_CTR) + CE_Crypto_SetCounter(CE, (uint32_t *)des->cnt, des->width); +#endif +#ifdef CE_CTS_MODE + else if ((CE_CTL_Crypto_Mode)des->mode == CE_CTL_CRYPT_MODE_CTS) + CE_Crypto_SetIV(CE, (uint32_t *)des->iv, 8); +#endif + + /* plain data convey to CE and cipher data convey from CE */ + HAL_Crypto_Convey(cipher, plain, size, 8); + + /* CE state storage */ + if ((CE_CTL_Crypto_Mode)des->mode == CE_CTL_CRYPT_MODE_CBC) + CE_Crypto_GetIV(CE, (uint32_t *)des->iv, 8); +#ifdef CE_CTR_MODE + else if ((CE_CTL_Crypto_Mode)des->mode == CE_CTL_CRYPT_MODE_CTR) + CE_Crypto_GetCounter(CE, (uint32_t *)des->cnt, des->width); +#endif +#ifdef CE_CTS_MODE + else if ((CE_CTL_Crypto_Mode)des->mode == CE_CTL_CRYPT_MODE_CTS) + CE_Crypto_GetIV(CE, (uint32_t *)des->iv, 8); +#endif + + CE_Crypto_Deinit(CE); + HAL_CE_DisableCCMU(); + HAL_MutexUnlock(&ce_lock); +out: + ce_running = 0; + CE_EXIT(ret); + return ret; +} + + +/************************ CRC MD5 SHA1 SHA256 private **************************************/ +typedef struct { + CE_CTL_CRC_Width width; + uint32_t poly; + uint8_t init; + uint8_t refin; + uint8_t refout; + uint8_t xorout; +} CE_CRC_Config; + +static const CE_CRC_Config crc_cfg[] = { + {CE_CTL_CRC_WIDTH_16BITS, 0x8005, 0, 1, 1, 0}, /* CE_CRC16_IBM, */ + {CE_CTL_CRC_WIDTH_16BITS, 0x8005, 0, 1, 1, 1}, /* CE_CRC16_MAXIM, */ + {CE_CTL_CRC_WIDTH_16BITS, 0x8005, 1, 1, 1, 1}, /* CE_CRC16_USB, */ + {CE_CTL_CRC_WIDTH_16BITS, 0x8005, 1, 1, 1, 0}, /* CE_CRC16_MODBUS, */ + {CE_CTL_CRC_WIDTH_16BITS, 0x1021, 0, 1, 1, 0}, /* CE_CRC16_CCITT, */ + {CE_CTL_CRC_WIDTH_16BITS, 0x1021, 1, 0, 0, 0}, /* CE_CRC16_CCITT_1 */ + {CE_CTL_CRC_WIDTH_16BITS, 0x1021, 1, 1, 1, 1}, /* CE_CRC16_X25, */ + {CE_CTL_CRC_WIDTH_16BITS, 0x1021, 0, 0, 0, 0}, /* CE_CRC16_XMODEM, */ + {CE_CTL_CRC_WIDTH_16BITS, 0x3d65, 0, 1, 1, 1}, /* CE_CRC16_DNP, */ + {CE_CTL_CRC_WIDTH_32BITS, 0x04c11db7, 1, 1, 1, 1}, /* CE_CRC32, */ + {CE_CTL_CRC_WIDTH_32BITS, 0x04c11db7, 1, 0, 0, 0}, /* CE_CRC32_MPEG2, */ +}; + +static HAL_Status HAL_CRC_Hash_InitDMA(DMA_Channel *input) +{ + HAL_Status ret = HAL_OK; + DMA_ChannelInitParam Input_param; + memset(&Input_param, 0, sizeof(Input_param)); + CE_ENTRY(); + + if ((*input = HAL_DMA_Request()) == DMA_CHANNEL_INVALID) { + CE_ALERT("DMA request failed \n"); + goto out; + } + + Input_param.cfg = HAL_DMA_MakeChannelInitCfg(DMA_WORK_MODE_SINGLE, + DMA_WAIT_CYCLE_16, + DMA_BYTE_CNT_MODE_REMAIN, + DMA_DATA_WIDTH_32BIT, + DMA_BURST_LEN_1, + DMA_ADDR_MODE_FIXED, + DMA_PERIPH_CE, + DMA_DATA_WIDTH_8BIT, + DMA_BURST_LEN_4, + DMA_ADDR_MODE_INC, + DMA_PERIPH_SRAM); + Input_param.irqType = DMA_IRQ_TYPE_END; + Input_param.endCallback = HAL_CE_DMACmpl; + Input_param.endArg = &ce_block; + + HAL_DMA_Init(*input, &Input_param); + CE_EnableDMA(CE); + +out: + CE_EXIT(ret); + return ret; +} + +static void HAL_CRC_Hash_DenitDMA(DMA_Channel input) +{ + CE_ENTRY(); + CE_DisableDMA(CE); + HAL_DMA_DeInit(input); + HAL_DMA_Release(input); + CE_EXIT(0); +} + +static HAL_Status HAL_CRC_Hash_Convey(DMA_Channel input, CE_Fifo_Align *align, uint8_t *data, uint32_t size) +{ + HAL_Status ret = HAL_OK; + uint32_t align_len; + uint32_t buf_left = 4 - align->word_size; + CE_ENTRY(); + CE_DEBUG("input size = %d, buf_left = %d\n", size, buf_left); + + if (size < buf_left) { + memcpy(align->word, data, size); + align->word_size += size; + CE_ASSERT(align->word_size < 4); + goto out; + } + + CE_SetInputThreshold(CE, 0); + + memcpy(&align->word[align->word_size], data, buf_left); + HAL_DMA_Start(input, (uint32_t)align->word, (uint32_t)CE_Crypto_GetInputAddr(CE), 4); + if ((ret = HAL_SemaphoreWait(&ce_block, CE_WAIT_TIME)) != HAL_OK) { + CE_ALERT("DMA transfer 1st block failed\n"); + goto out; + } + HAL_DMA_Stop(input); + + align->word_size = (size - buf_left) & 0x3; // len % 4 + align_len = size - buf_left - align->word_size; +// CE_DEBUG("align_len = %d, new buf_left should be = %d\n", align_len, align->word_size); + + if (align_len != 0) { + HAL_DMA_Start(input, (uint32_t)(data + buf_left), (uint32_t)CE_Crypto_GetInputAddr(CE), align_len); + if ((ret = HAL_SemaphoreWait(&ce_block, CE_WAIT_TIME)) != HAL_OK) { + CE_ALERT("DMA transfer 2rd block failed\n"); + goto out; + } + HAL_DMA_Stop(input); + } + +// CE_DEBUG("data copy from %d\n", size - align->word_size); + memset(align->word, 0, sizeof(align->word)); + memcpy(align->word, data + size - align->word_size, align->word_size); + +out: + CE_EXIT(ret); + return ret; +} + +HAL_Status HAL_MD5_Append(CE_MD5_Handler *hdl, uint8_t *data, uint32_t size); + +static void HAL_Hash_Finish(CE_MD5_Handler *hdl, uint64_t bit_size) +{ + uint32_t pad_size; + uint32_t remain_size = hdl->total_size & 0x3f; // len % 64 + uint32_t total_size = hdl->total_size; + uint8_t pad[128] = {0}; + + CE_ENTRY(); +// CE_DEBUG("total size before padding = %d, remain size = %d.\n", (uint32_t)hdl->total_size, (uint32_t)remain_size); + + if (remain_size < 56) + pad_size = 64 - remain_size - 8 - 1; + else + pad_size = 128 - remain_size - 8 - 1; + pad[0] = 0x80; + memcpy(&pad[1 + pad_size], &bit_size, 8); + +// CE_DEBUG("pad size = %d, buf size = %d, len copy to %d.\n", (uint32_t)pad_size, (uint32_t)hdl->word_size, (uint32_t)(1 + pad_size)); + + HAL_MD5_Append(hdl, pad, pad_size + 1 + 8); + + CE_ASSERT(((total_size + pad_size + 1 + 8) & 0x3f) == 0); +// CE_DEBUG("append size = %d, final total size = 0x%x.\n", (uint32_t)(pad_size + 1 + 8), (uint32_t)(total_size + pad_size + 1 + 8)); + + CE_Hash_Finish(CE); + CE_EXIT(0); +} +/************************ public **************************************/ + +/** + * @brief Initialize CRC module. + * @param hdl: It's a handler stored private info. created by user. + * @param type: CRC algorithm. + * @param total_size: the total size of data needed to calculate crc. + * @retval HAL_Status: The status of driver + */ +HAL_Status HAL_CRC_Init1(CE_CRC_Handler *hdl, CE_CRC_Types type, uint32_t total_size) +{ + HAL_Status ret = HAL_OK; + const CE_CRC_Config *config = &crc_cfg[type]; + + CE_ENTRY(); + + if (total_size == 0) { + ret = HAL_INVALID; + goto out; + } + + ce_running = 1; + if ((ret = HAL_MutexLock(&ce_lock, CE_WAIT_TIME)) != HAL_OK) + goto out; + HAL_CE_EnableCCMU(); + + hdl->type = type; + hdl->crc = 0; + hdl->word_size = 0; + HAL_Memset(hdl->word, 0, sizeof(hdl->word)); + + CE_CRC_Init(CE, config->init, + config->xorout, + config->refin, + config->refout, + config->width, + config->poly); + CE_CRC_SetResult(CE, 0); + CE_SetLength(CE, total_size); + CE_Enable(CE); + + CE_REG_ALL(CE); + +out: + CE_EXIT(ret); + return ret; +} + +HAL_Status HAL_CRC_Init(CE_CRC_Handler *hdl, CE_CRC_Types type, uint32_t total_size) +{ + uint32_t crc; + + /* temperarily bug fixed */ + HAL_CRC_Init1(hdl, CE_CRC32, 1); + HAL_CRC_Append(hdl, (uint8_t *)hdl, 1); + HAL_CRC_Finish(hdl, &crc); + + HAL_Memset(hdl, 0, sizeof(*hdl)); + return HAL_CRC_Init1(hdl, type, total_size); +} + + +/** + * @brief Append data to calculate crc. it can be append several times before + * HAL_CRC_Finish. + * @param hdl: It's a handler stored private info. + * @param data: data needed to calculate crc. + * @param size: size of data. + * @retval HAL_Status: The status of driver + */ +HAL_Status HAL_CRC_Append(CE_CRC_Handler *hdl, uint8_t *data, uint32_t size) +{ + HAL_Status ret = HAL_OK; + DMA_Channel input; + + CE_ENTRY(); + + if (size == 0) { + ret = HAL_INVALID; + goto out; + } + + if ((ret = HAL_CRC_Hash_InitDMA(&input)) != HAL_OK){ + CE_ALERT("DMA Request failed\n"); + goto out; + } + HAL_CRC_Hash_Convey(input, (CE_Fifo_Align *)(&hdl->word[0]), data, size); + HAL_CRC_Hash_DenitDMA(input); + + hdl->total_size += size; + +out: + CE_EXIT(ret); + return ret; +} + +/** + * @brief Calculate the crc of all appended data. + * @param hdl: It's a handler stored private info. + * @param crc: Store the result of calculation of crc. + * @retval HAL_Status: The status of driver + */ +HAL_Status HAL_CRC_Finish(CE_CRC_Handler *hdl, uint32_t *crc) +{ + HAL_Status ret = HAL_OK; + uint8_t pad[4] = {0}; + + CE_ENTRY(); + + HAL_CRC_Append(hdl, pad, (4 - hdl->word_size) & 0x03); + + CE_CRC_Finish(CE); + while (CE_Status(CE, CE_INT_TPYE_HASH_CRC_END) == 0); // use irq would be better + CE_CRC_Calc(CE, crc); + CE_REG_ALL(CE); + CE_REG(CE->CRC_RESULT); + + CE_Disable(CE); + CE_CRC_Deinit(CE); + HAL_CE_DisableCCMU(); + HAL_MutexUnlock(&ce_lock); + + ce_running = 0; + CE_EXIT(ret); + return ret; +} + +/** + * @brief Initialize MD5 module. + * @param hdl: It's a handler stored private info. created by user. + * @param src: Selected IV source + * @arg CE_CTL_IVMODE_SHA_MD5_FIPS180: use FIPS180 IV. + * @arg CE_CTL_IVMODE_SHA_MD5_INPUT: IV from param iv[4]. + * @param iv: MD5 Initialization Value. size of iv must be 16 bytes. + * @retval HAL_Status: The status of driver + */ +HAL_Status HAL_MD5_Init(CE_MD5_Handler *hdl, CE_Hash_IVsrc src, uint32_t iv[4]) +{ + HAL_Status ret = HAL_OK; + + CE_ENTRY(); + + ce_running = 1; + if ((ret = HAL_MutexLock(&ce_lock, CE_WAIT_TIME)) != HAL_OK) + goto out; + + HAL_CE_EnableCCMU(); + CE_Hash_Init(CE, CE_CTL_METHOD_MD5); + CE_Hash_SetIV(CE, src, iv, CE_MD5_IV_SIZE); + + hdl->total_size = 0; + hdl->word_size = 0; + memset(hdl->word, 0, sizeof(hdl->word)); + + CE_REG_ALL(CE); + +out: + CE_EXIT(ret); + return ret; +} + +/** + * @brief Append MD5 data to calculate. + * @param hdl: It's a handler stored private info. created by user. + * @param data: the data needed to calculate md5. + * @param size: size of data. + * @retval HAL_Status: The status of driver + */ +HAL_Status HAL_MD5_Append(CE_MD5_Handler *hdl, uint8_t *data, uint32_t size) +{ + HAL_Status ret = HAL_OK; + DMA_Channel input; + + CE_ENTRY(); + + if (size == 0) { + ret = HAL_INVALID; + goto out; + } + + if ((ret = HAL_CRC_Hash_InitDMA(&input)) != HAL_OK){ + CE_ALERT("DMA Request failed\n"); + goto out; + } + HAL_CRC_Hash_Convey(input, (CE_Fifo_Align *)(&hdl->word[0]), data, size); + HAL_CRC_Hash_DenitDMA(input); + + hdl->total_size += size; + +out: + CE_EXIT(ret); + return ret; +} + +/** + * @brief Calculate the MD5 of all appended data. + * @param hdl: It's a handler stored private info. + * @param digest: Store the result of calculation of MD5, result + * need 16 bytes space. + * @retval HAL_Status: The status of driver + */ +HAL_Status HAL_MD5_Finish(CE_MD5_Handler *hdl, uint32_t digest[4]) +{ + HAL_Status ret = HAL_OK; + uint64_t bit_size = htole64(hdl->total_size << 3); + + CE_ENTRY(); + + HAL_Hash_Finish(hdl, bit_size); + while (CE_Status(CE, CE_INT_TPYE_HASH_CRC_END) == 0); // use irq would be better + CE_REG_ALL(CE); + CE_Hash_Calc(CE, CE_CTL_METHOD_MD5, digest); + + CE_Hash_Deinit(CE); + HAL_CE_DisableCCMU(); + HAL_MutexUnlock(&ce_lock); + + ce_running = 0; + CE_EXIT(ret); + return ret; +} + +/** + * @brief Initialize SHA1 module. + * @param hdl: It's a handler stored private info. created by user. + * @param src: Selected IV source + * @arg CE_CTL_IVMODE_SHA_MD5_FIPS180: use FIPS180 IV. + * @arg CE_CTL_IVMODE_SHA_MD5_INPUT: IV from param iv[5]. + * @param iv: SHA1 Initialization Value. size of iv must be 20 bytes. + * @retval HAL_Status: The status of driver + */ +HAL_Status HAL_SHA1_Init(CE_SHA1_Handler *hdl, CE_Hash_IVsrc src, uint32_t iv[5]) +{ + HAL_Status ret = HAL_OK; + + CE_ENTRY(); + + ce_running = 1; + if ((ret = HAL_MutexLock(&ce_lock, CE_WAIT_TIME)) != HAL_OK) + goto out; + + HAL_CE_EnableCCMU(); + CE_Hash_Init(CE, CE_CTL_METHOD_SHA1); + CE_Hash_SetIV(CE, src, iv, CE_SHA1_IV_SIZE); + CE_REG_ALL(CE); + + hdl->total_size = 0; + hdl->word_size = 0; + memset(hdl->word, 0, sizeof(hdl->word)); + +out: + CE_EXIT(ret); + return ret; +} + +/** + * @brief Append SHA1 data to calculate. + * @param hdl: It's a handler stored private info. created by user. + * @param data: the data needed to calculate sha1. + * @param size: size of data. + * @retval HAL_Status: The status of driver + */ +HAL_Status HAL_SHA1_Append(CE_SHA1_Handler *hdl, uint8_t *data, uint32_t size) +{ + HAL_Status ret; + CE_ENTRY(); + + ret = HAL_MD5_Append(hdl, data, size); + + CE_EXIT(ret); + return ret; +} + +/** + * @brief Calculate the SHA1 of all appended data. + * @param hdl: It's a handler stored private info. + * @param digest: Store the result of calculation of SHA1, result + * need 20 bytes space. + * @retval HAL_Status: The status of driver + */ +HAL_Status HAL_SHA1_Finish(CE_SHA1_Handler *hdl, uint32_t digest[5]) +{ + HAL_Status ret = HAL_OK; + uint64_t bit_size = htobe64(hdl->total_size << 3); + CE_ENTRY(); + + HAL_Hash_Finish(hdl, bit_size); + while (CE_Status(CE, CE_INT_TPYE_HASH_CRC_END) == 0); // use irq + CE_REG_ALL(CE); + CE_Hash_Calc(CE, CE_CTL_METHOD_SHA1, digest); + + CE_Hash_Deinit(CE); + HAL_CE_DisableCCMU(); + HAL_MutexUnlock(&ce_lock); + + ce_running = 0; + CE_EXIT(ret); + return ret; +} + +/** + * @brief Initialize SHA256 module. + * @param hdl: It's a handler stored private info. created by user. + * @param src: Selected IV source + * @arg CE_CTL_IVMODE_SHA_MD5_FIPS180: use FIPS180 IV. + * @arg CE_CTL_IVMODE_SHA_MD5_INPUT: IV from param iv[8]. + * @param iv: SHA256 Initialization Value. size of iv must be 32 bytes. + * @retval HAL_Status: The status of driver + */ +HAL_Status HAL_SHA256_Init(CE_SHA256_Handler *hdl, CE_Hash_IVsrc src, uint32_t iv[8]) +{ + HAL_Status ret = HAL_OK; + + CE_ENTRY(); + + ce_running = 1; + if ((ret = HAL_MutexLock(&ce_lock, CE_WAIT_TIME)) != HAL_OK) + goto out; + + HAL_CE_EnableCCMU(); + CE_Hash_Init(CE, CE_CTL_METHOD_SHA256); + CE_Hash_SetIV(CE, src, iv, CE_SHA256_IV_SIZE); + CE_REG_ALL(CE); + + hdl->total_size = 0; + hdl->word_size = 0; + memset(hdl->word, 0, sizeof(hdl->word)); + +out: + CE_EXIT(ret); + return ret; +} + +/** + * @brief Append SHA256 data to calculate. + * @param hdl: It's a handler stored private info. created by user. + * @param data: the data needed to calculate sha256. + * @param size: size of data. + * @retval HAL_Status: The status of driver + */ +HAL_Status HAL_SHA256_Append(CE_SHA256_Handler *hdl, uint8_t *data, uint32_t size) +{ + HAL_Status ret; + CE_ENTRY(); + + ret = HAL_MD5_Append(hdl, data, size); + + CE_EXIT(ret); + return ret; +} + +/** + * @brief Calculate the SHA256 of all appended data. + * @param hdl: It's a handler stored private info. + * @param digest: Store the result of calculation of SHA256, result + * need 32 bytes space. + * @retval HAL_Status: The status of driver + */ +HAL_Status HAL_SHA256_Finish(CE_SHA256_Handler *hdl, uint32_t digest[8]) +{ + HAL_Status ret = HAL_OK; + uint64_t bit_size = htobe64(hdl->total_size << 3); + + CE_ENTRY(); + + HAL_Hash_Finish(hdl, bit_size); + while (CE_Status(CE, CE_INT_TPYE_HASH_CRC_END) == 0); // use irq would be better + CE_REG_ALL(CE); + CE_Hash_Calc(CE, CE_CTL_METHOD_SHA256, digest); + + CE_Hash_Deinit(CE); + HAL_CE_DisableCCMU(); + HAL_MutexUnlock(&ce_lock); + + ce_running = 0; + CE_EXIT(ret); + return ret; +} + +/** + * @brief Generate some random numbers. + * @param random: a buffer to store random number, create by user. + * @param size: size of random number to generate. + * @retval HAL_Status: The status of driver + */ +HAL_Status HAL_PRNG_Generate(uint8_t *random, uint32_t size) +{ + HAL_Status ret = HAL_OK; + uint32_t seed[6]; + uint32_t rand[5]; + uint8_t *p = random; + uint32_t cpsize; + + CE_ENTRY(); + + if (size == 0) { + ret = HAL_INVALID; + goto out; + } + + ce_running = 1; + if ((ret = HAL_MutexLock(&ce_lock, CE_WAIT_TIME)) != HAL_OK) + goto out; + HAL_CE_EnableCCMU(); + + while (1) { + + seed[0] = HAL_Ticks(); + seed[1] = HAL_Ticks(); + seed[2] = HAL_Ticks(); + seed[3] = HAL_Ticks(); + seed[4] = HAL_Ticks(); + seed[5] = HAL_Ticks(); + + CE_PRNG_Init(CE, 0); + CE_PRNG_Seed(CE, seed); + CE_REG_ALL(CE); + CE_PRNG_Generate(CE, rand); + cpsize = (size > 20) ? 20 : size; + memcpy(p, (uint8_t *)rand, cpsize); + if (size <= 20) + break; + p += cpsize; + size -= cpsize; + } + + CE_REG_ALL(CE); + CE_PRNG_Deinit(CE); + + HAL_CE_DisableCCMU(); + HAL_MutexUnlock(&ce_lock); + + out: + ce_running = 0; + CE_EXIT(ret); + return ret; + +} +/* +HAL_Status HAL_PRNG_Generate(uint32_t random[5]) +{ + HAL_Status ret = HAL_OK; + uint32_t seed[6]; + uint32_t tick; + + CE_ENTRY(); + + if ((ret = HAL_MutexLock(&ce_lock, CE_WAIT_TIME)) != HAL_OK) + goto out; + HAL_CE_EnableCCMU(); + + tick = HAL_Ticks(); + + seed[0] = tick; + seed[2] = seed[2] ^ HAL_Ticks(); + seed[4] = seed[0] ^ seed[1] ^ seed[2] ^ seed[3] ^ HAL_Ticks(); + seed[5] = seed[0] ^ seed[4] ^ seed[2] ^ seed[3] ^ HAL_Ticks(); + + CE_PRNG_Init(CE, 0); + CE_PRNG_Seed(CE, seed); + CE_REG_ALL(CE); + CE_PRNG_Generate(CE, random); + + CE_REG_ALL(CE); + CE_PRNG_Deinit(CE); + + HAL_CE_DisableCCMU(); + HAL_MutexUnlock(&ce_lock); + +out: + CE_EXIT(ret); + return ret; +} +*/ + + diff --git a/platform/mcu/xr871/src/driver/chip/hal_csi.c b/platform/mcu/xr871/src/driver/chip/hal_csi.c new file mode 100644 index 0000000000..ad140a41b1 --- /dev/null +++ b/platform/mcu/xr871/src/driver/chip/hal_csi.c @@ -0,0 +1,393 @@ +/** + * @file hal_csi.c + * @author XRADIO IOT WLAN Team + */ + +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "driver/chip/hal_csi.h" +#include "driver/chip/hal_gpio.h" +#include "driver/chip/hal_dma.h" +#include "hal_base.h" + +#define CSI_DEBUG 1 + +#define HAL_CSI_LOG(flags, fmt, arg...) \ + do { \ + if (flags) \ + printf(fmt, ##arg); \ + } while (0) + + +#define HAL_CSI_DBG(fmt, arg...) \ + HAL_CSI_LOG(CSI_DEBUG, "[HAL_CSI] "fmt, ##arg) + + +uint8_t csi_is_run = 0; + +void CSI_ModuleEnable() +{ + HAL_CCM_BusDisablePeriphClock(CCM_BUS_PERIPH_BIT_CSI); + HAL_CCM_BusForcePeriphReset(CCM_BUS_PERIPH_BIT_CSI); + HAL_CCM_BusReleasePeriphReset(CCM_BUS_PERIPH_BIT_CSI); + HAL_CCM_BusEnablePeriphClock(CCM_BUS_PERIPH_BIT_CSI); +} + +void CSI_ModuleDisable() +{ + HAL_CCM_BusDisablePeriphClock(CCM_BUS_PERIPH_BIT_CSI); +} + +void CSI_InputFormat() //raw +{ + HAL_CLR_BIT(CSI->CSI_CFG_REG , CSI_CFG_INPUT_FORMAT); +} + +void CSI_Irq_Enable() +{ + HAL_NVIC_SetPriority(CSI_IRQn, 0); + HAL_NVIC_EnableIRQ(CSI_IRQn); +} + +void CSI_Irq_Disable() +{ + HAL_NVIC_DisableIRQ(CSI_IRQn); +} + +CSI_Call_Back private_csi_cb; + + +void CSI_IRQHandler() +{ + if(private_csi_cb.callBack != NULL) + private_csi_cb.callBack(private_csi_cb.arg); +} + +/** + *@private + *@brief Initializes the CSI peripheral according to the specified parameters + * in the CSI_Config *param. + * @note This function is used to configure the source clock and pins of the CSI. + * The source clock freq is the mclk output freq. + * @param param: + * @arg param->src_Clk.clk used for select the source clock of CSI + * @arg param->src_Clk.divN * param->src_Clk.divM = The clock division + * @retval HAL_Status: The status of driver + */ +HAL_Status HAL_CSI_Config(CSI_Config *param) +{ + if (csi_is_run) { + HAL_WRN("%s, %d csi is busy\n", __func__, __LINE__); + return HAL_BUSY; + } + CSI_ModuleEnable(); + HAL_CCM_CSI_SetMClock(param->src_Clk.clk, param->src_Clk.divN, param->src_Clk.divM); + HAL_CCM_CSI_EnableMClock(); + + HAL_BoardIoctl(HAL_BIR_PINMUX_INIT, HAL_MKDEV(HAL_DEV_MAJOR_CSI, 0), 0); + csi_is_run = 1; + CSI_InputFormat(); + return HAL_OK; +} + +/** + * @brief Deinitializes all CSI peripherals registers and pins to their default reset + * values. + * @param None + * @retval None + */ +void HAL_CSI_DeInit(void) +{ + HAL_CLR_BIT(CSI->CSI_EN_REG, CSI_EN); + HAL_CCM_CSI_DisableMClock(); + CSI_ModuleDisable(); + HAL_BoardIoctl(HAL_BIR_PINMUX_DEINIT, HAL_MKDEV(HAL_DEV_MAJOR_CSI, 0), 0); + csi_is_run = 0; +} + +/** + * @brief Enables or disables the CSI interface. + * @param ctrl: Controls the enable of CSI moudle. + * @arg This parameter can be: CSI_ENABLE or CSI_DISABLE. + * @retval None + */ +void HAL_CSI_Moudle_Enalbe(CSI_CTRL ctrl) +{ + if (CSI_ENABLE == ctrl) + HAL_SET_BIT(CSI->CSI_EN_REG, CSI_EN); + else + HAL_CLR_BIT(CSI->CSI_EN_REG, CSI_EN); +} + +/** + * @brief Configure the CSI sync signal polarity. + * @param signal: Set the polarity for vsync, herf,p_Clk. + * @retval None + */ +void HAL_CSI_Sync_Signal_Polarity_Cfg(CSI_Sync_Signal *signal) +{ + uint32_t csi_sync_pol; + csi_sync_pol = signal->vsync + (signal->herf << 1) + (signal->p_Clk << 2); + HAL_CLR_BIT(CSI->CSI_CFG_REG, CSI_CFG_SYNC_SIGNAL_POL); + HAL_SET_BIT(CSI->CSI_CFG_REG, csi_sync_pol); +} + +/** + * @brief Enables or disables the CSI capture data. + * @param mode: Select the capture mode. + * @arg CSI_STILL_MODE: capture one picture. + * @arg CSI_VIDEO_MODE:Continuous capture. + * @param ctrl: Controls the enable of CSI capture. + * @arg This parameter can be: CSI_ENABLE or CSI_DISABLE. + * @retval None + */ +void HAL_CSI_Capture_Enable(CSI_CAPTURE_MODE mode , CSI_CTRL ctrl) +{ + HAL_CLR_BIT(CSI->CSI_CAP_REG, CSI_CAP_MODE); + + if (CSI_ENABLE == ctrl) { + if (mode == CSI_STILL_MODE) { + HAL_SET_BIT(CSI->CSI_CAP_REG, HAL_BIT(0)); + } else if (mode == CSI_VIDEO_MODE) + HAL_SET_BIT(CSI->CSI_CAP_REG, HAL_BIT(1)); + } +} + +/** + * @brief Configure the CSI interval capture. + * @note This function is used to Configure interlaced acquisition and interlaced pixels. + * @param vermask: the interlaced pixels value. + * @param hor_mask: the interlaced acquisition. + * @retval None + */ +void HAL_CSI_Interval_Capture_Cfg(uint8_t ver_mask, uint16_t hor_mask) +{ + HAL_CLR_BIT(CSI->CSI_SCALE_REG, CSI_VER_MASK); + HAL_CLR_BIT(CSI->CSI_SCALE_REG, CSI_HER_MASK); + + HAL_SET_BIT(CSI->CSI_SCALE_REG, ver_mask << 24); + HAL_SET_BIT(CSI->CSI_SCALE_REG, hor_mask); +} + +/** + * @brief Select the next FIFO for cache data. + * @param fifo_num: + * @arg CSI_FIFO_0_A: Select CSI_FIFO_0_A. + * @arg CSI_FIFO_0_B: Select CSI_FIFO_0_B. + * @retval None + */ +void HAL_CSI_Selection_Next_FIFO (CSI_FIFO fifo_num) +{ + HAL_CLR_BIT(CSI->CSI_BUF_CTL_REG ,HAL_BIT(2)); + HAL_SET_BIT(CSI->CSI_BUF_CTL_REG , fifo_num << 2); +} + +/** + * @brief Gets the currently used FIFO. + * @param None. + * @retval CSI_FIFO: The FIFO that currently used. + */ +CSI_FIFO HAL_CSI_Current_FIFO() +{ + return (HAL_GET_BIT(CSI->CSI_BUF_CTL_REG, HAL_BIT(1)) >> 1); +} + +/** + * @brief Enables or disables used double FIFO for cache the data. + * @note If you disable the double FIFO mode, the CSI_FIFO_A will be always + * used for cache data. + * @param ctrl: Controls the enable of double FIFO mode. + * @arg This parameter can be: CSI_ENABLE or CSI_DISABLE. + * @retval None. + */ +void HAL_CSI_Double_FIFO_Mode_Enable(CSI_CTRL ctrl) +{ + HAL_CLR_BIT(CSI->CSI_BUF_CTL_REG, HAL_BIT(0)); + + if (CSI_ENABLE == ctrl) + HAL_SET_BIT(CSI->CSI_BUF_CTL_REG, HAL_BIT(0)); +} + +/** + * @brief Gets the CSI moudle status. + * @param None. + * @retval CSI_Status: The status of CSI. + */ +CSI_Status HAL_CSI_Status() +{ + CSI_Status sta; + sta.luminance = HAL_GET_BIT(CSI->CSI_BUF_STA_REG, LUM_STATIS) >> 8; + sta.still_Mode = HAL_GET_BIT(CSI->CSI_BUF_STA_REG, HAL_BIT(1)) >> 1; + sta.video_Mode = HAL_GET_BIT(CSI->CSI_BUF_STA_REG, HAL_BIT(0)); + return sta; +} + +/** + * @brief Configure the interrupt flag for CSI. + * @note Set the interrupt source for CSI. + * @param irq_signel: The interrupt flag of CSI. + * @param ctrl: Controls the enable of interrupt flag. + * @arg This parameter can be: CSI_ENABLE or CSI_DISABLE. + * @retval None. + */ +void HAL_CSI_Interrupt_Cfg(CSI_INTERRUPT_SIGNAL irq_signel, CSI_CTRL ctrl) +{ + if (ctrl == CSI_ENABLE) + HAL_SET_BIT(CSI->CSI_INT_EN_REG, irq_signel); + else + HAL_CLR_BIT(CSI->CSI_INT_EN_REG, irq_signel); +} + +/** + * @brief Gets the CSI IRQ status. + * @param None. + * @return The status of CSI IRQ. + * @arg CSI_CAPTURE_DONE_IRQ: The picture capture done. + * @arg CSI_FRAME_DONE_IRQ: The ftame done. + * @arg CSI_FIFO_0_OVERFLOW_IRQ: The FIFO is overflow. + * When this flag appears, you need to restart CSI. + * @arg CSI_ALL_FIFO_OVERFLOW_IRQ: ALL FIFO is overflow. + * When this flag appears, you need to restart CSI. + * @arg CSI_VSYNC_IRQ: Capture a vsync signal. + * @arg CSI_FIFO_0_A_READY_IRQ: The CSI_FIFO_A is ready for read. + * @arg CSI_FIFO_0_B_READY_IRQ: The CSI_FIFO_B is ready for read. + */ +__IO uint32_t HAL_CSI_Interrupt_Sta() +{ + return CSI->CSI_INT_STA_REG; +} + +/** + * @brief Clear the CSI interrupt flag. + * @param None. + * @retval None. + */ +void HAL_CSI_Interrupt_Clear() +{ + HAL_SET_BIT(CSI->CSI_INT_STA_REG, CSI->CSI_INT_STA_REG); +} + +/** + * @brief Sets the size of the captured image. + * @param size: Set the start capture position and the number of pixels per row. + * @arg size->start: start capture position, unit byte.. + * @arg size->hor_len: the number of bytes per row. + * @retval HAL_Status: + * The status of driver. + */ +HAL_Status HAL_CSI_Set_Picture_Size(CSI_Picture_Size *size) +{ + if (size->hor_start > (HAL_BIT(14) - 1)) { + HAL_WRN("%s, %d csi Picture size error hor_start = %d\n", + __func__, __LINE__, size->hor_start); + return HAL_ERROR; + } + + if (size->hor_len > (HAL_BIT(14) - 1)) { + HAL_WRN("%s, %d csi Picture size error hor_len = %d\n", + __func__, __LINE__, size->hor_len); + return HAL_ERROR; + } + + HAL_CLR_BIT(CSI->CSI_HSIZE_REG, CSI_SIZE_REG); + + HAL_SET_BIT(CSI->CSI_HSIZE_REG, size->hor_len << 16); + HAL_SET_BIT(CSI->CSI_HSIZE_REG, size->hor_start); + return HAL_OK; +} + +/** + * @brief Cached data length of FIFO. + * @param None + * @retval CSI_FIFO_Data_Len: The data length of CSI_FIFO_A and CSI_FIFO_B. + * @arg FIFO_0_A_Data_Len: The data length of CSI_FIFO_A. + * @arg FIFO_0_B_Data_Len: The data length of CSI_FIFO_B. + */ +CSI_FIFO_Data_Len HAL_CSI_FIFO_Data_Len() +{ + CSI_FIFO_Data_Len len; + len.FIFO_0_B_Data_Len = HAL_GET_BIT(CSI->CSI_TRUE_DATA_NUM, (CSI_VALID_DATA_LEN << 16)) >> 16; + len.FIFO_0_A_Data_Len = HAL_GET_BIT(CSI->CSI_TRUE_DATA_NUM, CSI_VALID_DATA_LEN); + return len; +} + +/** + * @private + * @brief Enables or disables the CSI JPEG mode. + * @param ctrl: Controls the enable of CSI JPEG mode. + * @arg This parameter can be: CSI_ENABLE or CSI_DISABLE. + * @retval None + */ +void HAL_CIS_JPEG_Mode_Enable(CSI_CTRL ctrl) +{ + if (CSI_ENABLE == ctrl) + HAL_SET_BIT(CSI->CSI_JPEG_MOD_SEL, HAL_BIT(0)); + else + HAL_CLR_BIT(CSI->CSI_JPEG_MOD_SEL, HAL_BIT(0)); +} + +/** + * @brief Enables or disables the CSI interrupt. + * @param cb: The CSI interrupt callback function. + * @param ctrl: Controls the enable of CSI interrupt. + * @arg This parameter can be: CSI_ENABLE or CSI_DISABLE. + * @retval None + */ +void HAL_CSI_Interrupt_Enable(CSI_Call_Back *cb, CSI_CTRL ctrl) +{ + if (CSI_ENABLE == ctrl) { + if (cb == NULL) { + private_csi_cb.callBack = NULL; + private_csi_cb.arg = NULL; + } else + private_csi_cb = *cb; + CSI_Irq_Enable(); + } else { + private_csi_cb.callBack = NULL; + private_csi_cb.arg = NULL; + CSI_Irq_Disable(); + } +} + +void CSI_Printf() +{ + printf("CSI_EN_REG 0x%x 0x%x\n", (uint32_t)&CSI->CSI_EN_REG, CSI->CSI_EN_REG); + printf("CSI_CFG_REG 0x%x 0x%x\n", (uint32_t)&CSI->CSI_CFG_REG, CSI->CSI_CFG_REG); + printf("CSI_CAP_REG 0x%x 0x%x\n", (uint32_t)&CSI->CSI_CAP_REG, CSI->CSI_CAP_REG); + printf("CSI_SCALE_REG 0x%x 0x%x\n", (uint32_t)&CSI->CSI_SCALE_REG, CSI->CSI_SCALE_REG); + printf("CSI_BUF_CTL_REG 0x%x 0x%x\n", (uint32_t)&CSI->CSI_BUF_CTL_REG, CSI->CSI_BUF_CTL_REG); + printf("CSI_BUF_STA_REG 0x%x 0x%x\n", (uint32_t)&CSI->CSI_BUF_STA_REG, CSI->CSI_BUF_STA_REG); + printf("CSI_INT_EN_REG 0x%x 0x%x\n", (uint32_t)&CSI->CSI_INT_EN_REG, CSI->CSI_INT_EN_REG); + printf("CSI_INT_STA_REG 0x%x 0x%x\n", (uint32_t)&CSI->CSI_INT_STA_REG, CSI->CSI_INT_STA_REG); + printf("CSI_HSIZE_REG 0x%x 0x%x\n", (uint32_t)&CSI->CSI_HSIZE_REG, CSI->CSI_HSIZE_REG); + printf("CSI_BF_LEN_REG 0x%x 0x%x\n", (uint32_t)&CSI->CSI_BF_LEN_REG, CSI->CSI_BF_LEN_REG); + printf("CSI_TRUE_DATA_NUM 0x%x 0x%x\n", (uint32_t)&CSI->CSI_TRUE_DATA_NUM, CSI->CSI_TRUE_DATA_NUM); + printf("CSI_JPEG_MOD_SEL 0x%x 0x%x\n", (uint32_t)&CSI->CSI_JPEG_MOD_SEL, CSI->CSI_JPEG_MOD_SEL); +} diff --git a/platform/mcu/xr871/src/driver/chip/hal_debug.h b/platform/mcu/xr871/src/driver/chip/hal_debug.h new file mode 100644 index 0000000000..d0da8c6918 --- /dev/null +++ b/platform/mcu/xr871/src/driver/chip/hal_debug.h @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _DRIVER_CHIP_HAL_DEBUG_H_ +#define _DRIVER_CHIP_HAL_DEBUG_H_ + +#include +#include "sys/xr_util.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* debug */ +#define HAL_SYSLOG printf + +#define HAL_DBG_ON 1 +#define HAL_WRN_ON 1 +#define HAL_ERR_ON 1 +#define HAL_ABORT_ON 0 + +#define HAL_DBG_TIMER 0 +#define HAL_DBG_WDG 0 +#define HAL_DBG_MBOX 0 +#define HAL_DBG_I2C 0 + +#define HAL_ABORT() sys_abort() + +#define HAL_LOG(flags, fmt, arg...) \ + do { \ + if (flags) \ + HAL_SYSLOG(fmt, ##arg); \ + } while (0) + + +#define HAL_DBG(fmt, arg...) HAL_LOG(HAL_DBG_ON, "[HAL] "fmt, ##arg) + +#define HAL_TIMER_DBG(fmt, arg...) \ + HAL_LOG(HAL_DBG_ON && HAL_DBG_TIMER, "[HAL TIMER] "fmt, ##arg) + +#define HAL_WDG_DBG(fmt, arg...) \ + HAL_LOG(HAL_DBG_ON && HAL_DBG_WDG, "[HAL WDG] "fmt, ##arg) + +#define HAL_MBOX_DBG(fmt, arg...) \ + HAL_LOG(HAL_DBG_ON && HAL_DBG_MBOX, "[HAL MBOX] "fmt, ##arg) + +#define HAL_I2C_DBG(fmt, arg...) \ + HAL_LOG(HAL_DBG_ON && HAL_DBG_I2C, "[HAL I2C] "fmt, ##arg) + +#define HAL_WRN(fmt, arg...) HAL_LOG(HAL_WRN_ON, "[HAL WRN] "fmt, ##arg) + +#define HAL_ERR(fmt, arg...) \ + do { \ + HAL_LOG(HAL_ERR_ON, "[HAL ERR] %s():%d, "fmt, \ + __func__, __LINE__, ##arg); \ + if (HAL_ABORT_ON) \ + HAL_ABORT(); \ + } while (0) + +#define HAL_ASSERT_PARAM(exp) \ + do { \ + if (!(exp)) { \ + printf("Invalid param at %s:%d", __func__, __LINE__); \ + } \ + } while (0) + +#ifdef __cplusplus +} +#endif + +#endif /* _DRIVER_CHIP_HAL_DEBUG_H_ */ diff --git a/platform/mcu/xr871/src/driver/chip/hal_dma.c b/platform/mcu/xr871/src/driver/chip/hal_dma.c new file mode 100644 index 0000000000..c95af32fa8 --- /dev/null +++ b/platform/mcu/xr871/src/driver/chip/hal_dma.c @@ -0,0 +1,328 @@ +/** + * @file hal_dma.c + * @author XRADIO IOT WLAN Team + */ + +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "driver/chip/hal_dma.h" +#include "hal_base.h" + +#define DMA_IRQ_BITS(chan, type) ((uint32_t)type << ((uint32_t)chan << 1)) +#define DMA_IRQ_ALL_BITS ((1 << (DMA_CHANNEL_NUM << 1)) - 1) + +typedef struct { + DMA_IRQCallback endCallback; + void *endArg; +#if HAL_DMA_TRANSFER_HALF_IRQ_SUPPORT + DMA_IRQCallback halfCallback; + void *halfArg; +#endif +} DMA_Private; + +static DMA_Private gDMAPrivate[DMA_CHANNEL_NUM]; + +static uint8_t gDMAChannelUsed = 0; + +void DMA_IRQHandler(void) +{ + uint32_t i; + uint32_t irqStatus; +#if HAL_DMA_TRANSFER_HALF_IRQ_SUPPORT + uint32_t isHalfPending; +#endif + uint32_t isEndPending; + DMA_Private *priv; + + irqStatus = DMA->IRQ_STATUS & DMA->IRQ_EN & DMA_IRQ_ALL_BITS; /* get pending bits */ + DMA->IRQ_STATUS = irqStatus; /* clear pending bits */ + priv = gDMAPrivate; + + for (i = DMA_CHANNEL_0; i < DMA_CHANNEL_NUM && irqStatus != 0; ++i) { +#if HAL_DMA_TRANSFER_HALF_IRQ_SUPPORT + isHalfPending = irqStatus & HAL_BIT(0); + if (isHalfPending && priv[i].halfCallback) { + priv[i].halfCallback(priv[i].halfArg); + } +#endif + isEndPending = irqStatus & HAL_BIT(1); + if (isEndPending && priv[i].endCallback) { + priv[i].endCallback(priv[i].endArg); + } + irqStatus >>= 2; + } +} + +void DMA_EnableIRQ(DMA_Channel chan, DMA_IRQType type) +{ + HAL_SET_BIT(DMA->IRQ_EN, DMA_IRQ_BITS(chan, type)); +} + +void DMA_DisableIRQ(DMA_Channel chan, DMA_IRQType type) +{ + HAL_CLR_BIT(DMA->IRQ_EN, DMA_IRQ_BITS(chan, type)); +} + +int DMA_IsPendingIRQ(DMA_Channel chan, DMA_IRQType type) +{ + return HAL_GET_BIT(DMA->IRQ_STATUS, DMA_IRQ_BITS(chan, type)); +} + +void DMA_ClearPendingIRQ(DMA_Channel chan, DMA_IRQType type) +{ + HAL_SET_BIT(DMA->IRQ_STATUS, DMA_IRQ_BITS(chan, type)); +} + +__STATIC_INLINE void DMA_DisableAllIRQ(DMA_Channel chan) +{ + HAL_CLR_BIT(DMA->IRQ_EN, DMA_IRQ_BITS(chan, DMA_IRQ_TYPE_VMASK)); +} + +__STATIC_INLINE void DMA_ClearAllPendingIRQ(DMA_Channel chan) +{ + uint32_t irqStatus = HAL_GET_BIT(DMA->IRQ_STATUS, + DMA_IRQ_BITS(chan, DMA_IRQ_TYPE_VMASK)); + if (irqStatus) { + DMA->IRQ_STATUS = irqStatus; + } +} + +static void DMA_Attach(void) +{ + HAL_CCM_BusEnablePeriphClock(CCM_BUS_PERIPH_BIT_DMA); + HAL_CCM_BusReleasePeriphReset(CCM_BUS_PERIPH_BIT_DMA); +} + +static void DMA_Detach(void) +{ + HAL_CCM_BusForcePeriphReset(CCM_BUS_PERIPH_BIT_DMA); + HAL_CCM_BusDisablePeriphClock(CCM_BUS_PERIPH_BIT_DMA); +} + +/** + * @brief Request an available DMA channel + * @note Before using DMA, an available DMA channel MUST be requested first + * @retval DMA_Channel, DMA_CHANNEL_INVALID on failed + */ +DMA_Channel HAL_DMA_Request(void) +{ + DMA_Channel i; + DMA_Channel chan = DMA_CHANNEL_INVALID; + unsigned long flags; + + flags = HAL_EnterCriticalSection(); + + if (gDMAChannelUsed == 0) { + DMA_Attach(); + } + + for (i = DMA_CHANNEL_0; i < DMA_CHANNEL_NUM; ++i) { + if ((gDMAChannelUsed & HAL_BIT(i)) == 0) { + HAL_SET_BIT(gDMAChannelUsed, HAL_BIT(i)); + chan = i; + break; + } + } + + HAL_ExitCriticalSection(flags); + + return chan; +} + +/** + * @brief Release the DMA channel + * @param[in] chan DMA channel to be released + * @return None + */ +void HAL_DMA_Release(DMA_Channel chan) +{ + unsigned long flags; + + flags = HAL_EnterCriticalSection(); + + HAL_CLR_BIT(gDMAChannelUsed, HAL_BIT(chan)); + + if (gDMAChannelUsed == 0) { + DMA_Detach(); + } + + HAL_ExitCriticalSection(flags); +} + +/** + * @brief Initialize the DMA channel according to the specified parameters + * @param[in] chan DMA channel + * @param[in] param Pointer to DMA_ChannelInitParam structure + * @return None + */ +void HAL_DMA_Init(DMA_Channel chan, const DMA_ChannelInitParam *param) +{ + DMA_Private *priv; + unsigned long flags; + + HAL_ASSERT_PARAM(chan < DMA_CHANNEL_NUM); + + flags = HAL_EnterCriticalSection(); + + priv = &gDMAPrivate[chan]; + + /* set configuration */ + DMA->CHANNEL[chan].CTRL = param->cfg; /* NB: it will stop/reset the DMA channel */ + + /* set callback */ + if (param->irqType & DMA_IRQ_TYPE_END) { + priv->endCallback = param->endCallback; + priv->endArg = param->endArg; + } else { + priv->endCallback = NULL; + priv->endArg = NULL; + } +#if HAL_DMA_TRANSFER_HALF_IRQ_SUPPORT + if (param->irqType & DMA_IRQ_TYPE_HALF) { + priv->halfCallback = param->halfCallback; + priv->halfArg = param->halfArg; + } else { + priv->halfCallback = NULL; + priv->halfArg = NULL; + } +#endif + + /* clear pending bits */ +#if 0 + if (DMA_IsPendingIRQ(chan, param->irqType)) { + DMA_ClearPendingIRQ(chan, param->irqType); + } +#else + DMA_ClearAllPendingIRQ(chan); +#endif + + /* enable IRQ */ + if ((DMA->IRQ_EN & DMA_IRQ_ALL_BITS) == 0) { + HAL_NVIC_SetPriority(DMA_IRQn, NVIC_PERIPHERAL_PRIORITY_DEFAULT); + HAL_NVIC_EnableIRQ(DMA_IRQn); + } + DMA_EnableIRQ(chan, param->irqType); + + HAL_ExitCriticalSection(flags); +} + +/** + * @brief DeInitialize the specified DMA channel + * @param[in] chan DMA channel + * @return None + */ +void HAL_DMA_DeInit(DMA_Channel chan) +{ + DMA_Private *priv; + unsigned long flags; + + HAL_ASSERT_PARAM(chan < DMA_CHANNEL_NUM); + + flags = HAL_EnterCriticalSection(); + + priv = &gDMAPrivate[chan]; + + DMA_DisableAllIRQ(chan); + if ((DMA->IRQ_EN & DMA_IRQ_ALL_BITS) == 0) { + HAL_NVIC_DisableIRQ(DMA_IRQn); + } + DMA_ClearAllPendingIRQ(chan); + + priv->endCallback = NULL; + priv->endArg = NULL; +#if HAL_DMA_TRANSFER_HALF_IRQ_SUPPORT + priv->halfCallback = NULL; + priv->halfArg = NULL; +#endif + + HAL_ExitCriticalSection(flags); +} + +/** + * @brief Start the DMA transfer of the specified DMA channel + * @param[in] chan DMA channel + * @param[in] srcAddr The source address of DMA transfer + * @param[in] dstAddr The destination address of DMA transfer + * @param[in] datalen The length of data to be transferred from source to destination + * @return None + * + * @note The source/destination address MUST be aligned to the + * source/destination DMA transaction data width defined by DMA_DataWidth. + * @note The date length MUST not be more than DMA_DATA_MAX_LEN. + */ +void HAL_DMA_Start(DMA_Channel chan, uint32_t srcAddr, uint32_t dstAddr, uint32_t datalen) +{ + HAL_ASSERT_PARAM(chan < DMA_CHANNEL_NUM); + HAL_ASSERT_PARAM(datalen <= DMA_DATA_MAX_LEN); + + /* TODO: check alignment of @srcAddr and @dstAddr */ + + DMA->CHANNEL[chan].SRC_ADDR = srcAddr; + DMA->CHANNEL[chan].DST_ADDR = dstAddr; + DMA->CHANNEL[chan].BYTE_CNT = datalen & DMA_BYTE_CNT_VMASK; + HAL_SET_BIT(DMA->CHANNEL[chan].CTRL, DMA_START_BIT); +} + +/** + * @brief Stop the DMA transfer of the specified DMA channel + * @param[in] chan DMA channel + * @return None + */ +void HAL_DMA_Stop(DMA_Channel chan) +{ + HAL_ASSERT_PARAM(chan < DMA_CHANNEL_NUM); + HAL_CLR_BIT(DMA->CHANNEL[chan].CTRL, DMA_START_BIT); /* NB: it will reset the channel */ +} + +/** + * @brief Check the busy status of the specified DMA channel + * @param[in] chan DMA channel + * @return 1 on busy, 0 on idle + */ +int HAL_DMA_IsBusy(DMA_Channel chan) +{ + HAL_ASSERT_PARAM(chan < DMA_CHANNEL_NUM); + return HAL_GET_BIT(DMA->CHANNEL[chan].CTRL, DMA_BUSY_BIT); +} + +/** + * @brief Get the byte counter of the specified DMA channel + * @param[in] chan DMA channel + * @return The byte counter of the specified DMA channel + * - If DMA byte count mode is DMA_BYTE_CNT_MODE_NORMAL, the return value + * is the data length set by HAL_DMA_Start(). + * - If DMA byte count mode is DMA_BYTE_CNT_MODE_REMAIN, the return value + * is the length of the remaining data not transferred. + */ +uint32_t HAL_DMA_GetByteCount(DMA_Channel chan) +{ + HAL_ASSERT_PARAM(chan < DMA_CHANNEL_NUM); + return (DMA->CHANNEL[chan].BYTE_CNT & DMA_BYTE_CNT_VMASK); +} diff --git a/platform/mcu/xr871/src/driver/chip/hal_dmic.c b/platform/mcu/xr871/src/driver/chip/hal_dmic.c new file mode 100644 index 0000000000..240d65755f --- /dev/null +++ b/platform/mcu/xr871/src/driver/chip/hal_dmic.c @@ -0,0 +1,700 @@ +/** + * @file hal_dmic.c + * @author XRADIO IOT WLAN Team + */ + +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "driver/chip/hal_dma.h" +#include "driver/chip/hal_dmic.h" +#include "sys/xr_debug.h" +#include "hal_base.h" +#include "sys/io.h" +#include "pm/pm.h" + +#define DMIC_DBG_ON 0 + +#if (DMIC_DBG_ON == 1) +#define DMIC_DEBUG(fmt, arg...) HAL_LOG(DMIC_DBG_ON, "[DMIC] "fmt, ##arg) +#else +#define DMIC_DEBUG(fmt, arg...) +#endif +#define DMIC_ERROR(fmt, arg...) HAL_LOG(1, "[DMIC] "fmt, ##arg) + +typedef struct { + bool isHwInit; + bool isInitiate; + bool isSemaphore; + volatile bool isRunning; + DMA_Channel DMAChan; + DMIC_HWParam *hwParam; + DMIC_DataParam dataParam; + uint8_t *usrBuf; + uint32_t length; + HAL_Semaphore rxReady; + volatile uint32_t halfCounter; + volatile uint32_t endCounter; + uint8_t *lastReadPointer; + uint8_t *dmaPointer; + HAL_Mutex devTriggerLock; + uint32_t audioPllParam; + uint32_t audioPllPatParam; +} DMIC_Private; + +typedef enum { + DMIC_PLL_24M = 0U, + DMIC_PLL_22M = 1U, +} DMIC_PLLMode; + +typedef struct { + uint32_t hosc; + uint32_t audio; + uint32_t pllParam; + uint32_t pllPatParam; +} HOSC_DMIC_Type; + +#define DMIC_MEMCPY memcpy +#define DMIC_MALLOC malloc +#define DMIC_FREE free +#define DMIC_MEMSET memset + +#define DMIC_OVERRUN_THRESHOLD 3 + +#ifdef RESERVERD_MEMORY_FOR_DMIC +static uint8_t DMIC_BUF[DMIC_BUF_LENGTH]; +#endif +DMIC_Private gDMICPrivate; +static uint32_t DMIC_BUF_LENGTH = 0; + +/* default hardware configuration */ +static DMIC_HWParam gHwParam = { + DMIC_CTLR_DMICFDT_5MS, + DMIC_FIFOCR_MODE1, + 0x40, + 0xB0, + 1, +}; + +static const HOSC_DMIC_Type dmic_hosc_aud_type[] = { + {HOSC_CLOCK_26M, DMIC_PLL_24M, PRCM_AUD_PLL24M_PARAM_HOSC26M, PRCM_AUD_PLL24M_PAT_PARAM_HOSC26M}, + {HOSC_CLOCK_26M, DMIC_PLL_22M, PRCM_AUD_PLL22M_PARAM_HOSC26M, PRCM_AUD_PLL22M_PAT_PARAM_HOSC26M}, + {HOSC_CLOCK_24M, DMIC_PLL_24M, PRCM_AUD_PLL24M_PARAM_HOSC24M, PRCM_AUD_PLL24M_PAT_PARAM_HOSC24M}, + {HOSC_CLOCK_24M, DMIC_PLL_22M, PRCM_AUD_PLL22M_PARAM_HOSC24M, PRCM_AUD_PLL22M_PAT_PARAM_HOSC24M}, + {HOSC_CLOCK_40M, DMIC_PLL_24M, PRCM_AUD_PLL24M_PARAM_HOSC40M, PRCM_AUD_PLL24M_PAT_PARAM_HOSC40M}, + {HOSC_CLOCK_40M, DMIC_PLL_22M, PRCM_AUD_PLL22M_PARAM_HOSC40M, PRCM_AUD_PLL22M_PAT_PARAM_HOSC40M}, + {HOSC_CLOCK_52M, DMIC_PLL_24M, PRCM_AUD_PLL24M_PARAM_HOSC52M, PRCM_AUD_PLL24M_PAT_PARAM_HOSC52M}, + {HOSC_CLOCK_52M, DMIC_PLL_22M, PRCM_AUD_PLL22M_PARAM_HOSC52M, PRCM_AUD_PLL22M_PAT_PARAM_HOSC52M}, +}; + +static uint32_t DMIC_PLLAUDIO_Update(DMIC_PLLMode pll) +{ + DMIC_Private *dmicPrivate = &gDMICPrivate; + + if (pll != DMIC_PLL_24M && pll != DMIC_PLL_22M) + return -1; + + uint32_t hoscClock = HAL_PRCM_GetHFClock(); + + int i = 0; + for (i = 0; i < ARRAY_SIZE(dmic_hosc_aud_type); i++) { + if ((dmic_hosc_aud_type[i].hosc == hoscClock) && (dmic_hosc_aud_type[i].audio == pll)) { + dmicPrivate->audioPllParam = dmic_hosc_aud_type[i].pllParam; + dmicPrivate->audioPllPatParam = dmic_hosc_aud_type[i].pllPatParam; + break; + } + } + if (i == ARRAY_SIZE(dmic_hosc_aud_type)) { + DMIC_ERROR("Update audio pll failed....\n"); + return -1; + } + return 0; +} + +static void HAL_DMIC_Trigger(bool enable); + +static int DMIC_DMA_BUFFER_CHECK_Threshold() +{ + DMIC_Private *dmicPrivate = &gDMICPrivate; + if (dmicPrivate->halfCounter >= DMIC_OVERRUN_THRESHOLD || + dmicPrivate->endCounter >= DMIC_OVERRUN_THRESHOLD) { + HAL_DMIC_Trigger(0); + dmicPrivate->isRunning = 0; + dmicPrivate->halfCounter = 0; + dmicPrivate->endCounter = 0; + dmicPrivate->lastReadPointer = dmicPrivate->usrBuf; + DMIC_ERROR("Rx : overrun and stop dma rx....\n"); + return -1; + } + return 0; +} + +/** + * @internal + * @brief DMA DMIC receive process half complete callback + * @param arg: pointer to a HAL_Semaphore structure that contains + * semaphore. + * @retval None + */ +static void DMIC_DMAHalfCallback(void *arg) +{ + DMIC_Private *dmicPrivate = &gDMICPrivate; + dmicPrivate->halfCounter ++; + if (DMIC_DMA_BUFFER_CHECK_Threshold() != 0) + return; + if (dmicPrivate->isSemaphore == true) { + dmicPrivate->isSemaphore = false; + HAL_SemaphoreRelease((HAL_Semaphore *)arg); + } +} + +/** + * @internal + * @brief DMA DMIC receive process complete callback + * @param arg: pointer to a HAL_Semaphore structure + * @retval None + */ +static void DMIC_DMAEndCallback(void *arg) +{ + DMIC_Private *dmicPrivate = &gDMICPrivate; + dmicPrivate->endCounter ++; + if (DMIC_DMA_BUFFER_CHECK_Threshold() != 0) + return; + if (dmicPrivate->isSemaphore == true) { + dmicPrivate->isSemaphore = false; + HAL_SemaphoreRelease((HAL_Semaphore *)arg); + } +} + +/** + * @internal + * @brief Start the DMA Transfer + * @param chan: the specified DMA Channel. + * @param srcAddr: The source memory Buffer address. + * @param dstAddr: The destination memory Buffer address. + * @param datalen: The length of data to be transferred from source to destination. + * @retval None + */ +static void DMIC_DMAStart(DMA_Channel chan, uint32_t srcAddr, uint32_t dstAddr, uint32_t datalen) +{ + HAL_DMA_Start(chan, srcAddr, dstAddr, datalen); +} + +/** + * @internal + * @brief Stop DMA transfer. + * @param chan: the specified DMA Channel.. + * @retval None + */ +static void DMIC_DMAStop(DMA_Channel chan) +{ + HAL_DMA_Stop(chan); +} + +/** + * @internal + * @brief Sets the DMA Transfer parameter. + * @param channel: the specified DMA Channel. + * @param dir: Data transfer direction + * @retval none + */ +static void DMIC_DMASet(DMA_Channel channel) +{ + DMIC_Private *dmicPrivate = &gDMICPrivate; + DMA_ChannelInitParam dmaParam; + HAL_Memset(&dmaParam, 0, sizeof(dmaParam)); + dmaParam.cfg = HAL_DMA_MakeChannelInitCfg(DMA_WORK_MODE_CIRCULAR, + DMA_WAIT_CYCLE_2, + DMA_BYTE_CNT_MODE_NORMAL, + DMA_DATA_WIDTH_16BIT, + DMA_BURST_LEN_1, + DMA_ADDR_MODE_INC, + DMA_PERIPH_SRAM, + DMA_DATA_WIDTH_16BIT, + DMA_BURST_LEN_1, + DMA_ADDR_MODE_FIXED, + DMA_PERIPH_DMIC); + + dmaParam.irqType = DMA_IRQ_TYPE_BOTH; + dmaParam.endCallback = DMIC_DMAEndCallback; + dmaParam.halfCallback = DMIC_DMAHalfCallback; + dmaParam.endArg = &(dmicPrivate->rxReady); + dmaParam.halfArg = &(dmicPrivate->rxReady); + + HAL_DMA_Init(channel, &dmaParam); +} + +/** + * @internal + * @brief enbale/disable DMIC controller. + * @param enable: enable or disable. + * @retval None + */ +static void HAL_DMIC_Trigger(bool enable) +{ + DMIC_Private *dmicPrivate = &gDMICPrivate; + if (enable) { + HAL_SET_BIT(DMIC->FIFO_CTR, DMIC_FIFOCR_FIFO_FLUSH_BIT); + + if (dmicPrivate->dataParam.channels > 1) { + HAL_SET_BIT(DMIC->DMIC_EN, DMIC_ECR_DATA0_CHL_EN_BIT|DMIC_ECR_DATA0_CHR_EN_BIT); + } else { + HAL_SET_BIT(DMIC->DMIC_EN, DMIC_ECR_DATA0_CHL_EN_BIT); + } + HAL_SET_BIT(DMIC->DMIC_INTC, DMIC_ICR_FIFO_DRQ_EN_BIT); + DMIC_DMAStart(dmicPrivate->DMAChan, (uint32_t)&(DMIC->DMIC_DATA), + (uint32_t)dmicPrivate->usrBuf, dmicPrivate->length); + } else { + DMIC_DMAStop(dmicPrivate->DMAChan); + HAL_CLR_BIT(DMIC->DMIC_INTC, DMIC_ICR_FIFO_DRQ_EN_BIT); + HAL_CLR_BIT(DMIC->DMIC_EN, DMIC_ECR_DATA0_CHL_EN_BIT|DMIC_ECR_DATA0_CHR_EN_BIT); + } +} + +/** + * @brief Receive an amount of data with DMA + * @param buf: pointer to the Receive data buffer. + * @param Size: length of buf to be Receive: + * @retval length of receive data + */ +int32_t HAL_DMIC_Read_DMA(uint8_t *buf, uint32_t size) +{ + if (!buf || size < DMIC_BUF_LENGTH/2) + return HAL_INVALID; + + DMIC_Private *dmicPrivate = &gDMICPrivate; + uint8_t *readPointer = NULL; + uint32_t toRead = DMIC_BUF_LENGTH/2; + uint32_t readSize = 0; + uint8_t *writeBuf = buf; + uint8_t err_flag; /* temp solution to avoid outputing debug message when irq disabled */ + + while (size) { + if (size < toRead) + break; + if (!dmicPrivate->isRunning) { + dmicPrivate->lastReadPointer = dmicPrivate->usrBuf; + DMIC_DEBUG("Rx: record start...\n"); + dmicPrivate->isRunning = 1; + HAL_DMIC_Trigger(1); + } else { + err_flag = 0; + readPointer = dmicPrivate->lastReadPointer; + HAL_DisableIRQ(); + if (dmicPrivate->halfCounter && dmicPrivate->endCounter) { + dmicPrivate->halfCounter = 0; + dmicPrivate->endCounter = 0; + if (dmicPrivate->dmaPointer == dmicPrivate->usrBuf) { + readPointer = dmicPrivate->usrBuf + DMIC_BUF_LENGTH/2; + } else { + readPointer = dmicPrivate->usrBuf; + } + err_flag = 1; + } else if (dmicPrivate->halfCounter ) { + dmicPrivate->halfCounter --; + + } else if (dmicPrivate->endCounter) { + dmicPrivate->endCounter --; + } else { + dmicPrivate->isSemaphore = true; + HAL_EnableIRQ(); + HAL_SemaphoreWait(&dmicPrivate->rxReady, HAL_WAIT_FOREVER); + HAL_DisableIRQ(); + if (dmicPrivate->halfCounter) + dmicPrivate->halfCounter--; + if (dmicPrivate->endCounter) + dmicPrivate->endCounter--; + } + DMIC_MEMCPY(writeBuf, readPointer, toRead); + HAL_EnableIRQ(); + if (err_flag) { + DMIC_ERROR("overrun...\n"); + } + writeBuf += toRead; + size -= toRead; + readSize += toRead; + dmicPrivate->lastReadPointer = readPointer + toRead; + if (dmicPrivate->lastReadPointer >= dmicPrivate->usrBuf + DMIC_BUF_LENGTH) + dmicPrivate->lastReadPointer = dmicPrivate->usrBuf; + } + } + return readSize; +} + +/** + * @internal + * @brief DMIC peripheral Init + * @param param: pointer to a DMIC_HWParam structure that contains + * some hardware configuration information + * @retval HAL status + */ +static inline HAL_Status DMIC_HwInit(DMIC_HWParam *param) +{ + if (!param) + return HAL_INVALID; + /* Set delay time */ + HAL_MODIFY_REG(DMIC->DMIC_CTR, DMIC_CTLR_DMICFDT_MASK,param->delayTime); + /*Set fifo mode*/ + HAL_MODIFY_REG(DMIC->FIFO_CTR, DMIC_FIFOCR_MODE_MASK|DMIC_FIFOCR_TRG_LEVEL_MASK, + param->fifoMode|DMIC_FIFOCR_TRG_LEVEL(param->triggerLevel)); + /* Set volume gain*/ + HAL_MODIFY_REG(DMIC->DATA0_DATA1_VOL_CTR, DMIC_DATA0L_VOL_MASK|DMIC_DATA0R_VOL_MASK, + DMIC_DATA0L_VOL(param->volumeGain)|DMIC_DATA0R_VOL(param->volumeGain)); + if (param->hpfEnable) { + /* Set HPF*/ + HAL_SET_BIT(DMIC->HPF_EN_CTR, DMIC_HPF_DATA0_CHL_EN_BIT|DMIC_HPF_DATA0_CHR_EN_BIT); + } + /* Global enable */ + HAL_SET_BIT(DMIC->DMIC_EN, DMIC_ECR_GLOBE_EN); + return HAL_OK; +} + +/** + * @internal + * @brief DeInitializes the DMIC peripheral + * @param param: pointer to a DMIC_HWParam structure that contains + * some hardware configuration information. + * @retval HAL status + */ +static inline HAL_Status DMIC_HwDeInit(DMIC_HWParam *param) +{ + if (!param) + return HAL_INVALID; + if (param->hpfEnable) { + /* close HPF*/ + HAL_CLR_BIT(DMIC->HPF_EN_CTR, DMIC_HPF_DATA0_CHL_EN_BIT|DMIC_HPF_DATA0_CHR_EN_BIT); + } + /* Global disable */ + HAL_CLR_BIT(DMIC->DMIC_EN, DMIC_ECR_GLOBE_EN); + return HAL_OK; +} + +/** + * @brief Open the DMIC module according to the specified parameters + * in the DMIC_DataParam. + * @param param: pointer to a DMIC_DataParam structure that contains + * the data format information + * @retval HAL status + */ +HAL_Status HAL_DMIC_Open(DMIC_DataParam *param) +{ + DMIC_DEBUG("Dmic open.\n"); + + if (!param) + return HAL_INVALID; + DMIC_Private *dmicPrivate = &gDMICPrivate; + + if (dmicPrivate->isInitiate == true) { + DMIC_ERROR("DMIC opened already,faild...\n"); + return HAL_INVALID; + } + dmicPrivate->isInitiate = true; + + DMIC_MEMCPY(&(dmicPrivate->dataParam), param, sizeof(*param)); + + DMIC_BUF_LENGTH = param->bufSize; + dmicPrivate->DMAChan = DMA_CHANNEL_INVALID; + dmicPrivate->length = DMIC_BUF_LENGTH; +#ifdef RESERVERD_MEMORY_FOR_DMIC + dmicPrivate->usrBuf = DMIC_BUF; +#else + dmicPrivate->usrBuf = DMIC_MALLOC(DMIC_BUF_LENGTH); + if(dmicPrivate->usrBuf) + DMIC_MEMSET(dmicPrivate->usrBuf,0,DMIC_BUF_LENGTH); + else { + DMIC_ERROR("Malloc buf(for DMA),faild...\n"); + return HAL_ERROR; + } +#endif + /*request DMA channel*/ + dmicPrivate->DMAChan = HAL_DMA_Request(); + if (dmicPrivate->DMAChan == DMA_CHANNEL_INVALID) { + DMIC_ERROR("Request DMA channel(for DMIC),faild...\n"); + DMIC_FREE(dmicPrivate->usrBuf); + return HAL_ERROR; + } + HAL_SemaphoreInitBinary(&dmicPrivate->rxReady); + + HAL_MODIFY_REG(DMIC->CH_NUM, DMIC_CH_NUM_MASK,(param->channels - 1)); + + /* Enable chl chr(data0) */ + if (param->channels > 1) { + HAL_SET_BIT(DMIC->DMIC_EN, DMIC_ECR_DATA0_CHL_EN_BIT|DMIC_ECR_DATA0_CHR_EN_BIT); + + HAL_MODIFY_REG(DMIC->CH_MAP, DMIC_CMR_CH0_MAP_MASK(0)| DMIC_CMR_CH0_MAP_MASK(1), + DMIC_CMR_CH0_MAP(0)|DMIC_CMR_CH0_MAP(1)); + } else { + HAL_SET_BIT(DMIC->DMIC_EN, DMIC_ECR_DATA0_CHL_EN_BIT); + HAL_MODIFY_REG(DMIC->CH_MAP, DMIC_CMR_CH0_MAP_MASK(0),DMIC_CMR_CH0_MAP(0)); + } + + switch (param->sampleRate) { + case DMIC_SR48KHZ: + case DMIC_SR24KHZ: + case DMIC_SR12KHZ: + case DMIC_SR32KHZ: + case DMIC_SR16KHZ: + case DMIC_SR8KHZ: + DMIC_PLLAUDIO_Update(DMIC_PLL_24M); + HAL_PRCM_SetAudioPLLParam(dmicPrivate->audioPllParam); + HAL_PRCM_SetAudioPLLPatternParam(dmicPrivate->audioPllPatParam); + break; + case DMIC_SR44KHZ: + case DMIC_SR22KHZ: + case DMIC_SR11KHZ: + DMIC_PLLAUDIO_Update(DMIC_PLL_22M); + HAL_PRCM_SetAudioPLLParam(dmicPrivate->audioPllParam); + HAL_PRCM_SetAudioPLLPatternParam(dmicPrivate->audioPllPatParam); + break; + default: + DMIC_ERROR("Invalued Samplerate...\n"); + + } + + /* Set DMIC oversample rate*/ + switch (param->sampleRate) { + case DMIC_SR48KHZ: + case DMIC_SR24KHZ: + case DMIC_SR44KHZ: + case DMIC_SR22KHZ: + case DMIC_SR32KHZ: + HAL_MODIFY_REG(DMIC->DMIC_CTR, DMIC_CTLR_OVERSAMPLE_RATE_MASK, + DMIC_CTLR_OVERSAMPLE_RATE64); + break; + case DMIC_SR16KHZ: + case DMIC_SR12KHZ: + case DMIC_SR8KHZ: + case DMIC_SR11KHZ: + HAL_MODIFY_REG(DMIC->DMIC_CTR, DMIC_CTLR_OVERSAMPLE_RATE_MASK, + DMIC_CTLR_OVERSAMPLE_RATE128); + break; + } + + if (param->sampleRate == DMIC_SR44KHZ) { + param->sampleRate = DMIC_SR48KHZ; + } else if (param->sampleRate == DMIC_SR22KHZ) { + param->sampleRate = DMIC_SR24KHZ; + } else if (param->sampleRate == DMIC_SR11KHZ) { + param->sampleRate = DMIC_SR12KHZ; + } + + /* Set sample rate*/ + HAL_MODIFY_REG(DMIC->DMIC_SR, DMIC_SR_MASK,param->sampleRate); + + /* Set sample resolution */ + HAL_MODIFY_REG(DMIC->FIFO_CTR, DMIC_FIFOCR_SAMPLE_RES_MASK,param->resolution); + + /* init DMA*/ + if (dmicPrivate->DMAChan != DMA_CHANNEL_INVALID) + DMIC_DMASet(dmicPrivate->DMAChan); + + return HAL_OK; +} + +/** + * @brief Close the DMIC module + * @note The module is closed at the end of transaction to avoid power consumption + * @retval none + */ +void HAL_DMIC_Close() +{ + DMIC_Private *dmicPrivate = &gDMICPrivate; + HAL_DMIC_Trigger(false); + dmicPrivate->isRunning = false; + dmicPrivate->isInitiate = false; + + if (dmicPrivate->DMAChan != DMA_CHANNEL_INVALID) { + HAL_DMA_DeInit(dmicPrivate->DMAChan); + HAL_DMA_Release(dmicPrivate->DMAChan); + dmicPrivate->DMAChan = DMA_CHANNEL_INVALID; + } + DMIC_MEMSET(&(dmicPrivate->dataParam), 0, sizeof(DMIC_DataParam)); +#ifndef RESERVERD_MEMORY_FOR_DMIC + DMIC_FREE(dmicPrivate->usrBuf); +#endif + dmicPrivate->usrBuf= NULL; + dmicPrivate->length = 0; + dmicPrivate->halfCounter = 0; + dmicPrivate->endCounter = 0; +} + +/** + * @internal + * @brief DMIC PINS Init + * @retval HAL status + */ +static inline HAL_Status DMIC_PINS_Init() +{ + return HAL_BoardIoctl(HAL_BIR_PINMUX_INIT, HAL_MKDEV(HAL_DEV_MAJOR_DMIC, 0), 0); +} + +/** + * @internal + * @brief DMIC PINS DeInit + * @retval HAL status + */ +static inline HAL_Status DMIC_PINS_DeInit() +{ + return HAL_BoardIoctl(HAL_BIR_PINMUX_DEINIT, HAL_MKDEV(HAL_DEV_MAJOR_DMIC, 0), 0); +} + +#ifdef CONFIG_PM +static int dmic_suspend(struct soc_device *dev, enum suspend_state_t state) +{ + DMIC_Private *dmicPrivate = &gDMICPrivate; + + switch (state) { + case PM_MODE_SLEEP: + case PM_MODE_STANDBY: + case PM_MODE_HIBERNATION: + case PM_MODE_POWEROFF: + DMIC_HwDeInit(dmicPrivate->hwParam); + DMIC_PINS_DeInit(); + HAL_CCM_DMIC_DisableMClock(); + HAL_CCM_BusDisablePeriphClock(CCM_BUS_PERIPH_BIT_DMIC); + HAL_CCM_BusForcePeriphReset(CCM_BUS_PERIPH_BIT_DMIC); + HAL_PRCM_DisableAudioPLL(); + HAL_PRCM_DisableAudioPLLPattern(); + break; + default: + break; + } + return 0; +} + +static int dmic_resume(struct soc_device *dev, enum suspend_state_t state) +{ + DMIC_Private *dmicPrivate = &gDMICPrivate; + switch (state) { + case PM_MODE_SLEEP: + case PM_MODE_STANDBY: + case PM_MODE_HIBERNATION: + DMIC_PLLAUDIO_Update(DMIC_PLL_24M); + HAL_PRCM_SetAudioPLLParam(dmicPrivate->audioPllParam); + HAL_PRCM_SetAudioPLLPatternParam(dmicPrivate->audioPllPatParam); + HAL_PRCM_EnableAudioPLL(); + HAL_PRCM_EnableAudioPLLPattern(); + HAL_CCM_BusEnablePeriphClock(CCM_BUS_PERIPH_BIT_DMIC); + HAL_CCM_BusReleasePeriphReset(CCM_BUS_PERIPH_BIT_DMIC); + + /*init and enable clk*/ + HAL_CCM_DMIC_EnableMClock(); + /*init gpio*/ + DMIC_PINS_Init(); + DMIC_HwInit(dmicPrivate->hwParam); + break; + default: + break; + } + + return 0; +} + +static struct soc_device_driver dmic_drv = { + .name = "DMIC", + .suspend = dmic_suspend, + .resume = dmic_resume, +}; + +static struct soc_device dmic_dev = { + .name = "DMIC", + .driver = &dmic_drv, +}; + +#define DMIC_DEV (&dmic_dev) +#else +#define DMIC_DEV NULL +#endif + +/** + * @brief Initializes the DMIC according to the specified parameters + * in the DMIC_Param. + * @param param: pointer to a DMIC_Param structure that contains + * the hw configuration information for DMIC controller + * @retval HAL status + */ +HAL_Status HAL_DMIC_Init(DMIC_Param *param) +{ + int32_t ret = 0; + DMIC_DEBUG("Dmic init.\n"); + if (!param) + return HAL_INVALID; + + DMIC_Private *dmicPrivate = &gDMICPrivate; + + if (dmicPrivate->isHwInit) + return HAL_OK; + DMIC_MEMSET(dmicPrivate,0,sizeof(struct DMIC_Private *)); + dmicPrivate->isHwInit = true; + + if (!param->hwParam) + dmicPrivate->hwParam = &gHwParam; + else + dmicPrivate->hwParam = param->hwParam; + + /*init and enable clk*/ + DMIC_PLLAUDIO_Update(DMIC_PLL_24M); + HAL_PRCM_SetAudioPLLParam(dmicPrivate->audioPllParam); + HAL_PRCM_EnableAudioPLL(); + HAL_PRCM_SetAudioPLLPatternParam(dmicPrivate->audioPllPatParam); + HAL_PRCM_EnableAudioPLLPattern(); + HAL_CCM_BusEnablePeriphClock(CCM_BUS_PERIPH_BIT_DMIC); + HAL_CCM_BusReleasePeriphReset(CCM_BUS_PERIPH_BIT_DMIC); + /*init and enable clk*/ + HAL_CCM_DMIC_EnableMClock(); + + /*init gpio*/ + DMIC_PINS_Init(); +#ifdef CONFIG_PM + pm_register_ops(DMIC_DEV); +#endif + ret = DMIC_HwInit(dmicPrivate->hwParam); + return ret; +} + +/** + * @brief DeInitializes the DMIC module + * @retval + */ +void HAL_DMIC_DeInit() +{ + DMIC_DEBUG("Dmic deinit.\n"); + + DMIC_Private *dmicPrivate = &gDMICPrivate; + + DMIC_HwDeInit(dmicPrivate->hwParam); + DMIC_PINS_DeInit(); + + HAL_CCM_DMIC_DisableMClock(); + HAL_CCM_BusDisablePeriphClock(CCM_BUS_PERIPH_BIT_DMIC); + HAL_PRCM_DisableAudioPLLPattern(); + HAL_PRCM_DisableAudioPLL(); + + DMIC_MEMSET(dmicPrivate,0,sizeof(struct DMIC_Private *)); +} diff --git a/platform/mcu/xr871/src/driver/chip/hal_efuse.c b/platform/mcu/xr871/src/driver/chip/hal_efuse.c new file mode 100644 index 0000000000..4ca13e58e4 --- /dev/null +++ b/platform/mcu/xr871/src/driver/chip/hal_efuse.c @@ -0,0 +1,320 @@ +/** + * @file hal_efuse.c + * @author XRADIO IOT WLAN Team + */ + +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "driver/chip/hal_efuse.h" +#include "hal_base.h" + +typedef enum { + EFUSE_STATE_INVALID = 0, + EFUSE_STATE_READY = 1, + EFUSE_STATE_BUSY = 2 +} EFUSE_State; + +EFUSE_State gEfuseState = EFUSE_STATE_INVALID; + +__STATIC_INLINE void EFUSE_EnableClkGate(void) +{ + HAL_SET_BIT(EFUSE->CTRL, EFUSE_CLK_GATE_EN_BIT); +} + +__STATIC_INLINE void EFUSE_DisableClkGate(void) +{ + HAL_CLR_BIT(EFUSE->CTRL, EFUSE_CLK_GATE_EN_BIT); +} + +__STATIC_INLINE void EFUSE_SetIndex(uint32_t index) +{ + HAL_MODIFY_REG(EFUSE->CTRL, EFUSE_INDEX_MASK, + HAL_GET_BIT(index << EFUSE_INDEX_SHIFT, EFUSE_INDEX_MASK)); +} + +__STATIC_INLINE uint32_t EFUSE_GetHwReadStatus(void) +{ + return !!HAL_GET_BIT(EFUSE->CTRL, EFUSE_HW_READ_STATUS_BIT); +} + +__STATIC_INLINE void EFUSE_StartRead(void) +{ + HAL_MODIFY_REG(EFUSE->CTRL, EFUSE_OPERA_LOCK_MASK, + (EFUSE_OPERA_UNLOCK_VAL << EFUSE_OPERA_LOCK_SHIFT) | EFUSE_SW_READ_START_BIT); +} + +__STATIC_INLINE uint32_t EFUSE_GetSwReadStatus(void) +{ + return !!HAL_GET_BIT(EFUSE->CTRL, EFUSE_SW_READ_START_BIT); +} + +__STATIC_INLINE void EFUSE_StartProgram(void) +{ + HAL_MODIFY_REG(EFUSE->CTRL, EFUSE_OPERA_LOCK_MASK, + (EFUSE_OPERA_UNLOCK_VAL << EFUSE_OPERA_LOCK_SHIFT) | EFUSE_SW_PROG_START_BIT); +} + +__STATIC_INLINE uint32_t EFUSE_GetSwProgStatus(void) +{ + return !!HAL_GET_BIT(EFUSE->CTRL, EFUSE_SW_PROG_START_BIT); +} + +__STATIC_INLINE void EFUSE_ClrCtrlReg(void) +{ + HAL_CLR_BIT(EFUSE->CTRL, EFUSE_INDEX_MASK + | EFUSE_OPERA_LOCK_MASK + | EFUSE_SW_READ_START_BIT + | EFUSE_SW_PROG_START_BIT); +} + +__STATIC_INLINE void EFUSE_SetProgValue(uint32_t value) +{ + EFUSE->PROGRAM_VALUE = value; +} + +__STATIC_INLINE uint32_t EFUSE_GetReadValue(void) +{ + return EFUSE->READ_VALUE; +} + +__STATIC_INLINE void EFUSE_SetTimingParam(EFUSE_TimingParam timingParam) +{ + EFUSE->TIMING_CTRL = timingParam; +} + +static HAL_Status EFUSE_Init(void) +{ + uint32_t clk; + EFUSE_TimingParam timingParam; + + clk = HAL_GetHFClock(); + + if (clk == HOSC_CLOCK_24M) { + timingParam = EFUSE_TIMING_PARAM_24M; + } else if (clk == HOSC_CLOCK_26M) { + timingParam = EFUSE_TIMING_PARAM_26M; + } else { + HAL_ERR("unsupport HOSC %u\n", clk); + return HAL_ERROR; + } + + EFUSE_SetTimingParam(timingParam); + + return HAL_OK; +} + +static void EFUSE_ReadData(uint8_t index, uint32_t *pData) +{ + EFUSE_EnableClkGate(); + EFUSE_SetIndex(index << 2); + EFUSE_StartRead(); + + while (EFUSE_GetSwReadStatus()) + ; + + while (EFUSE_GetHwReadStatus()) + ; + + *pData = EFUSE_GetReadValue(); + EFUSE_ClrCtrlReg(); + EFUSE_DisableClkGate(); +} + +/** + * @brief Read an amount of data from EFUSE + * @param[in] start_bit The first bit to be read on EFUSE + * @param[in] bit_num Number of bits to be read + * @param[in] data Pointer to the data buffer + * @retval HAL_Status, HAL_OK on success + */ +HAL_Status HAL_EFUSE_Read(uint32_t start_bit, uint32_t bit_num, uint8_t *data) +{ + if ((data == NULL) + || (start_bit >= HAL_EFUSE_BIT_NUM) + || (bit_num == 0) + || (bit_num > HAL_EFUSE_BIT_NUM) + || (start_bit + bit_num > HAL_EFUSE_BIT_NUM)) { + HAL_ERR("start bit %d, bit num %d, data %p\n", start_bit, bit_num, data); + return HAL_ERROR; + } + + unsigned long flags; + + flags = HAL_EnterCriticalSection(); + if (gEfuseState == EFUSE_STATE_INVALID) { + gEfuseState = EFUSE_STATE_BUSY; + HAL_ExitCriticalSection(flags); + if (EFUSE_Init() != HAL_OK) { + flags = HAL_EnterCriticalSection(); + gEfuseState = EFUSE_STATE_INVALID; + HAL_ExitCriticalSection(flags); + return HAL_ERROR; + } + } else if (gEfuseState == EFUSE_STATE_READY) { + gEfuseState = EFUSE_STATE_BUSY; + HAL_ExitCriticalSection(flags); + } else { + HAL_ExitCriticalSection(flags); + HAL_WRN("EFUSE state %d\n", gEfuseState); + return HAL_BUSY; + } + + uint8_t *p_data = data; + uint32_t bit_shift = start_bit & (32 - 1); + uint32_t word_idx = start_bit >> 5; + + uint64_t buf = 0; + uint32_t *efuse_word = (uint32_t *)&buf; + uint32_t byte_num = (bit_num + 7) >> 3; + uint32_t byte_cnt = byte_num; + uint32_t bit_cnt; + uint32_t copy_size; + + HAL_Memset(data, 0, byte_num); + + while (byte_cnt > 0) { + EFUSE_ReadData((uint8_t)word_idx, &efuse_word[0]); + + if (word_idx + 1 < 64) { + EFUSE_ReadData((uint8_t)word_idx + 1, &efuse_word[1]); + } else { + efuse_word[1] = 0; + } + buf = buf >> bit_shift; + + copy_size = (byte_cnt > sizeof(efuse_word[0])) ? sizeof(efuse_word[0]) : byte_cnt; + HAL_Memcpy(p_data, &efuse_word[0], copy_size); + byte_cnt -= copy_size; + p_data += copy_size; + word_idx++; + } + + bit_cnt = bit_num & (8 - 1); + if (bit_cnt > 0) + data[byte_num - 1] &= ((1 << bit_cnt) - 1); + + flags = HAL_EnterCriticalSection(); + gEfuseState = EFUSE_STATE_READY; + HAL_ExitCriticalSection(flags); + + return HAL_OK; +} + +static void EFUSE_WriteData(uint8_t index, uint32_t data) +{ + EFUSE_EnableClkGate(); + EFUSE_SetIndex(index << 2); + EFUSE_SetProgValue(data); + EFUSE_StartProgram(); + + while (EFUSE_GetSwProgStatus()) + ; + + EFUSE_ClrCtrlReg(); + EFUSE_DisableClkGate(); +} + +/** + * @brief Write an amount of data to EFUSE + * @param[in] start_bit The first bit to be written on EFUSE + * @param[in] bit_num Number of bits to be written + * @param[in] data Pointer to the data buffer + * @retval HAL_Status, HAL_OK on success + */ +HAL_Status HAL_EFUSE_Write(uint32_t start_bit, uint32_t bit_num, uint8_t *data) +{ + if ((data == NULL) + || (start_bit >= HAL_EFUSE_BIT_NUM) + || (bit_num == 0) + || (bit_num > HAL_EFUSE_BIT_NUM) + || (start_bit + bit_num > HAL_EFUSE_BIT_NUM)) { + HAL_ERR("start bit %d, bit num %d, data %p\n", start_bit, bit_num, data); + return HAL_ERROR; + } + + unsigned long flags; + + flags = HAL_EnterCriticalSection(); + if (gEfuseState == EFUSE_STATE_INVALID) { + gEfuseState = EFUSE_STATE_BUSY; + HAL_ExitCriticalSection(flags); + if (EFUSE_Init() != HAL_OK) { + flags = HAL_EnterCriticalSection(); + gEfuseState = EFUSE_STATE_INVALID; + HAL_ExitCriticalSection(flags); + return HAL_ERROR; + } + } else if (gEfuseState == EFUSE_STATE_READY) { + gEfuseState = EFUSE_STATE_BUSY; + HAL_ExitCriticalSection(flags); + } else { + HAL_ExitCriticalSection(flags); + HAL_WRN("EFUSE state %d\n", gEfuseState); + return HAL_BUSY; + } + + uint8_t *p_data = data; + uint32_t bit_shift = start_bit & (32 - 1); + uint32_t word_idx = start_bit >> 5; + + uint64_t buf = 0; + uint32_t *efuse_word = (uint32_t *)&buf; + uint32_t bit_cnt = bit_num; + + HAL_Memcpy(&efuse_word[1], p_data, sizeof(efuse_word[1])); + if (bit_cnt < 32) + efuse_word[1] &= (1 << bit_cnt) - 1; + efuse_word[1] = efuse_word[1] << bit_shift; + + EFUSE_WriteData((uint8_t)word_idx, efuse_word[1]); + + word_idx++; + bit_cnt -= (bit_cnt <= 32 - bit_shift) ? bit_cnt : 32 - bit_shift; + + while (bit_cnt > 0) { + HAL_Memcpy(&buf, p_data, sizeof(buf)); + buf = buf << bit_shift; + if (bit_cnt < 32) + efuse_word[1] &= (1 << bit_cnt) - 1; + + EFUSE_WriteData((uint8_t)word_idx, efuse_word[1]); + + word_idx++; + p_data += 4; + bit_cnt -= (bit_cnt <= 32) ? bit_cnt : 32; + } + + flags = HAL_EnterCriticalSection(); + gEfuseState = EFUSE_STATE_READY; + HAL_ExitCriticalSection(flags); + + return HAL_OK; +} + diff --git a/platform/mcu/xr871/src/driver/chip/hal_flash.c b/platform/mcu/xr871/src/driver/chip/hal_flash.c new file mode 100644 index 0000000000..2143da61c5 --- /dev/null +++ b/platform/mcu/xr871/src/driver/chip/hal_flash.c @@ -0,0 +1,1158 @@ +/** + * @file hal_flash.c + * @author XRADIO IOT WLAN Team + */ + +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include "sys/list.h" +#include "sys/param.h" +#include "sys/defs.h" + +#include "driver/chip/hal_spi.h" +#include "driver/chip/hal_flashctrl.h" +#include "driver/chip/hal_flash.h" +#include "hal_base.h" + +#include "pm/pm.h" + + +#define FD_DEBUG(msg, arg...) XR_DEBUG((DBG_OFF | XR_LEVEL_ALL), NOEXPAND, "[Flash Driver debug] <%s : %d> " msg "\n", __func__, __LINE__, ##arg) +#define FD_ERROR(msg, arg...) XR_ERROR((DBG_ON | XR_LEVEL_ALL), NOEXPAND, "[Flash Driver error] <%s : %d> " msg "\n", __func__, __LINE__, ##arg) +#define FD_INFO(msg, arg...) XR_DEBUG((DBG_ON | XR_LEVEL_ALL), NOEXPAND, "[Flash Driver info] <%s : %d> " msg "\n", __func__, __LINE__, ##arg) + +#define FLASH_DMA_TRANSFER_MIN_SIZE (64) + +static FlashBoardCfg *getFlashBoardCfg(int minor); +#ifdef CONFIG_PM +static struct soc_device_driver flash_drv; +#endif + + +/* + SpiFlashDriver +*/ +typedef struct SpiFlashDriver +{ + FlashDrvierBase base; + SPI_Port port; + SPI_CS cs; + SPI_Config config; +} SpiFlashDriver; + +void HAL_XIP_Delay(unsigned int us); + + +HAL_Status spiFlashOpen(FlashDrvierBase *base) +{ + /*TODO: it should be suspend schedule and deinit xip when spi is using spi0 with xiping*/ + + return HAL_OK; +} + +HAL_Status spiFlashClose(FlashDrvierBase *base) +{ + /*TODO: it should be resume schedule and init xip when spi is using spi0 with xiping*/ + + return HAL_OK; +} + +#define FD_SPI_WRITE(impl, ins, line_max) \ + do { \ + ret = insToSpi(impl, ins, line_max, HAL_SPI_Transmit); \ + if (ret != HAL_OK) { \ + FD_ERROR("spi instruction param error"); \ + goto failed; \ + } \ + } while (0) + +#define FD_SPI_READ(impl, ins, line_max) \ + do { \ + ret = insToSpi(impl, ins, line_max, HAL_SPI_Receive); \ + if (ret != HAL_OK) { \ + FD_ERROR("spi instruction param error"); \ + goto failed; \ + } \ + } while (0) + +static HAL_Status insToSpi(SpiFlashDriver *impl, InstructionField *ins, uint32_t line_max, HAL_Status (*fun)(SPI_Port, uint8_t *, uint32_t)) +{ + uint8_t *p; + + if (ins) + { + if (ins->line > line_max) + return HAL_INVALID; + if (!ins->pdata && ins->len > 4) + return HAL_ERROR; + + p = ins->pdata ? ins->pdata : (uint8_t *)&ins->data; + return fun(impl->port, p, ins->len); + } + else + return HAL_OK; +} + +static HAL_Status spiFlashWrite(FlashDrvierBase *base, InstructionField *cmd, InstructionField *addr, InstructionField *dummy, InstructionField *data) +{ + SpiFlashDriver *impl = __containerof(base, SpiFlashDriver, base); + InstructionField naddr = {0}; + HAL_Status ret; + + if (data && data->len >= (base->sizeToDma - 1)) + impl->config.opMode = SPI_OPERATION_MODE_DMA; + else + impl->config.opMode = SPI_OPERATION_MODE_POLL; + + ret = HAL_SPI_Open(impl->port, impl->cs, &impl->config, 5000); + if (ret != HAL_OK) { + FD_ERROR("spi open failed"); + return ret; + } + + HAL_SPI_CS(impl->port, 1); + FD_SPI_WRITE(impl, cmd, 1); + if (addr) + { + naddr.len = addr->len; + naddr.line = addr->line; + if (!addr->pdata) + addr->pdata = (uint8_t *)&addr->data; + naddr.data = ((addr->pdata)[2]) | ((addr->pdata)[1] << 8) | ((addr->pdata)[0] << 16); + FD_DEBUG("naddr.data: 0x%x", naddr.data); + FD_SPI_WRITE(impl, &naddr, 1); + } + FD_SPI_WRITE(impl, dummy, 1); + FD_SPI_WRITE(impl, data, 1); + HAL_SPI_CS(impl->port, 0); + +failed: + HAL_SPI_Close(impl->port); + return ret; +} + +static HAL_Status spiFlashRead(FlashDrvierBase *base, InstructionField *cmd, InstructionField *addr, InstructionField *dummy, InstructionField *data) +{ + SpiFlashDriver *impl = __containerof(base, SpiFlashDriver, base); + InstructionField naddr = {0}; + HAL_Status ret; + + if (data && data->len >= (base->sizeToDma - 1)) + impl->config.opMode = SPI_OPERATION_MODE_DMA; + else + impl->config.opMode = SPI_OPERATION_MODE_POLL; + + ret = HAL_SPI_Open(impl->port, impl->cs, &impl->config, 5000); + if (ret != HAL_OK) { + FD_ERROR("spi open failed"); + return ret; + } + + if (data && data->line == 2) + HAL_SPI_Config(impl->port, SPI_ATTRIBUTION_IO_MODE, SPI_IO_MODE_DUAL_RX); + else + HAL_SPI_Config(impl->port, SPI_ATTRIBUTION_IO_MODE, SPI_IO_MODE_NORMAL); + + HAL_SPI_CS(impl->port, 1); + FD_SPI_WRITE(impl, cmd, 1); + if (addr) + { + naddr.len = addr->len; + naddr.line = addr->line; + if (!addr->pdata) + addr->pdata = (uint8_t *)&addr->data; + naddr.data = ((addr->pdata)[2]) | ((addr->pdata)[1] << 8) | ((addr->pdata)[0] << 16); + FD_DEBUG("naddr.data: 0x%x", naddr.data); + FD_SPI_WRITE(impl, &naddr, 1); + } + FD_SPI_WRITE(impl, dummy, 1); + FD_SPI_READ(impl, data, 2); + HAL_SPI_CS(impl->port, 0); + +failed: + HAL_SPI_Close(impl->port); + return ret; +} + +static HAL_Status spiFlashSetFreq(FlashDrvierBase *base, uint32_t freq) +{ + /* TODO: tbc... */ + return HAL_INVALID; +} + +void spiFlashMsleep(FlashDrvierBase *base, uint32_t ms) +{ + HAL_MSleep(ms); +} + +void spiFlashMsleepReuseFlashc(FlashDrvierBase *base, uint32_t ms) +{ + HAL_XIP_Delay(ms * 1000); +} + +static void spiFlashDestroy(FlashDrvierBase *base) +{ + SpiFlashDriver *impl = __containerof(base, SpiFlashDriver, base); + HAL_Free(impl); +} + +FlashDrvierBase *spiDriverCreate(int dev, FlashBoardCfg *bcfg) +{ + /*TODO: check read mode*/ + + SpiFlashDriver *impl = HAL_Malloc(sizeof(SpiFlashDriver)); + HAL_Memset(impl, 0, sizeof(*impl)); + + impl->port = bcfg->spi.port; + impl->cs = bcfg->spi.cs; + impl->config.sclk = bcfg->spi.clk; + impl->config.firstBit = SPI_TCTRL_FBS_MSB; + impl->config.mode = SPI_CTRL_MODE_MASTER; + impl->config.opMode = SPI_OPERATION_MODE_DMA; /*spi0 must be poll on xip;*/ + impl->config.sclkMode = SPI_SCLK_Mode0; + + FD_DEBUG("bcfg->type: %d; impl->port: %d; impl->cs: %d; impl->config.sclk: %d;", + bcfg->type, impl->port, impl->cs, impl->config.sclk); + + impl->base.dev = dev; + impl->base.open = spiFlashOpen; + impl->base.close = spiFlashClose; + impl->base.read = spiFlashRead; + impl->base.write = spiFlashWrite; + impl->base.setFreq = spiFlashSetFreq; + impl->base.destroy = spiFlashDestroy; + impl->base.sizeToDma = FLASH_DMA_TRANSFER_MIN_SIZE; + if (bcfg->spi.port == SPI1) + impl->base.msleep = spiFlashMsleep; + else + impl->base.msleep = spiFlashMsleepReuseFlashc; + + return &impl->base; +} + +/* + FlashcFlashDriver +*/ +typedef struct FlashcFlashDriver +{ + FlashDrvierBase base; + +} FlashcFlashDriver; + +/** + * @internal + * @brief Flash driver open. + * @param base: driver. + * @retval HAL_Status: The status of driver + */ +HAL_Status flashcFlashOpen(FlashDrvierBase *base) +{ + FD_DEBUG("open"); + return HAL_Flashc_Open(); +} + +/** + * @internal + * @brief Flash driver close. + * @param base: driver. + * @retval HAL_Status: The status of driver + */ +HAL_Status flashcFlashClose(FlashDrvierBase *base) +{ + HAL_Status ret = HAL_Flashc_Close(); + FD_DEBUG("close"); + return ret; +} + +void insToFcIns(InstructionField *ins, FC_InstructionField *fcins) +{ + if (ins == NULL) + return; + + if (ins->pdata) + fcins->pdata = ins->pdata; + else + fcins->pdata = (uint8_t *)&ins->data; + fcins->len = ins->len; + fcins->line = (FC_CycleBits)((ins->line == 4) ? FC_CYCLEBITS_4 : ins->line); +} + +#define INS_CMD (0) +#define INS_ADDR (1) +#define INS_DUM (2) +#define INS_DATA (3) + +/** + * @internal + * @brief Flash driver write. + * @param base: Driver. + * @param cmd: Instruction command field. + * @param addr: Instruction address field. + * @param dummy: Instruction dummy field. + * @param data: Instruction data field. + * @retval HAL_Status: The status of driver + */ +static HAL_Status flashcFlashWrite(FlashDrvierBase *base, InstructionField *cmd, InstructionField *addr, InstructionField *dummy, InstructionField *data) +{ + int dma = 0; + FC_InstructionField tmp[4]; + HAL_Memset(tmp, 0, sizeof(tmp)); + + insToFcIns(cmd, &tmp[INS_CMD]); + insToFcIns(addr, &tmp[INS_ADDR]); + insToFcIns(dummy, &tmp[INS_DUM]); + insToFcIns(data, &tmp[INS_DATA]); + + if (tmp[INS_DATA].len >= (base->sizeToDma - 1) && tmp[INS_DATA].len < 128 * 1024) + dma = 1; + + return HAL_Flashc_Write(&tmp[INS_CMD], &tmp[INS_ADDR], &tmp[INS_DUM], &tmp[INS_DATA], dma); +} + +/** + * @internal + * @brief Flash driver read. + * @param base: Driver. + * @param cmd: Instruction command field. + * @param addr: Instruction address field. + * @param dummy: Instruction dummy field. + * @param data: Instruction data field. + * @retval HAL_Status: The status of driver + */ +static HAL_Status flashcFlashRead(FlashDrvierBase *base, InstructionField *cmd, InstructionField *addr, InstructionField *dummy, InstructionField *data) +{ + int dma = 0; + FC_InstructionField tmp[4]; + HAL_Memset(tmp, 0, sizeof(tmp)); + + insToFcIns(cmd, &tmp[INS_CMD]); + insToFcIns(addr, &tmp[INS_ADDR]); + insToFcIns(dummy, &tmp[INS_DUM]); + insToFcIns(data, &tmp[INS_DATA]); + + if (tmp[INS_DATA].len >= (base->sizeToDma - 1) && tmp[INS_DATA].len < 128 * 1024) + dma = 1; + + return HAL_Flashc_Read(&tmp[INS_CMD], &tmp[INS_ADDR], &tmp[INS_DUM], &tmp[INS_DATA], dma); +} + +/** + * @internal + * @brief Flash driver set working frequency. + * @param base: Driver. + * @param freq: Device and driver working frequency. + * @retval HAL_Status: The status of driver + */ +static HAL_Status flashcFlashSetFreq(FlashDrvierBase *base, uint32_t freq) +{ + /* TODO: tbc... */ + return HAL_INVALID; +} + +/** + * @internal + * @brief Sleep realization by driver. + * @note For some reason(XIP), system sleep function can't be used in some + * case. So the realiztion of sleep should be performed by driver. + * @param base: Driver. + * @param ms: Sleep or wait sometime in millisecond. + * @retval HAL_Status: The status of driver + */ +void flashcFlashMsleep(FlashDrvierBase *base, uint32_t ms) +{ + HAL_XIP_Delay(ms * 1000); +} + +/** + * @internal + * @brief Destroy flash driver. + * @note This may not be used. + * @param base: Driver. + * @retval HAL_Status: The status of driver + */ +static void flashcFlashDestroy(FlashDrvierBase *base) +{ + FlashcFlashDriver *impl = __containerof(base, FlashcFlashDriver, base); + HAL_Flashc_Deinit(); + HAL_Free(impl); +} + +/** + * @internal + * @brief Create a flash driver. + * @param dev: Flash device number, but not minor number. + * @param bcfg: Config from board config. + * @retval HAL_Status: The status of driver + */ +static FlashDrvierBase *flashcDriverCreate(int dev, FlashBoardCfg *bcfg) +{ + FlashcFlashDriver *impl = HAL_Malloc(sizeof(FlashcFlashDriver)); + if (impl == NULL) + FD_ERROR("malloc failed"); + HAL_Memset(impl, 0, sizeof(*impl)); + + Flashc_Config cfg; + cfg.freq = bcfg->flashc.clk; + HAL_Flashc_Init(&cfg); + + impl->base.dev = dev; + impl->base.open = flashcFlashOpen; + impl->base.close = flashcFlashClose; + impl->base.read = flashcFlashRead; + impl->base.write = flashcFlashWrite; + impl->base.setFreq = flashcFlashSetFreq; + impl->base.msleep = flashcFlashMsleep; + impl->base.destroy = flashcFlashDestroy; + impl->base.sizeToDma = FLASH_DMA_TRANSFER_MIN_SIZE; + + return &impl->base; +} + + +/** + * @internal + * @brief Create a flash driver according board config. + * @param minor: flash number = flash minor number, from board config. + * @retval HAL_Status: The status of driver + */ +static FlashDrvierBase *flashDriverCreate(int minor) +{ + FlashDrvierBase *base = NULL; + FlashBoardCfg *cfg; + int dev = HAL_MKDEV(HAL_DEV_MAJOR_FLASH, minor); + + HAL_BoardIoctl(HAL_BIR_GET_CFG, dev, (uint32_t)&cfg); + if (cfg == NULL) + FD_ERROR("flash config error"); + +#if FLASH_FLASHC_ENABLE + if (cfg->type == FLASH_DRV_FLASHC) + base = flashcDriverCreate(dev, cfg); +#endif +#if FLASH_SPI_ENABLE + if (cfg->type == FLASH_DRV_SPI) + base = spiDriverCreate(dev, cfg); +#endif + if (base == NULL) + FD_ERROR("No Flash driver support"); + + if (base == NULL) + FD_ERROR("flash driver can't be create"); + + /*manage driver gpio. tbc...*/ + + return base; +} + +/** + * @internal + * @brief Destroy a flash driver. + * @param base: Driver. + * @retval HAL_Status: The status of driver + */ +static int flashDriverDestory(FlashDrvierBase *base) +{ + if (base == NULL) + return -1; + + base->destroy(base); + return 0; +} + + +/* + FlashBoard +*/ +static FlashBoardCfg *getFlashBoardCfg(int minor) +{ + FlashBoardCfg *cfg; + int dev = HAL_MKDEV(HAL_DEV_MAJOR_FLASH, minor); + + HAL_BoardIoctl(HAL_BIR_GET_CFG, dev, (uint32_t)&cfg); + if (cfg == NULL) + FD_ERROR("flash config error"); + + return cfg; +} + + +/* + Flash +*/ +struct FlashDev +{ + struct list_head node; + HAL_Mutex lock; + uint32_t usercnt; /*not thread safe*/ + + uint32_t flash; + FlashDrvierBase *drv; + FlashChipBase *chip; + FlashReadMode rmode; + FlashPageProgramMode wmode; + +#ifdef CONFIG_PM + struct soc_device *pm; +#endif +}; + +struct list_head flashNodeHead = { + .next = &flashNodeHead, + .prev = &flashNodeHead +}; + +FlashChipBase *getFlashChip(FlashDev *dev) +{ + return dev->chip; +} + +FlashReadMode getFlashMode(FlashDev *dev) +{ + return dev->rmode; +} + +FlashDev *getFlashDev(uint32_t flash) +{ + FlashDev *dev = NULL; + FlashDev *itor = NULL; + + list_for_each_entry(itor, &flashNodeHead, node) + { + if (itor->flash == flash) { + dev = itor; + break; + } + } + + if (dev == NULL) + FD_ERROR("no this flash or not inited"); + + return dev; +} + +static int deleteFlashDev(FlashDev *dev) +{ + if (dev == NULL) { + FD_ERROR("NULL flash device"); + return -1; + } + + list_del(&dev->node); + + return 0; +} + +static int addFlashDev(FlashDev *dev) +{ + if (dev == NULL) { + FD_ERROR("NULL flash device"); + return -1; + } + + list_add_tail(&dev->node, &flashNodeHead); + + return 0; +} + +/** + * @brief Initializes flash Device. + * @note The flash device configuration is in the board_config g_flash_cfg. + * Device number is the g_flash_cfg vector sequency number. + * @param flash: the flash device number, same as the g_flash_cfg vector + * sequency number + * @retval HAL_Status: The status of driver + */ +HAL_Status HAL_Flash_Init(uint32_t flash) +{ + HAL_Status ret; + FlashDrvierBase *drv = NULL; + FlashChipBase *chip = NULL; + FlashDev *dev = NULL; + FlashBoardCfg *cfg = NULL; + + cfg = getFlashBoardCfg(flash); + if (cfg == NULL) + { + FD_ERROR("getFlashBoardCfg failed"); + goto failed; + } + + drv = flashDriverCreate(flash); + if (drv == NULL) + { + FD_ERROR("flashDriverCreate failed"); + goto failed; + } + + chip = FlashChipCreate(drv); + if (chip == NULL) + { + FD_ERROR("FlashChipCreate failed"); + goto failed; + } + + dev = HAL_Malloc(sizeof(*dev)); + if (dev == NULL) + { + FD_ERROR("malloc dev failed"); + goto failed; + } + HAL_Memset(dev, 0, sizeof(*dev)); + + /*TODO: get read mode from board, and init*/ + + dev->chip = chip; + dev->drv = drv; + dev->flash = flash; + dev->rmode = cfg->mode; + dev->wmode = FLASH_PAGEPROGRAM; + dev->usercnt = 0; + INIT_LIST_HEAD(&dev->node); + ret = HAL_MutexInit(&dev->lock); + if (ret != HAL_OK) + { + FD_ERROR("mutex init failed: %d", ret); + goto failed; + } + + addFlashDev(dev); + + drv->open(drv); +// chip->setFreq(chip, ); + if (dev->rmode & (FLASH_READ_QUAD_O_MODE | FLASH_READ_QUAD_IO_MODE | FLASH_READ_QPI_MODE)) + { + chip->switchReadMode(chip, dev->rmode); + if (dev->rmode & FLASH_READ_QPI_MODE) + chip->enableQPIMode(chip); + } + drv->close(drv); + + FD_INFO("mode: 0x%x, freq: %dHz, drv: %d", cfg->mode, cfg->flashc.clk, cfg->type); + +#ifdef CONFIG_PM + struct soc_device *flash_pm = HAL_Malloc(sizeof(*flash_pm)); + HAL_Memset(flash_pm, 0, sizeof(*flash_pm)); + flash_pm->name = "Flash"; + flash_pm->driver = &flash_drv; + flash_pm->platform_data = (void *)flash; + dev->pm = flash_pm; + pm_register_ops(flash_pm); +#endif + + return HAL_OK; + +failed: + FD_ERROR("flash init failed"); + + if (drv != NULL) + flashDriverDestory(drv); + if (dev != NULL) + HAL_Free(dev); + + return HAL_ERROR; +} + +/** + * @brief Deinitializes flash Device. + * @param flash: the flash device number, same as the g_flash_cfg vector + * sequency number + * @retval HAL_Status: The status of driver + */ +HAL_Status HAL_Flash_Deinit(uint32_t flash) +{ + FlashDev *dev = getFlashDev(flash); + FlashDrvierBase *drv = dev->drv; + FlashChipBase *chip = dev->chip; + + /*not thread safe*/ + if (dev->usercnt != 0) + return HAL_TIMEOUT; + +#ifdef CONFIG_PM + pm_unregister_ops(dev->pm); +#endif + + drv->open(drv); + chip->reset(chip); + drv->close(drv); + + deleteFlashDev(dev); + HAL_MutexDeinit(&dev->lock); + if (dev->drv != NULL) + flashDriverDestory(dev->drv); + if (dev != NULL) + HAL_Free(dev); + + return HAL_OK; +} + +/** + * @brief Open flash Device. + * @note Opened a flash device, other user can't open again, so please + * close it while don't need the flash device. + * @param flash: the flash device number, same as the g_flash_cfg vector + * sequency number. + * @param timeout_ms: timeout in millisecond. + * @retval HAL_Status: The status of driver + */ +HAL_Status HAL_Flash_Open(uint32_t flash, uint32_t timeout_ms) +{ + FlashDev *dev = getFlashDev(flash); + HAL_Status ret = HAL_ERROR; + + ret = HAL_MutexLock(&dev->lock, timeout_ms); + if (ret == HAL_OK) + dev->usercnt++; + + return ret; +} + +/** + * @brief Close flash Device. + * @param flash: the flash device number, same as the g_flash_cfg vector + * sequency number + * @retval HAL_Status: The status of driver + */ +HAL_Status HAL_Flash_Close(uint32_t flash) +{ + FlashDev *dev = getFlashDev(flash); + HAL_Status ret = HAL_ERROR; + + ret = HAL_MutexUnlock(&dev->lock); + if (ret == HAL_OK) + dev->usercnt--; + + return ret; +} + +static HAL_Status HAL_Flash_WaitCompl(FlashDev *dev, int32_t timeout_ms) +{ +#define FLASH_WAIT_TIME (1) + while (dev->chip->isBusy(dev->chip) > 0) + { + dev->drv->msleep(dev->drv, FLASH_WAIT_TIME); + timeout_ms -= FLASH_WAIT_TIME; + if (timeout_ms <= 0) { + FD_ERROR("wait clr busy timeout!"); + return HAL_TIMEOUT; + } + } + return HAL_OK; +} + +/** + * @brief Flash ioctl function. + * @note attr : arg + * others are not support for now. + * @param flash: the flash device number, same as the g_flash_cfg vector + * sequency number + * @param attr: ioctl command + * @param arg: ioctl arguement + * @retval HAL_Status: The status of driver + */ +HAL_Status HAL_Flash_Control(uint32_t flash, FlashControlCmd attr, uint32_t arg) +{ + FlashDev *dev = getFlashDev(flash); + + switch (attr) + { + /*TODO: 1.return min erase size */ + case FLASH_GET_MIN_ERASE_SIZE: + *((FlashEraseMode *)arg) = dev->chip->minEraseSize(dev->chip); + break; + /*TODO: tbc...*/ + default: + return HAL_INVALID; + } + + return HAL_OK; +} + +/** + * @brief Write flash Device memory, no need to erase first and other memory + * will not be change. Only can be used in the flash supported 4k erase. + * @note Only the flash supported 4k erase!! FDCM module is much fast than + * this function. + * @param flash: the flash device number, same as the g_flash_cfg vector + * sequency number + * @param addr: the address of memory. + * @param data: the data needed to write to flash device. + * @param size: the data size needed to write. + * @retval HAL_Status: The status of driver + */ +HAL_Status HAL_Flash_Overwrite(uint32_t flash, uint32_t addr, uint8_t *data, uint32_t size) +{ + FlashDev *dev = getFlashDev(flash); + HAL_Status ret = HAL_ERROR; + uint8_t *buf = NULL; + uint8_t *ptr = data; + uint32_t paddr = addr; + int32_t left = (int32_t)size; + uint32_t pp_size; + uint32_t saddr; + + FD_DEBUG("dev->chip->mEraseSizeSupport 0x%x", dev->chip->mEraseSizeSupport); + if (!(dev->chip->mEraseSizeSupport & FLASH_ERASE_4KB)) + return HAL_INVALID; + + buf = HAL_Malloc(FLASH_ERASE_4KB); + if (buf == NULL) + goto out; + + while (left > 0) + { + HAL_Flash_MemoryOf(flash, FLASH_ERASE_4KB, paddr, &saddr); + HAL_Flash_Read(flash, saddr, buf, FLASH_ERASE_4KB); + ret = HAL_Flash_Erase(flash, FLASH_ERASE_4KB, saddr, 1); + if (ret != HAL_OK) + return ret; + + pp_size = MIN(left, FLASH_ERASE_4KB - (paddr - saddr)); + HAL_Memcpy(buf + (paddr - saddr), ptr, pp_size); + + ret = HAL_Flash_Write(flash, saddr, buf, FLASH_ERASE_4KB); + if (ret != HAL_OK) + goto out; + + ptr += pp_size; + paddr += pp_size; + left -= pp_size; + } + +out: + if (buf != NULL) + HAL_Free(buf); + + return ret; +} + +/** + * @brief Write flash Device memory, if this memory has been written before, + * the memory must be erase first by user. HAL_Flash_Check can check + * this memory whether is writable. + * @note If write a written memory, the memory data will a be error data. + * @param flash: the flash device number, same as the g_flash_cfg vector + * sequency number + * @param addr: the address of memory. + * @param data: the data needed to write to flash device. + * @param size: the data size needed to write. + * @retval HAL_Status: The status of driver + */ +HAL_Status HAL_Flash_Write(uint32_t flash, uint32_t addr, uint8_t *data, uint32_t size) +{ + FlashDev *dev = getFlashDev(flash); + HAL_Status ret = HAL_ERROR; + uint32_t address = addr; + uint32_t left = size; + uint8_t *ptr = data; + uint32_t pp_size; + + FD_DEBUG("%d: w%d, a: 0x%x", flash, size, addr); + + if (dev == NULL) + return HAL_INVALID; + if (dev->chip->pageProgram == NULL) + return HAL_INVALID; + + while (left > 0) + { + pp_size = MIN(left, dev->chip->mPageSize - (address % dev->chip->mPageSize)); + + dev->drv->open(dev->drv); + + dev->chip->writeEnable(dev->chip); + FD_DEBUG("WE"); + ret = dev->chip->pageProgram(dev->chip, dev->wmode, address, ptr, pp_size); + FD_DEBUG("PP"); + dev->chip->writeDisable(dev->chip); + FD_DEBUG("WD"); + + if (ret < 0) + break; + + ret = HAL_Flash_WaitCompl(dev, 5000); + + dev->drv->close(dev->drv); + + if (ret < 0) + FD_ERROR("write failed: %d", ret); + + address += pp_size; + ptr += pp_size; + left -= pp_size; + } + + if (ret != 0) + FD_ERROR("write failed"); + + return ret; +} + +/** + * @brief Read flash device memory. + * @param flash: the flash device number, same as the g_flash_cfg vector + * sequency number + * @param addr: the address of memory. + * @param data: the data needed to write to flash device. + * @param size: the data size needed to write. + * @retval HAL_Status: The status of driver + */ +HAL_Status HAL_Flash_Read(uint32_t flash, uint32_t addr, uint8_t *data, uint32_t size) +{ + FlashDev *dev = getFlashDev(flash); + HAL_Status ret; + + FD_DEBUG("%d: r%d, a: 0x%x", flash, size, addr); + + if (dev == NULL) + return HAL_INVALID; + if (dev->chip->read == NULL) + return HAL_INVALID; + + dev->drv->open(dev->drv); + ret = dev->chip->read(dev->chip, dev->rmode, addr, data, size); + dev->drv->close(dev->drv); + + if (ret != 0) + FD_ERROR("read failed"); + + return ret; +} + +/** + * @brief Erase flash device memory. Flash can only erase sector or block or + * chip. + * @note Some flash is not support some erase mode, for example: FLASH M25P64 + * is only support FLASH_ERASE_CHIP and FLASH_ERASE_64KB. + * The erase address must be aligned to erase mode size, for example: + * the address should be n * 0x1000 in the erase 4kb mode, this address + * can be calculated in HAL_Flash_MemoryOf. + * @param flash: the flash device number, same as the g_flash_cfg vector + * sequency number. + * @param blk_size: + * @arg FLASH_ERASE_4KB: 4kbyte erase mode. + * @arg FLASH_ERASE_32KB: 32kbtye erase mode. + * @arg FLASH_ERASE_64KB: 64kbtye erase mode. + * @arg FLASH_ERASE_CHIP: erase whole flash chip. + * @param addr: the address of memory. + * @param blk_cnt: erase number of block or sector, no use in FLASH_ERASE_CHIP. + * @retval HAL_Status: The status of driver + */ +HAL_Status HAL_Flash_Erase(uint32_t flash, FlashEraseMode blk_size, uint32_t addr, uint32_t blk_cnt) +{ + FlashDev *dev = getFlashDev(flash); + HAL_Status ret = HAL_ERROR; + uint32_t esize = blk_size; + uint32_t eaddr = addr; + + FD_DEBUG("%d: e%d * %d, a: 0x%x", flash, (uint32_t)blk_size, blk_cnt, addr); + + if ((addr + blk_size * blk_cnt) > dev->chip->mSize) { + FD_ERROR("erase memory is over flash memory\n"); + return HAL_INVALID; + } + if ((blk_size == FLASH_ERASE_CHIP) && (blk_cnt != 1)) + FD_DEBUG("chip erase will be execute more than 1"); + if (addr % blk_size) + FD_DEBUG("chip erase on a incompatible address"); + + while (blk_cnt-- > 0) + { + dev->drv->open(dev->drv); + + dev->chip->writeEnable(dev->chip); + ret = dev->chip->erase(dev->chip, blk_size, eaddr); + dev->chip->writeDisable(dev->chip); + + if (ret < 0) + FD_ERROR("erase failed: %d", ret); + + ret = HAL_Flash_WaitCompl(dev, 5000); + + dev->drv->close(dev->drv); + + if (ret < 0) + break; + eaddr += esize; + } + + return ret; +} + +/** + * @brief Calculate which block the flash address belong to, and output a + * block address to user. + * @param flash: the flash device number, same as the g_flash_cfg vector + * sequency number. + * @param blk_size: + * @arg FLASH_ERASE_4KB: 4kbyte erase mode. + * @arg FLASH_ERASE_32KB: 32kbtye erase mode. + * @arg FLASH_ERASE_64KB: 64kbtye erase mode. + * @arg FLASH_ERASE_CHIP: erase whole flash chip. + * @param addr: the address of memory. + * @param start: the address of the block contained the addr. + * @retval HAL_Status: The status of driver + */ +HAL_Status HAL_Flash_MemoryOf(uint32_t flash, FlashEraseMode size, uint32_t addr, uint32_t *start) +{ + FlashDev *dev = getFlashDev(flash); + uint32_t page_mask; + HAL_Status ret = HAL_OK; + + if (!(size & dev->chip->mEraseSizeSupport)) + return HAL_INVALID; + + page_mask = ~((uint32_t)(size - 1)); + *start = addr & page_mask; + + return ret; +} + +/** + * @brief Check the flash memory whether . + * @note The flash device configuration is in the board_config g_flash_cfg. + * Device number is the g_flash_cfg vector sequency number. + * @param flash: the flash device number, same as the g_flash_cfg vector + * sequency number. + * @param addr: the address of memory. + * @param data: the data needed to write to flash device. + * @param size: the data size needed to write. + * @retval int: 0: same as data, no need to write or erase; + * 1: write directly, no need to erase; + * 2: need to erase first; + */ +int HAL_Flash_Check(uint32_t flash, uint32_t addr, uint8_t *data, uint32_t size) +{ +#define FLASH_CHECK_BUF_SIZE (128) + + uint8_t *pdata = data; + uint8_t *pbuf; + uint8_t *buf; + uint8_t src; + uint8_t dst; + uint32_t left = size; + uint32_t paddr = addr; + int32_t ret = 0; + + buf = HAL_Malloc(FLASH_CHECK_BUF_SIZE); + pbuf = buf + FLASH_CHECK_BUF_SIZE; + if (buf == NULL) + return -1; + + while (left > 0) + { + if ((pbuf - buf) == FLASH_CHECK_BUF_SIZE) { + HAL_Flash_Read(flash, paddr, buf, FLASH_CHECK_BUF_SIZE); + pbuf = buf; + } + + src = *pbuf++; + dst = *pdata++; + left--; + paddr++; + + dst ^= src; + if (dst == 0) + continue; /* src == dst */ + + ret = 1; /* src != dst */ + if (dst & src) { + ret = 2; /* src has bit '1', need to erase */ + break; + } + } + + if (buf != NULL) + HAL_Free(buf); + + return ret; +} + + +#ifdef CONFIG_PM +//#define FLASH_POWERDOWN (PM_MODE_POWEROFF) + +static int PM_FlashSuspend(struct soc_device *dev, enum suspend_state_t state) +{ + /* + suspend condition: + (1) + (2) + */ + FlashDev *fdev = getFlashDev((uint32_t)dev->platform_data); + + if (fdev->usercnt != 0) + return -1; + + switch (state) { + case PM_MODE_SLEEP: + break; + case PM_MODE_STANDBY: + /* flash chip do not power down */ + break; + case PM_MODE_HIBERNATION: + case PM_MODE_POWEROFF: + HAL_Flash_Deinit((uint32_t)dev->platform_data); + break; + default: + break; + } + + return 0; +} + +static int PM_FlashResume(struct soc_device *dev, enum suspend_state_t state) +{ + switch (state) { + case PM_MODE_SLEEP: + break; + case PM_MODE_STANDBY: + break; + case PM_MODE_HIBERNATION: + case PM_MODE_POWEROFF: + HAL_Flash_Init((uint32_t)dev->platform_data); + break; + default: + break; + } + + return 0; +} + +static struct soc_device_driver flash_drv = { + .name = "Flash", + .suspend = PM_FlashSuspend, + .resume = PM_FlashResume, +}; +#endif + + diff --git a/platform/mcu/xr871/src/driver/chip/hal_flashcache.c b/platform/mcu/xr871/src/driver/chip/hal_flashcache.c new file mode 100644 index 0000000000..a00b2f8cae --- /dev/null +++ b/platform/mcu/xr871/src/driver/chip/hal_flashcache.c @@ -0,0 +1,141 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include "hal_base.h" +#include "driver/chip/hal_flashcache.h" + +#include "sys/xr_debug.h" + +#define FC_DEBUG(msg, arg...) XR_DEBUG((DBG_OFF | XR_LEVEL_ALL), NOEXPAND, "[FC Debug] " msg, ##arg) + +#define FC_REG(reg) FC_DEBUG("register " #reg ": 0x%x.\n", reg); + +#define FC_REG_ALL() { \ + FC_DEBUG("flash cache register:\n"); \ + FC_REG(FLASH_CACHE->COM_CTRL); \ + FC_REG(FLASH_CACHE->PREFETCH_CTRL); \ + FC_REG(FLASH_CACHE->PREFETCH_START_ADDR); \ + FC_REG(FLASH_CACHE->IV_PREFETCH_CTRL); \ + FC_REG(FLASH_CACHE->MIN_ADDR); \ + FC_REG(FLASH_CACHE->MAX_ADDR); \ + FC_REG(FLASH_CACHE->READ_BIAS_ADDR); \ + } + + + + + + +HAL_Status Hal_FlashCache_EnablePrefetch(FlashCache_PrefetchConfig *cfg) +{ +/* uint32_t cache_line = (cfg->prefetch_cache_size * 8 + 127) / 128; + if (cache_line >= CACHE_LINE_MAX) + return HAL_INVALID; +*/ + + HAL_MODIFY_REG(FLASH_CACHE->PREFETCH_START_ADDR, + FLASH_CACHE_PREFETCH_START_ADDR_MASK, + cfg->addr_prefetch_start << FLASH_CACHE_PREFETCH_START_ADDR_SHIFT); + HAL_MODIFY_REG(FLASH_CACHE->PREFETCH_CTRL, +// FLASH_CACHE_PREFETCH_CTRL_SIZE_MASK | + FLASH_CACHE_PREFETCH_CTRL_BRANCH_MASK | + FLASH_CACHE_PREFETCH_CTRL_STOP_MASK, +// (cache_line << FLASH_CACHE_PREFETCH_CTRL_SIZE_SHIFT) | + (cfg->prefetch_2nd_branch << FLASH_CACHE_PREFETCH_CTRL_BRANCH_SHIFT) | + (FLASH_CACHE_PREFETCH_ENABLE << FLASH_CACHE_PREFETCH_CTRL_STOP_SHIFT)); + + return HAL_OK; +} + +HAL_Status Hal_FlashCache_DisablePrefetch() +{ + HAL_MODIFY_REG(FLASH_CACHE->PREFETCH_CTRL, + FLASH_CACHE_PREFETCH_CTRL_STOP_MASK, + (FLASH_CACHE_PREFETCH_DISABLE << FLASH_CACHE_PREFETCH_CTRL_STOP_SHIFT)); + return HAL_OK; +} + +/* +HAL_Status Hal_FlashCache_PrefetchInt(FlashCache_PrefetchIntConfig * cfg) +{ + uint32_t cnt = 0; + + + + HAL_MODIFY_REG(FLASH_CACHE->IV_PREFETCH_CTRL, + FLASH_CACHE_IV_PREFETCH_CTRL_IV_NUM_MASK, + cnt << FLASH_CACHE_IV_PREFETCH_CTRL_IV_NUM_SHIFT); + + if (cnt != 0) + HAL_SET_BIT(FLASH_CACHE->IV_PREFETCH_CTRL, + FLASH_CACHE_IV_PREFETCH_CTRL_RELEASE_MASK | + FLASH_CACHE_IV_PREFETCH_CTRL_ENABLE_MASK); + else + HAL_CLR_BIT(FLASH_CACHE->IV_PREFETCH_CTRL, + FLASH_CACHE_IV_PREFETCH_CTRL_RELEASE_MASK | + FLASH_CACHE_IV_PREFETCH_CTRL_ENABLE_MASK); +} +*/ + + +HAL_Status Hal_FlashCache_Init(FlashCache_Config *cfg) +{ +// uint32_t cache_line = (cfg->cache_size * 8 + 127) / 128; +// uint32_t cache_line = (CACHE_SIZE * 8 + 127) / 128; + + /* check param */ + if (/*(cache_line > 0x1FF) || */(cfg->addr_bias & (~FLASH_CACHE_READ_BIAS_ADDR_MASK))) { + + return HAL_INVALID; + } + + /* CCMU Enable */ + +// HAL_MODIFY_REG(FLASH_CACHE, FLASH_CACHE_COM_CTRL_SIZE_MASK, cache_line << FLASH_CACHE_COM_CTRL_SIZE_SHIFT); + HAL_MODIFY_REG(FLASH_CACHE->MIN_ADDR, + FLASH_CACHE_MIN_ADDR_MASK, + CACHE_START_ADDR << FLASH_CACHE_MIN_ADDR_SHIFT); + HAL_MODIFY_REG(FLASH_CACHE->MAX_ADDR, + FLASH_CACHE_MAX_ADDR_MASK, + CACHE_END_ADDR << FLASH_CACHE_MAX_ADDR_SHIFT); + HAL_MODIFY_REG(FLASH_CACHE->READ_BIAS_ADDR, + FLASH_CACHE_READ_BIAS_ADDR_MASK, + cfg->addr_bias << FLASH_CACHE_READ_BIAS_ADDR_SHIFT); + + FC_REG_ALL(); + return HAL_OK; +} + +HAL_Status Hal_FlashCache_Deinit() +{ + return HAL_OK; +} + + diff --git a/platform/mcu/xr871/src/driver/chip/hal_flashctrl.c b/platform/mcu/xr871/src/driver/chip/hal_flashctrl.c new file mode 100644 index 0000000000..ef2b965939 --- /dev/null +++ b/platform/mcu/xr871/src/driver/chip/hal_flashctrl.c @@ -0,0 +1,1242 @@ +/** + * @file hal_flashctrl.c + * @author XRADIO IOT WLAN Team + */ + +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include "hal_base.h" +#include "driver/chip/hal_flashctrl.h" +#include "driver/chip/hal_flashcache.h" +#include "driver/chip/hal_dma.h" + +#include "pm/pm.h" + +#include "sys/xr_debug.h" + +#define FC_DEBUG_ON (DBG_OFF) + +#define FC_DEBUG(msg, arg...) XR_DEBUG((FC_DEBUG_ON | XR_LEVEL_ALL), NOEXPAND, "[FC Debug] <%s : %d> " msg "\n", __func__, __LINE__, ##arg) + +#define FC_ERROR(msg, arg...) XR_ERROR((DBG_ON | XR_LEVEL_ALL), NOEXPAND, "[FC error] <%s : %d> " msg "\n", __func__, __LINE__, ##arg) + +#define FC_REG(reg) FC_DEBUG("register " #reg " (addr: 0x%x): 0x%x.", (uint32_t)&(reg), reg); + +#define FC_REG_ALL() \ + { \ + FC_DEBUG("flash controller register:"); \ + FC_REG(FLASH_CTRL->COMMON_CFG); \ + FC_REG(FLASH_CTRL->I_CMD_CFG); \ + FC_REG(FLASH_CTRL->I_DUMMY_H); \ + FC_REG(FLASH_CTRL->I_DUMMY_L); \ + FC_REG(FLASH_CTRL->I_CS_WAIT); \ + FC_REG(FLASH_CTRL->I_IO_WAIT); \ + FC_REG(FLASH_CTRL->RESERVE0[0]); \ + FC_REG(FLASH_CTRL->FLASH_COMMON_CFG); \ + FC_REG(FLASH_CTRL->XIP_EXEC); \ + FC_REG(FLASH_CTRL->COMMON_ADD_CFG); \ + FC_REG(FLASH_CTRL->S_CMD_CFG); \ + FC_REG(FLASH_CTRL->S_ADDR_CFG); \ + FC_REG(FLASH_CTRL->S_WR_NUM); \ + FC_REG(FLASH_CTRL->S_RD_NUM); \ + FC_REG(FLASH_CTRL->S_DUMMY_H); \ + FC_REG(FLASH_CTRL->S_DUMMY_L); \ + FC_REG(FLASH_CTRL->FIFO_TRIG_LEVEL); \ + FC_REG(FLASH_CTRL->FIFO_STATUS); \ + FC_REG(FLASH_CTRL->S_IO_WAIT); \ + FC_REG(FLASH_CTRL->WRAP_MODE); \ + FC_REG(FLASH_CTRL->START_SEND); \ + FC_REG(FLASH_CTRL->INT_EN); \ + FC_REG(FLASH_CTRL->INT_STA); \ + } + +#if (FC_DEBUG_ON == DBG_ON) +#define FC_WHILE_TIMEOUT(cond, i) \ + i = 0x3FFFFFF; \ + do \ + { \ + if (--i == 0) { \ + FC_REG_ALL(); \ + return HAL_ERROR; \ + } \ + } while(cond) +#else +#define FC_WHILE_TIMEOUT(cond, i) \ + (void)i; \ + while(cond) +#endif + + +void udelay(unsigned int us); + +static HAL_Status HAL_Flashc_DMAWrite(uint8_t *data, uint32_t size); +static HAL_Status HAL_Flashc_PollWrite(uint8_t *data, uint32_t size); +static HAL_Status HAL_Flashc_DMARead(uint8_t *data, uint32_t size); +static HAL_Status HAL_Flashc_PollRead(uint8_t *data, uint32_t size); + +typedef enum { + FC_EN_CONTINUE = 1 << FC_CC_CONT_EN_SHIFT, + FC_EN_PREFETCH = 1 << FC_CC_PREFETCH_EN_SHIFT, + FC_EN_IBUS = 1 << FC_CC_IBUS_EN_SHIFT +} FC_En; + +static inline void FC_Ibus_Enable(uint32_t flash_ctrl_en) +{ + HAL_SET_BIT(FLASH_CTRL->COMMON_CFG, flash_ctrl_en); +} + +static inline void FC_Ibus_Disable(uint32_t flash_ctrl_en) +{ + HAL_CLR_BIT(FLASH_CTRL->COMMON_CFG, flash_ctrl_en); +} + +static inline bool FC_Ibus_IsXIP() +{ + return HAL_GET_BIT(FLASH_CTRL->XIP_EXEC, FC_XE_MASK); +} + +/*typedef enum { + FLASH_CTRL_READMODE_NORMAL_IO = () | () | (), + FLASH_CTRL_READMODE_DUAL_OUTPUT, + FLASH_CTRL_READMODE_DUAL_IO, + FLASH_CTRL_READMODE_QUAD_OUTPUT, + FLASH_CTRL_READMODE_QUAD_IO +} Flash_Ctrl_ReadMode;*/ + +static inline uint32_t FC_DefOutput(uint8_t io_num, FC_Io_Output io) +{ + uint32_t mask, shift; + + if (io_num == 1) { + mask = FC_CC_IO1_MASK; + shift = FC_CC_IO1_SHIFT; + } else if (io_num == 2) { + mask = FC_CC_IO2_MASK; + shift = FC_CC_IO2_SHIFT; + } else if (io_num == 3) { + mask = FC_CC_IO3_MASK; + shift = FC_CC_IO3_SHIFT; + } else + return -1; + + HAL_MODIFY_REG(FLASH_CTRL->COMMON_CFG, mask, io << shift); + + return 0; +} + +static inline void FC_Ibus_ReadConfig(uint8_t read_cmd, + FC_CycleBits cmd, + FC_CycleBits addr, + FC_CycleBits dummy, + FC_CycleBits data, + uint8_t dummy_byte) +{ + uint8_t dummy_width; +/* if (dummy == FC_CYCLEBITS_4) + dummy_width = dummy_cycle * 4; + else if (dummy == FC_CYCLEBITS_2) + dummy_width = dummy_cycle * 2;??????????????????? + else + dummy_width = dummy_cycle * dummy;*/ + + if (dummy_byte > 8) + dummy_byte = 8; + dummy_width = dummy_byte * 8; + + HAL_MODIFY_REG(FLASH_CTRL->I_CMD_CFG, + FC_ICC_CMD_MASK + | FC_ICC_CMD_BIT_MASK + | FC_ICC_ADDR_BIT_MASK + | FC_ICC_DUMMY_BIT_MASK + | FC_ICC_DATA_BIT_MASK + | FC_ICC_DUMMY_WIDTH_MASK, + (read_cmd << FC_ICC_CMD_SHIFT) + | (cmd << FC_ICC_CMD_BIT_SHIFT) + | (addr << FC_ICC_ADDR_BIT_SHIFT) + | (dummy << FC_ICC_DUMMY_BIT_SHIFT) + | (data << FC_ICC_DATA_BIT_SHIFT) + | (dummy_width << FC_ICC_DUMMY_WIDTH_SHIFT)); +} + +static inline void FC_Ibus_DummyData(uint32_t dummyh, uint32_t dummyl) +{ + FLASH_CTRL->I_DUMMY_H = dummyh; + FLASH_CTRL->I_DUMMY_L = dummyl; +} + +static inline void FC_Ibus_TransmitDelay(Flash_Ctrl_DelayCycle *delay) +{ + HAL_MODIFY_REG(FLASH_CTRL->I_CS_WAIT, + FC_ICW_BEGIN_MASK + | FC_ICW_OVER_MASK + | FC_ICW_DESEL_MASK, + (delay->cs_begin << FC_ICW_BEGIN_SHIFT) + | (delay->cs_over << FC_ICW_OVER_SHIFT) + | (delay->cs_deselect << FC_ICW_DESEL_SHIFT)); + HAL_MODIFY_REG(FLASH_CTRL->I_IO_WAIT, + FC_IIW_CMD_MASK + | FC_IIW_ADDR_MASK + | FC_IIW_DUM_MASK, + (delay->cmd_over << FC_IIW_CMD_SHIFT) + | (delay->addr_over << FC_IIW_ADDR_SHIFT) + | (delay->dummy_over << FC_IIW_DUM_SHIFT)); + HAL_MODIFY_REG(FLASH_CTRL->FLASH_COMMON_CFG, + FC_FCC_WAIT_DATA_MASK, + delay->data << FC_FCC_WAIT_DATA_SHIFT); +} + +static inline void FC_SetFlash(FC_Cs cs, FC_TCTRL_Fbs fbs, FC_Sclk_Mode mode) +{ + HAL_MODIFY_REG(FLASH_CTRL->FLASH_COMMON_CFG, + FC_FCC_CS_MASK + | FC_FCC_FBS_MASK + | FC_FCC_CPOL_MASK + | FC_FCC_CPHA_MASK, + cs | fbs | mode); +} + +/*static inline void FC_Ibus_SetCsDelay(uint8_t enable_cyc, uint8_t disable_cyc, uint8_t deselect_cyc) +{ + HAL_MODIFY_REG(FLASH_CTRL->I_CS_WAIT, + FC_ICW_BEGIN_MASK + | FC_ICW_DESEL_MASK + | FC_ICW_OVER_MASK, + (enable_cyc << FC_ICW_BEGIN_SHIFT) + | (disable_cyc << FC_ICW_OVER_SHIFT) + | (deselect_cyc << FC_ICW_DESEL_SHIFT)); +} + +static inline void FC_Sbus_SetCsDelay(uint8_t enable_cyc, uint8_t disable_cyc, uint8_t deselect_cyc) +{ + HAL_MODIFY_REG(FLASH_CTRL->I_CS_WAIT, + FC_ICW_BEGIN_MASK + | FC_ICW_OVER_MASK, + (enable_cyc << FC_ICW_BEGIN_SHIFT) + | (disable_cyc << FC_ICW_OVER_SHIFT)); + + HAL_MODIFY_REG(FLASH_CTRL->COMMON_ADD_CFG, + FC_CAC_CS_DESEL_WAIT_MASK, + deselect_cyc << FC_CAC_CS_DESEL_WAIT_SHIFT); +}*/ + +static inline void FC_Sbus_ResetFIFO(bool tx, bool rx) +{ + HAL_MODIFY_REG(FLASH_CTRL->COMMON_ADD_CFG, + FC_CAC_TX_FIFO_RESET_MASK | FC_CAC_RX_FIFO_RESET_MASK, + (tx << FC_CAC_TX_FIFO_RESET_SHIFT) | (rx << FC_CAC_RX_FIFO_RESET_SHIFT)); +} + +static inline void FC_WrapMode(bool enable) +{ + HAL_MODIFY_REG(FLASH_CTRL->COMMON_ADD_CFG, FC_CAC_WRAP_EN_MASK, enable << FC_CAC_WRAP_EN_SHIFT); +} + +static inline void FC_AddressMode(FC_Addr_Mode mode) +{ + HAL_MODIFY_REG(FLASH_CTRL->COMMON_ADD_CFG, FC_CAC_ADDR_SIZE_MODE_MASK, mode << FC_CAC_ADDR_SIZE_MODE_SHIFT); +} + +static inline void FC_Sbus_CommandConfig(FC_CycleBits cmd, + FC_CycleBits addr, + FC_CycleBits dummy, + FC_CycleBits data, + uint8_t dummy_byte) +{ + uint8_t dummy_width; + + if (dummy_byte > 8) + dummy_byte = 8; + dummy_width = dummy_byte * 8; + + HAL_MODIFY_REG(FLASH_CTRL->S_CMD_CFG, + FC_SCC_CMD_BIT_MASK + | FC_SCC_ADDR_BIT_MASK + | FC_SCC_DUMMY_BIT_MASK + | FC_SCC_DATA_BIT_MASK + | FC_SCC_DUMMY_DATA_BIT_MASK, + (cmd << FC_SCC_CMD_BIT_SHIFT) + | (addr << FC_SCC_ADDR_BIT_SHIFT) + | (dummy << FC_SCC_DUMMY_BIT_SHIFT) + | (data << FC_SCC_DATA_BIT_SHIFT) + | (dummy_width << FC_SCC_DUMMY_DATA_BIT_SHIFT)); +} + +static inline void FC_Sbus_Command(uint8_t cmd, uint32_t addr, uint32_t dummyh, uint32_t dummyl) +{ + HAL_MODIFY_REG(FLASH_CTRL->S_CMD_CFG, FC_SCC_CMD_MASK, cmd << FC_SCC_CMD_SHIFT); + HAL_MODIFY_REG(FLASH_CTRL->S_ADDR_CFG, FC_SAC_MASK, addr << FC_SAC_SHIFT); + HAL_MODIFY_REG(FLASH_CTRL->S_DUMMY_H, FC_SDH_MASK, dummyh << FC_SDH_SHIFT); + HAL_MODIFY_REG(FLASH_CTRL->S_DUMMY_L, FC_SDL_MASK, dummyl << FC_SDL_SHIFT); +} + +static inline void FC_Sbus_WriteSize(uint16_t size) +{ + if (size & (~0x1FF)) + FC_DEBUG("write number error"); + size &= 0x1FF; + HAL_MODIFY_REG(FLASH_CTRL->S_WR_NUM, FC_SWN_MASK, size << FC_SWN_SHIFT); +} + +static inline void FC_Sbus_ReadSize(uint32_t size) +{ + HAL_MODIFY_REG(FLASH_CTRL->S_RD_NUM, FC_SRN_MASK, size << FC_SRN_SHIFT); +} + +/*static inline void FC_Sbus_DummyData(uint32_t dummyh, uint32_t dummyl) +{ + FLASH_CTRL->S_DUMMY_H = dummyh; + FLASH_CTRL->S_DUMMY_L = dummyl; +}*/ + +static inline void FC_Sbus_TransmitDelay(Flash_Ctrl_DelayCycle *delay) +{ + HAL_MODIFY_REG(FLASH_CTRL->I_CS_WAIT, + FC_ICW_BEGIN_MASK + | FC_ICW_OVER_MASK, + (delay->cs_begin << FC_ICW_BEGIN_SHIFT) + | (delay->cs_over << FC_ICW_OVER_SHIFT)); + + HAL_MODIFY_REG(FLASH_CTRL->COMMON_ADD_CFG, + FC_CAC_CS_DESEL_WAIT_MASK, + delay->cs_deselect << FC_CAC_CS_DESEL_WAIT_SHIFT); + + HAL_MODIFY_REG(FLASH_CTRL->S_IO_WAIT, + FC_SIW_CMD_MASK + | FC_SIW_ADDR_MASK + | FC_SIW_DUMMY_MASK, + (delay->cmd_over << FC_SIW_CMD_SHIFT) + | (delay->addr_over << FC_SIW_ADDR_SHIFT) + | (delay->dummy_over << FC_SIW_DUMMY_SHIFT)); + + HAL_MODIFY_REG(FLASH_CTRL->FLASH_COMMON_CFG, + FC_FCC_WAIT_DATA_MASK, + delay->data << FC_FCC_WAIT_DATA_SHIFT); +} + +static inline void FC_Sbus_FIFOTriggerLevel(uint8_t txfull, uint8_t txempty, uint8_t rxfull, uint8_t rxempty) +{ + HAL_MODIFY_REG(FLASH_CTRL->FIFO_TRIG_LEVEL, + FC_FTL_RD_FIFO_EMPTY_MASK + | FC_FTL_RD_FIFO_FULL_MASK + | FC_FTL_WR_FIFO_EMPTY_MASK + | FC_FTL_WR_FIFO_FULL_MASK, + (txfull << FC_FTL_WR_FIFO_FULL_SHIFT) + | (txempty << FC_FTL_WR_FIFO_EMPTY_SHIFT) + | (rxfull << FC_FTL_RD_FIFO_FULL_SHIFT) + | (rxempty << FC_FTL_RD_FIFO_EMPTY_SHIFT)); +} + +typedef enum FC_Sbus_RW +{ + FC_SBUS_READ, + FC_SBUS_WRITE, +} FC_Sbus_RW; + +bool FC_Sbus_IsAvailable(FC_Sbus_RW rw) +{ + if (rw == FC_SBUS_WRITE) + return !!HAL_GET_BIT(FLASH_CTRL->FIFO_STATUS, FC_FS_WR_BUF_VALID_MASK); + else + return !!HAL_GET_BIT(FLASH_CTRL->FIFO_STATUS, FC_FS_RD_BUF_VALID_MASK); +} + +int FC_Sbus_GetBufCnt(FC_Sbus_RW rw) +{ + if (rw == FC_SBUS_WRITE) + return HAL_GET_BIT_VAL(FLASH_CTRL->FIFO_STATUS, FC_FS_WR_BUF_CNT_SHIFT, FC_FS_WR_BUF_CNT_VMASK); + else + return HAL_GET_BIT_VAL(FLASH_CTRL->FIFO_STATUS, FC_FS_RD_BUF_CNT_SHIFT, FC_FS_RD_BUF_CNT_VMASK); +} + +int FC_Sbus_GetFIFOCnt(FC_Sbus_RW rw) +{ + if (rw == FC_SBUS_WRITE) + return HAL_GET_BIT_VAL(FLASH_CTRL->FIFO_STATUS, FC_FS_WR_FIFO_CNT_SHIFT, FC_FS_WR_FIFO_CNT_VMASK); + else + return HAL_GET_BIT_VAL(FLASH_CTRL->FIFO_STATUS, FC_FS_RD_FIFO_CNT_SHIFT, FC_FS_RD_FIFO_CNT_VMASK); +} + +/* + Debug State: + 0x2 Send CMD; + 0x4 Send Address; + 0x6 Send Dummy; + 0x8 Send Data; + 0x9 Get Data; +*/ +static inline int FC_Sbus_GetDebugState() +{ + return HAL_GET_BIT_VAL(FLASH_CTRL->FIFO_STATUS, FC_FS_STATUS_DGB_SHIFT, FC_FS_STATUS_DGB_VMASK); +} + +#if (FC_DEBUG_ON == DBG_ON) +#define FC_DebugCheck(state) __FC_DebugCheck(state, __LINE__) +static int __FC_DebugCheck(int state, uint32_t line) +{ + int debug = FC_Sbus_GetDebugState(); + if (debug != state) { + HAL_UDelay(5000); + debug = FC_Sbus_GetDebugState(); + if (debug != state) { + FC_DEBUG("line: %d, error state: 0x%x", line, state); + FC_REG_ALL(); + return -1; + } + } + return 0; +} +#else +#define FC_DebugCheck(state) __FC_DebugCheck(state) +static inline int __FC_DebugCheck(int state) +{ + while(FC_Sbus_GetDebugState() != state); + return 0; +} + +#endif + +static inline bool FC_IsWrapMode() +{ + return !!HAL_GET_BIT(FLASH_CTRL->WRAP_MODE, FC_WM_MASK); +} + +static inline void FC_Sbus_StartSend() +{ + HAL_SET_BIT(FLASH_CTRL->START_SEND, FC_SS_MASK); +} + +static inline bool FC_Sbus_isSending() +{ + return !!HAL_GET_BIT(FLASH_CTRL->START_SEND, FC_SS_MASK); +} + +typedef enum FC_Sbus_IntType +{ + FC_INT_HREADY_TIMEOUT = 1 << 13, + FC_INT_TC = 1 << FC_IE_CMPL_SHIFT, + FC_INT_WR_FIFO_UNDERFLOW = 1 << FC_IE_WR_FIFO_UNDERFLOW_SHIFT, + FC_INT_WR_FIFO_OVERFLOW = 1 << FC_IE_WR_FIFO_OVERFLOW_SHIFT, + FC_INT_RD_FIFO_UNDERFLOW = 1 << FC_IE_RD_FIFO_UNDERFLOW_SHIFT, + FC_INT_RD_FIFO_OVERFLOW = 1 << FC_IE_RD_FIFO_OVERFLOW_SHIFT, + FC_INT_WR_FIFO_FULL = 1 << FC_IE_WR_FIFO_FULL_SHIFT, + FC_INT_WR_FIFO_EMPTY = 1 << FC_IE_WR_FIFO_EMPTY_SHIFT, + FC_INT_WR_FIFO_READY = 1 << FC_IE_WR_FIFO_READY_SHIFT, + FC_INT_RD_FIFO_FULL = 1 << FC_IE_RD_FIFO_FULL_SHIFT, + FC_INT_RD_FIFO_EMPTY = 1 << FC_IE_RD_FIFO_EMPTY_SHIFT, + FC_INT_RD_FIFO_READY = 1 << FC_IE_RD_FIFO_READY_SHIFT, +} FC_Sbus_IntType; + +static inline void FC_Sbus_EnableInt(FC_Sbus_IntType type) +{ + HAL_SET_BIT(FLASH_CTRL->INT_EN, type); +} + +static inline void FC_Sbus_DisableInt(FC_Sbus_IntType type) +{ + HAL_CLR_BIT(FLASH_CTRL->INT_EN, type); +} + +static inline int FC_Sbus_GetStatus(FC_Sbus_IntType type) +{ + return !!HAL_GET_BIT(FLASH_CTRL->INT_STA, type); +} + +static inline void FC_Sbus_ClrStatus(FC_Sbus_IntType type) +{ + HAL_CLR_BIT(FLASH_CTRL->INT_STA, type); +} + +static inline void FC_Sbus_Write(uint8_t data) +{ + *((uint8_t *)&FLASH_CTRL->S_WDATA) = data; +} + +static inline uint8_t FC_Sbus_Read(void) +{ + return *((uint8_t *)&FLASH_CTRL->S_RDATA); +} + +static inline uint8_t FC_GetDataDelay(uint32_t freq) +{ + if (freq < 48000000) + return 0; + else if (freq <= 64000000) + return 1; + else + return 2; +} + +/* + * @brief + */ +static uint8_t ccmu_on = 0; +static void HAL_Flashc_EnableCCMU() +{ + if (ccmu_on++ != 0) + return; + HAL_CCM_BusEnablePeriphClock(CCM_BUS_PERIPH_BIT_FLASHC); + HAL_CCM_FLASHC_EnableMClock(); +} + +/* + * @brief + */ +static void HAL_Flashc_DisableCCMU() +{ + if (--ccmu_on != 0) + return; + + FC_DEBUG("DISABLE CCMU"); + HAL_CCM_BusDisablePeriphClock(CCM_BUS_PERIPH_BIT_FLASHC); + HAL_CCM_FLASHC_DisableMClock(); +} + +static inline void HAL_Flashc_ResetCCMU() +{ + CCM_BusPeriphBit ccm_flashc = CCM_BUS_PERIPH_BIT_FLASHC; /* spi_port translate to ccm_bit */ + HAL_CCM_BusForcePeriphReset(ccm_flashc); + HAL_CCM_BusReleasePeriphReset(ccm_flashc); +} + + +/* + * @brief + */ +static bool HAL_Flashc_ConfigCCMU(uint32_t clk) +{ + CCM_AHBPeriphClkSrc src; + uint32_t mclk; + uint32_t div; + + if (clk > HAL_GetHFClock()) + { + mclk = HAL_PRCM_GetDevClock(); + src = CCM_AHB_PERIPH_CLK_SRC_DEVCLK; + } + else + { + mclk = HAL_GetHFClock(); + src = CCM_AHB_PERIPH_CLK_SRC_HFCLK; + } + + div = (mclk + clk - 1) / clk; + div = div==0 ? 1 : div; + + if (div > (16 * 8)) + return 0; + + if (div > 64) + HAL_CCM_FLASHC_SetMClock(src, CCM_PERIPH_CLK_DIV_N_8, (CCM_PeriphClkDivM)((div >> 3) - 1)); + else if (div > 32) + HAL_CCM_FLASHC_SetMClock(src, CCM_PERIPH_CLK_DIV_N_4, (CCM_PeriphClkDivM)((div >> 2) - 1)); + else if (div > 16) + HAL_CCM_FLASHC_SetMClock(src, CCM_PERIPH_CLK_DIV_N_2, (CCM_PeriphClkDivM)((div >> 1) - 1)); + else + HAL_CCM_FLASHC_SetMClock(src, CCM_PERIPH_CLK_DIV_N_1, (CCM_PeriphClkDivM)((div >> 0) - 1)); + + return 1; +} + +#ifdef CONFIG_PM +static int hal_flashc_suspending = 0; +static XIP_Config pm_ibus_cfg; +static Flashc_Config pm_sbus_cfg; +static uint8_t pm_xip = 0; +static struct soc_device flashc_dev; +#endif + +static uint8_t xip_on = 0; +static uint8_t pin_inited = 0; +static FC_En xip_continue = 0; +static int sbusing = 0; + + +void HAL_XIP_Delay(unsigned int us); + +static void HAL_Flashc_PinInit() +{ +// unsigned long flags = HAL_EnterCriticalSection(); + if (pin_inited++ != 0) { +// HAL_ExitCriticalSection(flags); + return; + } + /* open io */ + HAL_BoardIoctl(HAL_BIR_PINMUX_INIT, HAL_MKDEV(HAL_DEV_MAJOR_FLASHC, 0), 0); +// HAL_ExitCriticalSection(flags); +} + +static void HAL_Flashc_PinDeinit() +{ +// unsigned long flags = HAL_EnterCriticalSection(); + if (--pin_inited != 0) { +// HAL_ExitCriticalSection(flags); + return; + } + //close io + HAL_BoardIoctl(HAL_BIR_PINMUX_DEINIT, HAL_MKDEV(HAL_DEV_MAJOR_FLASHC, 0), 0); +// HAL_ExitCriticalSection(flags); +} + +/** + * @brief Initialize Flash controller IBUS driver (XIP). + * @param cfg: + * @arg cfg->addr: Started address of XIP code in Flash. + * @arg cfg->freq: Flash working frequency. + * @arg cfg->delay: Delay of hardware. + * @arg cfg->ins: Instruction of XIP reading + * @arg cfg->cont_mode: Enable continue mode in reading or not. + * @retval HAL_Status: The status of driver. + */ +HAL_Status HAL_Flashc_Xip_Init(XIP_Config *cfg) +{ +#ifdef CONFIG_PM + if (!hal_flashc_suspending) + HAL_Memcpy(&pm_ibus_cfg, cfg, sizeof(pm_ibus_cfg)); + xip_on = 1; +#endif + + /* enable ccmu */ + HAL_Flashc_EnableCCMU(); + + /* open io */ + HAL_Flashc_PinInit(); + + FC_Ibus_Disable(FC_EN_CONTINUE | FC_EN_IBUS | FC_EN_PREFETCH); + + /* config flash controller */ + Flash_Ctrl_DelayCycle delay = {1, 0, 3, 0, 0, 0, 1}; + delay.data = FC_GetDataDelay(cfg->freq); + FC_Ibus_TransmitDelay(&delay); + + FC_SetFlash(FC_TCTRL_CS_LOW_ENABLE, FC_TCTRL_FBS_MSB, FC_SCLK_Mode0); + + FC_Ibus_ReadConfig(cfg->ins.cmd, + cfg->ins.cmd_line, + cfg->ins.addr_line, + cfg->ins.dummy_line, + cfg->ins.data_line, + cfg->ins.dum_btyes); + if (cfg->cont_mode) { + FC_Ibus_DummyData(0x20000000, 0); + FC_Ibus_Enable(FC_EN_CONTINUE | FC_EN_IBUS); + xip_continue = FC_EN_CONTINUE; + } else { + FC_Ibus_DummyData(0, 0); + FC_Ibus_Enable(FC_EN_IBUS); + } + + //config flash cache + FlashCache_Config cache_cfg = {cfg->addr}; + Hal_FlashCache_Init(&cache_cfg); + + FC_DEBUG("cfg->freq: %d; cfg->ins.cmd: %d; cfg->ins.cmd_line: %d", cfg->freq, cfg->ins.cmd, cfg->ins.cmd_line); + FC_DEBUG("cfg->ins.addr_line: %d; cfg->ins.dummy_line: %d; cfg->ins.data_line: %d", cfg->ins.addr_line, cfg->ins.dummy_line, cfg->ins.data_line); + FC_DEBUG("cfg->ins.dum_btyes: %d; cfg->cont_mode: %d; cfg->addr: %d", cfg->ins.dum_btyes, cfg->cont_mode, cfg->addr); + + FC_DEBUG("ccmu : %d", ccmu_on); + + FC_REG_ALL(); + + return HAL_OK; +} + +/** + * @brief Deinitialize Flash controller IBUS (XIP). + * @param None + * @retval HAL_Status: The status of driver. + */ +HAL_Status HAL_Flashc_Xip_Deinit() +{ + unsigned long flags = HAL_EnterCriticalSection(); + //config flash controller + FC_Ibus_Disable(FC_EN_CONTINUE | FC_EN_IBUS | FC_EN_PREFETCH); + xip_on = 0; + HAL_ExitCriticalSection(flags); + + //close io + HAL_Flashc_PinDeinit(); + + //deinit flash cache + Hal_FlashCache_Deinit(); + + //disable ccmu + HAL_Flashc_DisableCCMU(); + + return HAL_OK; +} + +/** + * @internal + * @brief Flash controller IBUS (XIP) Enable without Pin initialization. + * @note Most for Flash controller SBUS. It will resume system schedule. + * @param None + * @retval None + */ +void HAL_Flashc_Xip_RawEnable() +{ + if (!xip_on) + return; + +// HAL_UDelay(100); +// FC_Ibus_Enable(FC_EN_IBUS | xip_continue); + + FC_Ibus_Enable(xip_continue); + OS_ThreadResumeScheduler(); +} + +/** + * @internal + * @brief Flash controller IBUS (XIP) Enable with Pin initialization. + * @note Most for SPI. It will resume system schedule. + * @param None + * @retval None + */ +void HAL_Flashc_Xip_Enable() +{ + /* open io */ + HAL_BoardIoctl(HAL_BIR_PINMUX_INIT, HAL_MKDEV(HAL_DEV_MAJOR_FLASHC, 0), 0); + HAL_Flashc_Xip_RawEnable(); +} + +/** + * @internal + * @brief Flash controller IBUS (XIP) Enable without Pin deinitialization. + * @note Most for Flash controller SBUS. It will suspend system schedule. + * @param None + * @retval None + */ +void HAL_Flashc_Xip_RawDisable() +{ + if (!xip_on) + return; + + OS_ThreadSuspendScheduler(); +// HAL_UDelay(100); +// FC_Ibus_Disable(FC_EN_IBUS | xip_continue); + while((FC_Sbus_GetDebugState() != 0x0c) && (FC_Sbus_GetDebugState() != 0x00)); + FC_Ibus_Disable(xip_continue); +} + +/** + * @internal + * @brief Flash controller IBUS (XIP) Enable with Pin deinitialization. + * @note Most for SPI. It will suspend system schedule. + * @param None + * @retval None + */ +void HAL_Flashc_Xip_Disable() +{ + HAL_Flashc_Xip_RawDisable(); + + //close io + HAL_BoardIoctl(HAL_BIR_PINMUX_DEINIT, HAL_MKDEV(HAL_DEV_MAJOR_FLASHC, 0), 0); +} + + +/** + * @brief Delay realization in Flash controller IBUS (XIP). + * @note Delay can be system sleep while it's not in XIP, but must be a while + * delay without system interface while it's in XIP. + * @param us: delay time in microsecond. + * @retval None + */ +void HAL_XIP_Delay(unsigned int us) +{ + if (us == 0) + return; + + if (xip_on) + { + HAL_UDelay(us); + } + else + { + us += 1023; + unsigned int ms = us >> 10; + HAL_MSleep(ms); + } +} + +/** + * @brief Initialize Flash controller SBUS. + * @param cfg: + * @arg cfg->freq: Flash working frequency. + * @retval HAL_Status: The status of driver. + */ +HAL_Status HAL_Flashc_Init(const Flashc_Config *cfg) +{ + /* enable ccmu */ + HAL_Flashc_ResetCCMU(); + HAL_Flashc_ConfigCCMU(cfg->freq); + HAL_Flashc_EnableCCMU(); + + /* config flash controller */ + Flash_Ctrl_DelayCycle delay = {1, 0, 3, 0, 0, 0, 1}; + /*delay.cs_deselect = cfg->t_shsl_ns * (cfg->freq / 1000000) / 1000;*/ + delay.data = FC_GetDataDelay(cfg->freq); + FC_Sbus_TransmitDelay(&delay); + + FC_SetFlash(FC_TCTRL_CS_LOW_ENABLE, FC_TCTRL_FBS_MSB, FC_SCLK_Mode0); + FC_Sbus_ResetFIFO(1, 1); + + FC_DEBUG("ccmu : %d", ccmu_on); + FC_REG_ALL(); + + HAL_Flashc_DisableCCMU(); + +#ifdef CONFIG_PM + if (!hal_flashc_suspending) + { + HAL_Memcpy(&pm_sbus_cfg, cfg, sizeof(pm_sbus_cfg)); + pm_register_ops(&flashc_dev); + } +#endif + + return HAL_OK; +} + +/** + * @brief Deinitialize Flash controller SBUS. + * @param None + * @retval HAL_Status: The status of driver. + */ +HAL_Status HAL_Flashc_Deinit() +{ +#ifdef CONFIG_PM + if (!hal_flashc_suspending) + pm_unregister_ops(&flashc_dev); +#endif + + return HAL_OK; +} + +/** + * @brief Open flash controller SBUS. + * @note At the same time, it will disable XIP and suspend schedule. + * @param None + * @retval HAL_Status: The status of driver. + */ +HAL_Status HAL_Flashc_Open() +{ + sbusing = 1; + HAL_Flashc_Xip_RawDisable(); + + HAL_Flashc_EnableCCMU(); + HAL_Flashc_PinInit(); + FC_Sbus_ResetFIFO(1, 1); + + return HAL_OK; +} + +/** + * @brief Close flash controller SBUS. + * @param None + * @retval HAL_Status: The status of driver. + */ +HAL_Status HAL_Flashc_Close() +{ + HAL_Flashc_PinDeinit(); + HAL_Flashc_DisableCCMU(); + + HAL_Flashc_Xip_RawEnable(); + sbusing = 0; + return HAL_OK; +} + +/** + * @brief Flash controller ioctl. + * @note op : arg + * nothing support for now. + * @param op: ioctl command. + * @param arg: ioctl arguement + * @retval HAL_Status: The status of driver. + */ +HAL_Status HAL_Flashc_Control(Flashc_Commands op, void *arg) +{ + /*TODO: tbc...*/ + + return HAL_INVALID; +} + +/** + * @brief Write flash by flash controller SBUS. + * @note Send a instruction in command + address + dummy + write data. + * @param cmd: Command of instruction. + * @arg cmd->pdata: The data is filled with in this field. + * @arg cmd->len: The data len of this field. + * @arg cmd->line: The number of line transfering this field data. + * @param addr: Address of instruction + * @param dummy: Dummy of instruction + * @param data: Data of instruction + * @param dma: Transfer data by DMA or not. + * @retval HAL_Status: The status of driver. + */ +HAL_Status HAL_Flashc_Write(FC_InstructionField *cmd, FC_InstructionField *addr, FC_InstructionField *dummy, FC_InstructionField *data, bool dma) +{ + HAL_Status ret; + FC_InstructionField zero; + HAL_Memset(&zero, 0, sizeof(zero)); + + /* instruction check */ + if (cmd == NULL) + cmd = &zero; + if (addr == NULL) + addr = &zero; + if (dummy == NULL) + dummy = &zero; + if (data == NULL) + data = &zero; + + FC_Sbus_ResetFIFO(1, 1); + FC_Sbus_CommandConfig(cmd->line, addr->line, dummy->line, data->line, dummy->len); + FC_Sbus_Command(*(cmd->pdata), *((uint32_t *)addr->pdata), 0, 0); + FC_Sbus_WriteSize(data->len); + + if (dma == 1 && data->len != 0 && !xip_on) + ret = HAL_Flashc_DMAWrite(data->pdata, data->len); + else + ret = HAL_Flashc_PollWrite(data->pdata, data->len); + + if (ret != HAL_OK) + FC_ERROR("error occured on cmd: 0x%x, data len: %d", *cmd->pdata, data->len); + + return ret; +} + +/** + * @brief Read flash by flash controller SBUS. + * @note Send a instruction in command + address + dummy + read data. + * @param cmd: Command of instruction. + * @arg cmd->pdata: The data is filled with in this field. + * @arg cmd->len: The data len of this field. + * @arg cmd->line: The number of line transfering this field data. + * @param addr: Address of instruction + * @param dummy: Dummy of instruction + * @param data: Data of instruction + * @param dma: Transfer data by DMA or not. + * @retval HAL_Status: The status of driver. + */ +HAL_Status HAL_Flashc_Read(FC_InstructionField *cmd, FC_InstructionField *addr, FC_InstructionField *dummy, FC_InstructionField *data, bool dma) +{ + HAL_Status ret; + FC_InstructionField zero; + HAL_Memset(&zero, 0, sizeof(zero)); + + /* instruction check */ + if (cmd == NULL) + cmd = &zero; + if (addr == NULL) + addr = &zero; + if (dummy == NULL) + dummy = &zero; + if (data == NULL) + data = &zero; + + FC_Sbus_ResetFIFO(1, 1); + FC_Sbus_CommandConfig(cmd->line, addr->line, dummy->line, data->line, dummy->len); + FC_Sbus_Command(*(cmd->pdata), *((uint32_t *)addr->pdata), 0, 0); + FC_Sbus_ReadSize(data->len); + + if (dma == 1 && data->len != 0 && !xip_on) + ret = HAL_Flashc_DMARead(data->pdata, data->len); + else + ret = HAL_Flashc_PollRead(data->pdata, data->len); + + if (ret != HAL_OK) + FC_ERROR("error occured on cmd: 0x%x, data len: %d", *cmd->pdata, data->len); + + return ret; +} + +HAL_Semaphore dmaSem; + +static void HAL_Flashc_DMARelease(void *arg) +{ + HAL_SemaphoreRelease(&dmaSem); +} + +static HAL_Status HAL_Flashc_DMAWrite(uint8_t *data, uint32_t size) +{ + HAL_Status ret = HAL_OK; + DMA_ChannelInitParam dma_arg; + DMA_Channel dma_ch; + HAL_Memset(&dma_arg, 0, sizeof(dma_arg)); + HAL_Memset(&dma_ch, 0, sizeof(dma_ch)); + HAL_Memset(&dmaSem, 0, sizeof(dmaSem)); + + if (size == 0 || data == NULL) + return HAL_ERROR; + + if ((dma_ch = HAL_DMA_Request()) == DMA_CHANNEL_INVALID) { + FC_ERROR("DMA request failed"); + ret = HAL_BUSY; + goto failed; + } + + HAL_SemaphoreInit(&dmaSem, 0, 1); + + dma_arg.irqType = DMA_IRQ_TYPE_END; + dma_arg.endCallback = HAL_Flashc_DMARelease; + dma_arg.endArg = NULL; + dma_arg.cfg = HAL_DMA_MakeChannelInitCfg(DMA_WORK_MODE_SINGLE, + DMA_WAIT_CYCLE_2, + DMA_BYTE_CNT_MODE_REMAIN, + DMA_DATA_WIDTH_8BIT, + DMA_BURST_LEN_1, + DMA_ADDR_MODE_FIXED, + (DMA_Periph)(DMA_PERIPH_FLASHC), + DMA_DATA_WIDTH_8BIT, + DMA_BURST_LEN_1, + DMA_ADDR_MODE_INC, + DMA_PERIPH_SRAM); + HAL_DMA_Init(dma_ch, &dma_arg); + HAL_DMA_Start(dma_ch, (uint32_t)data, (uint32_t)&FLASH_CTRL->S_WDATA, size); + + FC_Sbus_StartSend(); + if ((ret = HAL_SemaphoreWait(&dmaSem, 5000)) != HAL_OK) + FC_ERROR("sem wait failed: %d", ret); + + uint32_t i; +// FC_WHILE_TIMEOUT(FC_Sbus_GetStatus(FC_INT_TC) == 0, i); + FC_WHILE_TIMEOUT(FC_Sbus_isSending(), i); + FC_Sbus_ClrStatus(FC_INT_TC); + + HAL_DMA_Stop(dma_ch); + HAL_DMA_DeInit(dma_ch); + HAL_DMA_Release(dma_ch); + + HAL_SemaphoreDeinit(&dmaSem); + + if (FC_DebugCheck(0)) + return HAL_ERROR; + +failed: + return ret; +} + +static HAL_Status HAL_Flashc_PollWrite(uint8_t *data, uint32_t size) +{ + uint32_t wsize = size; + uint32_t i; + + FC_Sbus_StartSend(); + + while (wsize--) { + FC_WHILE_TIMEOUT(FC_Sbus_GetFIFOCnt(FC_SBUS_WRITE) > 100, i); + FC_Sbus_Write(*(data++)); + } + +// FC_WHILE_TIMEOUT(FC_Sbus_GetDebugState() != 0, i); +// FC_WHILE_TIMEOUT(FC_Sbus_GetStatus(FC_INT_TC) == 0, i); + FC_WHILE_TIMEOUT(FC_Sbus_isSending(), i); + FC_Sbus_ClrStatus(FC_INT_TC); + + if (FC_DebugCheck(0)) + return HAL_ERROR; + + return HAL_OK; +} + +static HAL_Status HAL_Flashc_DMARead(uint8_t *data, uint32_t size) +{ + HAL_Status ret = HAL_OK; + DMA_ChannelInitParam dma_arg; + DMA_Channel dma_ch; + HAL_Memset(&dma_arg, 0, sizeof(dma_arg)); + HAL_Memset(&dma_ch, 0, sizeof(dma_ch)); + HAL_Memset(&dmaSem, 0, sizeof(dmaSem)); + + if (size == 0 || data == NULL) + return HAL_ERROR; + + if ((dma_ch = HAL_DMA_Request()) == DMA_CHANNEL_INVALID) { + FC_ERROR("DMA request failed"); + ret = HAL_BUSY; + goto failed; + } + + HAL_SemaphoreInit(&dmaSem, 0, 1); + + dma_arg.irqType = DMA_IRQ_TYPE_END; + dma_arg.endCallback = HAL_Flashc_DMARelease; + dma_arg.endArg = NULL; + dma_arg.cfg = HAL_DMA_MakeChannelInitCfg(DMA_WORK_MODE_SINGLE, + DMA_WAIT_CYCLE_2, + DMA_BYTE_CNT_MODE_REMAIN, + DMA_DATA_WIDTH_8BIT, + DMA_BURST_LEN_1, + DMA_ADDR_MODE_INC, + DMA_PERIPH_SRAM, + DMA_DATA_WIDTH_8BIT, + DMA_BURST_LEN_1, + DMA_ADDR_MODE_FIXED, + (DMA_Periph)(DMA_PERIPH_FLASHC)); + HAL_DMA_Init(dma_ch, &dma_arg); + HAL_DMA_Start(dma_ch, (uint32_t)&FLASH_CTRL->S_RDATA, (uint32_t)data, size); + + FC_Sbus_StartSend(); + if ((ret = HAL_SemaphoreWait(&dmaSem, 5000)) != HAL_OK) + FC_ERROR("sem wait failed: %d", ret); + + uint32_t i; +// FC_WHILE_TIMEOUT(FC_Sbus_GetStatus(FC_INT_TC) == 0, i); + FC_WHILE_TIMEOUT(FC_Sbus_isSending(), i); + FC_Sbus_ClrStatus(FC_INT_TC); + + HAL_DMA_Stop(dma_ch); + HAL_DMA_DeInit(dma_ch); + HAL_DMA_Release(dma_ch); + + HAL_SemaphoreDeinit(&dmaSem); + + if (FC_DebugCheck(0)) + return HAL_ERROR; + +failed: + return ret; +} + +static HAL_Status HAL_Flashc_PollRead(uint8_t *data, uint32_t size) +{ + uint32_t rsize = size; + uint32_t i; + + FC_Sbus_StartSend(); + + while (rsize--) + { + FC_WHILE_TIMEOUT(FC_Sbus_GetFIFOCnt(FC_SBUS_READ) == 0, i); + *(data++) = FC_Sbus_Read(); + } + +// FC_WHILE_TIMEOUT(FC_Sbus_GetDebugState() != 0, i); +// FC_WHILE_TIMEOUT(FC_Sbus_GetStatus(FC_INT_TC) == 0, i); + FC_WHILE_TIMEOUT(FC_Sbus_isSending(), i); + FC_Sbus_ClrStatus(FC_INT_TC); + + if (FC_DebugCheck(0)) + return HAL_ERROR; + + return HAL_OK; +} + + +#ifdef CONFIG_PM +static int flashc_suspend(struct soc_device *dev, enum suspend_state_t state) +{ + /* + suspend condition: + (1) not in sbus opened state + (2) + */ + hal_flashc_suspending = 1; + + if (sbusing) + return -1; + + while((FC_Sbus_GetDebugState() != 0x0c) && (FC_Sbus_GetDebugState() != 0x00)); + + switch (state) { + case PM_MODE_SLEEP: + FC_Ibus_Disable(xip_continue); + HAL_CCM_BusDisablePeriphClock(CCM_BUS_PERIPH_BIT_FLASHC); + HAL_CCM_FLASHC_DisableMClock(); + break; + case PM_MODE_STANDBY: + if (xip_on) + { + HAL_Flashc_Xip_Deinit(); + FC_DEBUG("ccmu : %d", ccmu_on); + pm_xip = 1; + } + HAL_Flashc_Deinit(); + FC_DEBUG("ccmu : %d", ccmu_on); + break; + case PM_MODE_HIBERNATION: + case PM_MODE_POWEROFF: + + break; + default: + break; + } + + FC_REG_ALL(); + return 0; +} + +static int flashc_resume(struct soc_device *dev, enum suspend_state_t state) +{ + + FC_REG_ALL(); + + switch (state) { + case PM_MODE_SLEEP: + FC_Ibus_Enable(xip_continue); + HAL_CCM_BusEnablePeriphClock(CCM_BUS_PERIPH_BIT_FLASHC); + HAL_CCM_FLASHC_EnableMClock(); + break; + case PM_MODE_STANDBY: + HAL_Flashc_Init(&pm_sbus_cfg); + if (pm_xip) + { + pm_xip = 0; + HAL_Flashc_Xip_Init(&pm_ibus_cfg); + HAL_UDelay(300); + } + FC_DEBUG("ccmu: %d, pin: %d", ccmu_on, pin_inited); + break; + + case PM_MODE_HIBERNATION: + case PM_MODE_POWEROFF: + + break; + default: + break; + } + + hal_flashc_suspending = 0; + + return 0; +} + +static struct soc_device_driver flashc_drv = { + .name = "flashc", + .suspend_noirq = flashc_suspend, + .resume_noirq = flashc_resume, +}; + +static struct soc_device flashc_dev = { + .name = "flashc", + .driver = &flashc_drv, +}; +#endif + diff --git a/platform/mcu/xr871/src/driver/chip/hal_global.c b/platform/mcu/xr871/src/driver/chip/hal_global.c new file mode 100644 index 0000000000..fddb47bdb7 --- /dev/null +++ b/platform/mcu/xr871/src/driver/chip/hal_global.c @@ -0,0 +1,46 @@ +/** + * @file hal_global.c + * @author XRADIO IOT WLAN Team + */ + +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "hal_base.h" + +/** + * @brief Global initialization for HAL module + * @return None + */ +void HAL_GlobalInit(void) +{ + HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_DEFAULT); + HAL_CCM_BusForceAllPeriphReset(); + HAL_CCM_BusDisableAllPeriphClock(); +} diff --git a/platform/mcu/xr871/src/driver/chip/hal_gpio.c b/platform/mcu/xr871/src/driver/chip/hal_gpio.c new file mode 100644 index 0000000000..16bbeaf9b9 --- /dev/null +++ b/platform/mcu/xr871/src/driver/chip/hal_gpio.c @@ -0,0 +1,423 @@ +/** + * @file hal_gpio.c + * @author XRADIO IOT WLAN Team + */ + +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "driver/chip/hal_gpio.h" +#include "hal_base.h" + +/* + * register bits of GPIO_CTRL_T for each GPIO pin + */ +#define GPIO_CTRL_MODE_BITS 4 +#define GPIO_CTRL_MODE_MASK 0xFU +#define GPIO_CTRL_MODE_MAX GPIOx_Pn_F7_DISABLE + +#define GPIO_CTRL_DATA_BITS 1 + +#define GPIO_CTRL_DRIVING_BITS 2 +#define GPIO_CTRL_DRIVING_MASK 0x3U +#define GPIO_CTRL_DRIVING_MAX GPIO_DRIVING_LEVEL_3 + +#define GPIO_CTRL_PULL_BITS 2 +#define GPIO_CTRL_PULL_MASK 0x3U +#define GPIO_CTRL_PULL_MAX GPIO_PULL_DOWN + +/* + * register bits of GPIO_IRQ_T for each GPIO pin + */ +#define GPIO_IRQ_EVT_BITS 4 +#define GPIO_IRQ_EVT_MASK 0xFU +#define GPIO_IRQ_EVT_MAX GPIO_IRQ_EVT_BOTH_EDGE + +#define GPIO_IRQ_EN_BITS 1 + +#define GPIO_IRQ_STAUTS_BITS 1 + +/* useful macros */ +#define GPIO_PINS_MASK(pinNum) ((1U << pinNum) - 1) + +#define GPIO_REG_BITS 32 +#define GPIO_GET_REG_IDX_SHIFT(idx, shift, pin, bits) \ + do { \ + (shift) = (pin) * (bits); \ + (idx) = (shift) / GPIO_REG_BITS; \ + (shift) = (shift) % GPIO_REG_BITS; \ + } while (0) + +typedef struct { + GPIO_IRQCallback callback; + void *arg; +} GPIO_Private; + +static GPIO_Private gGPIOAPrivate[GPIOA_PIN_NUM]; +static GPIO_Private gGPIOBPrivate[GPIOB_PIN_NUM]; + +static uint8_t gGPIOUsedCnt = 0; + +#define GPIOA_CTRL ((GPIO_CTRL_T *)GPIOA_CTRL_BASE) +#define GPIOB_CTRL ((GPIO_CTRL_T *)GPIOB_CTRL_BASE) + +#define GPIOA_IRQ ((GPIO_IRQ_T *)GPIOA_IRQ_BASE) +#define GPIOB_IRQ ((GPIO_IRQ_T *)GPIOB_IRQ_BASE) + +static GPIO_CTRL_T * const gGPIOPortCtrl[GPIO_PORT_NUM] = { GPIOA_CTRL, GPIOB_CTRL }; +static GPIO_IRQ_T * const gGPIOPortIRQ[GPIO_PORT_NUM] = { GPIOA_IRQ, GPIOB_IRQ }; + + +__STATIC_INLINE GPIO_CTRL_T *GPIO_GetCtrlInstance(GPIO_Port port) +{ + return gGPIOPortCtrl[port]; +} + +__STATIC_INLINE GPIO_IRQ_T *GPIO_GetIRQInstance(GPIO_Port port) +{ + return gGPIOPortIRQ[port]; +} + +/* + * IRQ handling + */ +static void GPIO_IRQHandler(GPIO_IRQ_T *gpiox, uint32_t pinNum, uint32_t pinMask, GPIO_Private *priv) +{ + uint32_t i; + uint32_t irqStatus; + uint32_t isPending; + + irqStatus = gpiox->IRQ_STATUS & gpiox->IRQ_EN & pinMask; /* get pending bits */ + gpiox->IRQ_STATUS = irqStatus; /* clear pending bits */ + + for (i = GPIO_PIN_0; i < pinNum && irqStatus != 0; ++i) { + isPending = irqStatus & HAL_BIT(0); + if (isPending && priv[i].callback) { + priv[i].callback(priv[i].arg); + } + irqStatus >>= 1; + } +} + +void GPIOA_IRQHandler(void) +{ + GPIO_IRQHandler(GPIOA_IRQ, GPIOA_PIN_NUM, GPIO_PINS_MASK(GPIOA_PIN_NUM), gGPIOAPrivate); +} + +void GPIOB_IRQHandler(void) +{ + GPIO_IRQHandler(GPIOB_IRQ, GPIOB_PIN_NUM, GPIO_PINS_MASK(GPIOB_PIN_NUM), gGPIOBPrivate); +} + +static void GPIO_EnableIRQ(GPIO_IRQ_T *gpiox, GPIO_Pin pin) +{ + HAL_SET_BIT(gpiox->IRQ_EN, HAL_BIT(pin)); +} + +static void GPIO_DisableIRQ(GPIO_IRQ_T *gpiox, GPIO_Pin pin) +{ + HAL_CLR_BIT(gpiox->IRQ_EN, HAL_BIT(pin)); +} + +static int GPIO_IsPendingIRQ(GPIO_IRQ_T *gpiox, GPIO_Pin pin) +{ + return HAL_GET_BIT_VAL(gpiox->IRQ_STATUS, pin, 1); +} + +static void GPIO_ClearPendingIRQ(GPIO_IRQ_T *gpiox, GPIO_Pin pin) +{ + HAL_SET_BIT(gpiox->IRQ_STATUS, HAL_BIT(pin)); +} + +void HAL_GPIO_EnableIRQ(GPIO_Port port, GPIO_Pin pin, const GPIO_IrqParam *param) +{ + uint32_t regIdx; + uint32_t bitShift; + GPIO_IRQ_T *gpiox; + GPIO_Private *gpioPriv; + IRQn_Type IRQn; + unsigned long flags; + + flags = HAL_EnterCriticalSection(); + + if (port == GPIO_PORT_A) { + gpioPriv = gGPIOAPrivate; + IRQn = GPIOA_IRQn; + } else if (port == GPIO_PORT_B) { + gpioPriv = gGPIOBPrivate; + IRQn = GPIOB_IRQn; + } else { + HAL_ERR("Invalid port %d for IRQ\n", port); + return; + } + gpiox = GPIO_GetIRQInstance(port); + + /* set callback */ + gpioPriv[pin].callback = param->callback; + gpioPriv[pin].arg = param->arg; + + /* set IRQ trigger mode */ + GPIO_GET_REG_IDX_SHIFT(regIdx, bitShift, pin, GPIO_IRQ_EVT_BITS); + HAL_MODIFY_REG(gpiox->IRQ_MODE[regIdx], + GPIO_IRQ_EVT_MASK << bitShift, + (param->event & GPIO_IRQ_EVT_MASK) << bitShift); + + if (GPIO_IsPendingIRQ(gpiox, pin)) { + GPIO_ClearPendingIRQ(gpiox, pin); + } + GPIO_EnableIRQ(gpiox, pin); + HAL_NVIC_SetPriority(IRQn, NVIC_PERIPHERAL_PRIORITY_DEFAULT); + HAL_NVIC_EnableIRQ(IRQn); + + HAL_ExitCriticalSection(flags); +} + +void HAL_GPIO_DisableIRQ(GPIO_Port port, GPIO_Pin pin) +{ + GPIO_IRQ_T *gpiox; + GPIO_Private *gpioPriv; + IRQn_Type IRQn; + unsigned long flags; + + flags = HAL_EnterCriticalSection(); + + if (port == GPIO_PORT_A) { + gpioPriv = gGPIOAPrivate; + IRQn = GPIOA_IRQn; + } else if (port == GPIO_PORT_B) { + gpioPriv = gGPIOBPrivate; + IRQn = GPIOB_IRQn; + } else { + HAL_ERR("Invalid port %d for IRQ\n", port); + return; + } + gpiox = GPIO_GetIRQInstance(port); + + HAL_NVIC_DisableIRQ(IRQn); + GPIO_DisableIRQ(gpiox, pin); + if (GPIO_IsPendingIRQ(gpiox, pin)) { + GPIO_ClearPendingIRQ(gpiox, pin); + } + + gpioPriv[pin].callback = NULL; + gpioPriv[pin].arg = NULL; + + HAL_ExitCriticalSection(flags); +} + +/** + * @brief Initialize the specified GPIO + * @param[in] port GPIO port + * @param[in] pin GPIO pin number + * @param[in] param Pointer to GPIO_InitParam structure + * @return None + */ +void HAL_GPIO_Init(GPIO_Port port, GPIO_Pin pin, const GPIO_InitParam *param) +{ + uint32_t regIdx; + uint32_t bitShift; + GPIO_CTRL_T *gpiox; + unsigned long flags; + +#if 0 + HAL_ASSERT_PARAM(pin <= GPIO_PIN_MAX); + HAL_ASSERT_PARAM(param->mode <= GPIO_CTRL_MODE_MAX); + HAL_ASSERT_PARAM(param->driving <= GPIO_CTRL_DRIVING_MAX); + HAL_ASSERT_PARAM(param->pull <= GPIO_CTRL_PULL_MAX); +#endif + + flags = HAL_EnterCriticalSection(); + + if (gGPIOUsedCnt++ == 0) { + HAL_CCM_BusEnablePeriphClock(CCM_BUS_PERIPH_BIT_GPIO); + } + + gpiox = GPIO_GetCtrlInstance(port); + + /* set working mode (function) */ + GPIO_GET_REG_IDX_SHIFT(regIdx, bitShift, pin, GPIO_CTRL_MODE_BITS); + HAL_MODIFY_REG(gpiox->MODE[regIdx], + GPIO_CTRL_MODE_MASK << bitShift, + (param->mode & GPIO_CTRL_MODE_MASK) << bitShift); + + /* set driving */ + GPIO_GET_REG_IDX_SHIFT(regIdx, bitShift, pin, GPIO_CTRL_DRIVING_BITS); + HAL_MODIFY_REG(gpiox->DRIVING[regIdx], + GPIO_CTRL_DRIVING_MASK << bitShift, + (param->driving & GPIO_CTRL_DRIVING_MASK) << bitShift); + + /* set pull */ + GPIO_GET_REG_IDX_SHIFT(regIdx, bitShift, pin, GPIO_CTRL_PULL_BITS); + HAL_MODIFY_REG(gpiox->PULL[regIdx], + GPIO_CTRL_PULL_MASK << bitShift, + (param->pull & GPIO_CTRL_PULL_MASK) << bitShift); + + HAL_ExitCriticalSection(flags); +} + +/** + * @brief Deinitialize the specified GPIO + * @param[in] port GPIO port + * @param[in] pin GPIO pin number + * @return None + * @note After deinitialization, the GPIO is in its reset state: + * (GPIOx_Pn_F7_DISABLE, GPIO_DRIVING_LEVEL_1, GPIO_PULL_NONE). + */ +void HAL_GPIO_DeInit(GPIO_Port port, GPIO_Pin pin) +{ + uint32_t regIdx; + uint32_t bitShift; + GPIO_CTRL_T *gpiox; + unsigned long flags; + + flags = HAL_EnterCriticalSection(); + + gpiox = GPIO_GetCtrlInstance(port); + + /* set working mode (function) to disable */ + GPIO_GET_REG_IDX_SHIFT(regIdx, bitShift, pin, GPIO_CTRL_MODE_BITS); + HAL_MODIFY_REG(gpiox->MODE[regIdx], + GPIO_CTRL_MODE_MASK << bitShift, + (GPIOx_Pn_F7_DISABLE & GPIO_CTRL_MODE_MASK) << bitShift); + + /* set driving to default value (GPIO_DRIVING_LEVEL_1) */ + GPIO_GET_REG_IDX_SHIFT(regIdx, bitShift, pin, GPIO_CTRL_DRIVING_BITS); + HAL_MODIFY_REG(gpiox->DRIVING[regIdx], + GPIO_CTRL_DRIVING_MASK << bitShift, + (GPIO_DRIVING_LEVEL_1 & GPIO_CTRL_DRIVING_MASK) << bitShift); + + /* set pull to default value (GPIO_PULL_NONE) */ + GPIO_GET_REG_IDX_SHIFT(regIdx, bitShift, pin, GPIO_CTRL_PULL_BITS); + HAL_MODIFY_REG(gpiox->PULL[regIdx], + GPIO_CTRL_PULL_MASK << bitShift, + (GPIO_PULL_NONE & GPIO_CTRL_PULL_MASK) << bitShift); + + if ((gGPIOUsedCnt > 0) && (--gGPIOUsedCnt == 0)) { + HAL_CCM_BusDisablePeriphClock(CCM_BUS_PERIPH_BIT_GPIO); + } + + HAL_ExitCriticalSection(flags); +} + +/** + * @brief Set the state of the specified GPIO + * @param[in] port GPIO port + * @param[in] pin GPIO pin number + * @param[in] state GPIO pin state + * @return None + */ +void HAL_GPIO_WritePin(GPIO_Port port, GPIO_Pin pin, GPIO_PinState state) +{ + GPIO_CTRL_T *gpiox; + unsigned long flags; + + gpiox = GPIO_GetCtrlInstance(port); + + flags = HAL_EnterCriticalSection(); + + if (state == GPIO_PIN_LOW) + HAL_CLR_BIT(gpiox->DATA, HAL_BIT(pin)); + else + HAL_SET_BIT(gpiox->DATA, HAL_BIT(pin)); + + HAL_ExitCriticalSection(flags); +} + +/** + * @brief Get the state of the specified GPIO + * @param[in] port GPIO port + * @param[in] pin GPIO pin number + * @return GPIO pin state + */ +GPIO_PinState HAL_GPIO_ReadPin(GPIO_Port port, GPIO_Pin pin) +{ + GPIO_CTRL_T *gpiox; + + gpiox = GPIO_GetCtrlInstance(port); + return (GPIO_PinState)HAL_GET_BIT_VAL(gpiox->DATA, pin, 1); +} + +/** + * @brief Set the state of the specified GPIO port + * @param[in] port GPIO port + * @param[in] portMask GPIO port state, bit mask of all pins + * @return None + */ +void HAL_GPIO_WritePort(GPIO_Port port, uint32_t portMask) +{ + GPIO_CTRL_T *gpiox; + + gpiox = GPIO_GetCtrlInstance(port); + gpiox->DATA = portMask; +} + +/** + * @brief Get the state of the specified GPIO port + * @param[in] port GPIO port + * @return GPIO port state, bit mask of all pins + */ +uint32_t HAL_GPIO_ReadPort(GPIO_Port port) +{ + GPIO_CTRL_T *gpiox; + + gpiox = GPIO_GetCtrlInstance(port); + return gpiox->DATA; +} + +/** + * @brief Configure the GPIOs pinmux by the specified parameters + * @param[in] param Pointer to the array of GPIO_PinMuxParam structure, one + * array element for one GPIO pinmux + * @param[in] count Elements number of the GPIO pinmux parameters array + * @return None + */ +void HAL_GPIO_PinMuxConfig(const GPIO_PinMuxParam *param, uint32_t count) +{ + uint32_t i; + + for (i = 0; i < count; ++i) { + HAL_GPIO_Init(param[i].port, param[i].pin, ¶m[i].config); + } +} + +/** + * @brief Deconfigure the GPIOs pinmux by the specified parameters + * @param[in] param Pointer to the array of GPIO_PinMuxParam structure, one + * array element for one GPIO pinmux, param->config is ignored. + * @param[in] count Elements number of the GPIO pinmux parameters array + * @return None + */ +void HAL_GPIO_PinMuxDeConfig(const GPIO_PinMuxParam *param, uint32_t count) +{ + uint32_t i; + + for (i = 0; i < count; ++i) { + HAL_GPIO_DeInit(param[i].port, param[i].pin); + } +} diff --git a/platform/mcu/xr871/src/driver/chip/hal_i2c.c b/platform/mcu/xr871/src/driver/chip/hal_i2c.c new file mode 100644 index 0000000000..37b36837a0 --- /dev/null +++ b/platform/mcu/xr871/src/driver/chip/hal_i2c.c @@ -0,0 +1,909 @@ +/** + * @file hal_i2c.c + * @author XRADIO IOT WLAN Team + */ + +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "driver/chip/hal_i2c.h" +#include "hal_base.h" +#include "pm/pm.h" + +#define I2C_MTX_TIMEOUT_MS (5000) +#define I2C_SEM_TIMEOUT_MS (3000) + +/* I2C_Private.ctrl */ +#define I2C_INIT_STATE_BIT HAL_BIT(0) +#define I2C_7BIT_ADDR_BIT HAL_BIT(1) +#define I2C_READ_MODE_BIT HAL_BIT(2) +#define I2C_SCCB_MODE_BIT HAL_BIT(3) +#define I2C_MEM_MODE_BIT HAL_BIT(4) +#define I2C_RESTART_BIT HAL_BIT(5) + +typedef struct { + uint8_t ctrl; + + uint8_t memAddr; + uint16_t devAddr; + uint8_t *buf; + int32_t size; + + HAL_Mutex mtx; + HAL_Semaphore sem; +} I2C_Private; + +static I2C_Private gI2CPrivate[I2C_NUM]; +static I2C_T *gI2CInstance[I2C_NUM] = {I2C0, I2C1}; + +#define I2C_ASSERT_ID(i2cID) HAL_ASSERT_PARAM((i2cID) < I2C_NUM) + +#ifdef CONFIG_PM +static I2C_InitParam hal_i2c_param[I2C_NUM]; +static uint8_t hal_i2c_suspending = 0; + +static int i2c_suspend(struct soc_device *dev, enum suspend_state_t state) +{ + I2C_ID i2cID = (I2C_ID)dev->platform_data; + + hal_i2c_suspending |= (1 << i2cID); + + switch (state) { + case PM_MODE_SLEEP: + case PM_MODE_STANDBY: + case PM_MODE_HIBERNATION: + case PM_MODE_POWEROFF: + HAL_I2C_DeInit(i2cID); + break; + default: + break; + } + + return 0; +} + +static int i2c_resume(struct soc_device *dev, enum suspend_state_t state) +{ + I2C_ID i2cID = (I2C_ID)dev->platform_data; + + switch (state) { + case PM_MODE_SLEEP: + case PM_MODE_STANDBY: + case PM_MODE_HIBERNATION: + HAL_I2C_Init(i2cID, &hal_i2c_param[i2cID]); + break; + default: + break; + } + + hal_i2c_suspending &= ~(1 << i2cID); + + return 0; +} + +static struct soc_device_driver i2c_drv = { + .name = "i2c", + .suspend = i2c_suspend, + .resume = i2c_resume, +}; + +static struct soc_device i2c_dev[] = { + {.name = "i2c0", .driver = &i2c_drv, .platform_data = (void *)I2C0_ID,}, + {.name = "i2c1", .driver = &i2c_drv, .platform_data = (void *)I2C1_ID,}, +}; + +#define I2C_DEV(id) (&i2c_dev[id]) +#else +#define I2C_DEV(id) NULL +#endif + +__STATIC_INLINE I2C_T *I2C_GetI2CInstance(I2C_ID i2cID) +{ + return gI2CInstance[i2cID]; +} + +__STATIC_INLINE I2C_Private *I2C_GetI2CPriv(I2C_ID i2cID) +{ + return &gI2CPrivate[i2cID]; +} + +__STATIC_INLINE void I2C_SetInitStateBit(I2C_Private *priv) +{ + HAL_SET_BIT(priv->ctrl, I2C_INIT_STATE_BIT); +} + +__STATIC_INLINE void I2C_ClrInitStateBit(I2C_Private *priv) +{ + HAL_CLR_BIT(priv->ctrl, I2C_INIT_STATE_BIT); +} + +__STATIC_INLINE uint8_t I2C_IsInitState(I2C_Private *priv) +{ + return !!HAL_GET_BIT(priv->ctrl, I2C_INIT_STATE_BIT); +} + +__STATIC_INLINE void I2C_Set7BitAddrMode(I2C_Private *priv) +{ + HAL_SET_BIT(priv->ctrl, I2C_7BIT_ADDR_BIT); +} + +__STATIC_INLINE void I2C_Set10BitAddrMode(I2C_Private *priv) +{ + HAL_CLR_BIT(priv->ctrl, I2C_7BIT_ADDR_BIT); +} + +__STATIC_INLINE uint8_t I2C_Is7BitAddrMode(I2C_Private *priv) +{ + return !!HAL_GET_BIT(priv->ctrl, I2C_7BIT_ADDR_BIT); +} + +__STATIC_INLINE void I2C_SetReadMode(I2C_Private *priv) +{ + HAL_SET_BIT(priv->ctrl, I2C_READ_MODE_BIT); +} + +__STATIC_INLINE void I2C_SetWriteMode(I2C_Private *priv) +{ + HAL_CLR_BIT(priv->ctrl, I2C_READ_MODE_BIT); +} + +__STATIC_INLINE uint8_t I2C_IsReadMode(I2C_Private *priv) +{ + return !!HAL_GET_BIT(priv->ctrl, I2C_READ_MODE_BIT); +} + +__STATIC_INLINE void I2C_SetSCCBMode(I2C_Private *priv) +{ + HAL_SET_BIT(priv->ctrl, I2C_SCCB_MODE_BIT); +} + +__STATIC_INLINE void I2C_ClrSCCBMode(I2C_Private *priv) +{ + HAL_CLR_BIT(priv->ctrl, I2C_SCCB_MODE_BIT); +} + +__STATIC_INLINE uint8_t I2C_IsSCCBMode(I2C_Private *priv) +{ + return !!HAL_GET_BIT(priv->ctrl, I2C_SCCB_MODE_BIT); +} + +__STATIC_INLINE void I2C_SetMemMode(I2C_Private *priv) +{ + HAL_SET_BIT(priv->ctrl, I2C_MEM_MODE_BIT); +} + +__STATIC_INLINE void I2C_ClrMemMode(I2C_Private *priv) +{ + HAL_CLR_BIT(priv->ctrl, I2C_MEM_MODE_BIT); +} + +__STATIC_INLINE uint8_t I2C_IsMemMode(I2C_Private *priv) +{ + return !!HAL_GET_BIT(priv->ctrl, I2C_MEM_MODE_BIT); +} + +__STATIC_INLINE void I2C_SetRestartBit(I2C_Private *priv) +{ + HAL_SET_BIT(priv->ctrl, I2C_RESTART_BIT); +} + +__STATIC_INLINE void I2C_ClrRestartBit(I2C_Private *priv) +{ + HAL_CLR_BIT(priv->ctrl, I2C_RESTART_BIT); +} + +__STATIC_INLINE uint8_t I2C_IsRestart(I2C_Private *priv) +{ + return !!HAL_GET_BIT(priv->ctrl, I2C_RESTART_BIT); +} + +__STATIC_INLINE uint8_t I2C_Get7BitAddrRd(I2C_Private *priv) +{ + return (uint8_t)((priv->devAddr << 1) | 0x1); +} + +__STATIC_INLINE uint8_t I2C_Get7BitAddrWr(I2C_Private *priv) +{ + return (uint8_t)((priv->devAddr << 1) | 0x0); +} + +__STATIC_INLINE uint8_t I2C_Get10BitAddr1Rd(I2C_Private *priv) +{ + uint8_t tmp = (uint8_t)(priv->devAddr >> 7); + tmp &= ~(0x08U); + tmp |= 0xF1U; + return tmp; +} + +__STATIC_INLINE uint8_t I2C_Get10BitAddr1Wr(I2C_Private *priv) +{ + uint8_t tmp = (uint8_t)(priv->devAddr >> 7); + tmp &= ~(0x09U); + tmp |= 0xF0U; + return tmp; +} + +__STATIC_INLINE uint8_t I2C_Get10BitAddr2(I2C_Private *priv) +{ + return (uint8_t)(HAL_GET_BIT(priv->devAddr, 0xFFU)); +} + +__STATIC_INLINE uint8_t I2C_GetData(I2C_T *i2c) +{ + return (uint8_t)(HAL_GET_BIT(i2c->I2C_DATA, I2C_DATA_MASK)); +} + +__STATIC_INLINE void I2C_PutData(I2C_T *i2c, uint8_t data) +{ + i2c->I2C_DATA = data; +} + +__STATIC_INLINE void I2C_EnableIRQ(I2C_T *i2c) +{ + HAL_MODIFY_REG(i2c->I2C_CTRL, I2C_WR_CTRL_MASK, I2C_IRQ_EN_BIT); +} + +__STATIC_INLINE void I2C_DisableIRQ(I2C_T *i2c) +{ + HAL_CLR_BIT(i2c->I2C_CTRL, I2C_WR_CTRL_MASK | I2C_IRQ_EN_BIT); +} + +__STATIC_INLINE void I2C_EnableBus(I2C_T *i2c) +{ + HAL_MODIFY_REG(i2c->I2C_CTRL, I2C_WR_CTRL_MASK, I2C_BUS_EN_BIT); +} + +__STATIC_INLINE void I2C_DisableBus(I2C_T *i2c) +{ + HAL_CLR_BIT(i2c->I2C_CTRL, I2C_WR_CTRL_MASK | I2C_BUS_EN_BIT); +} + +__STATIC_INLINE void I2C_SendStart(I2C_T *i2c) +{ + HAL_MODIFY_REG(i2c->I2C_CTRL, I2C_WR_CTRL_MASK, I2C_START_BIT); +} + +__STATIC_INLINE uint8_t I2C_GetStartBit(I2C_T *i2c) +{ + return !!HAL_GET_BIT(i2c->I2C_CTRL, I2C_START_BIT); +} + +__STATIC_INLINE void I2C_SendStop(I2C_T *i2c) +{ + HAL_MODIFY_REG(i2c->I2C_CTRL, I2C_WR_CTRL_MASK, I2C_STOP_BIT); +} + +__STATIC_INLINE uint8_t I2C_GetStopBit(I2C_T *i2c) +{ + return !!HAL_GET_BIT(i2c->I2C_CTRL, I2C_STOP_BIT); +} + +__STATIC_INLINE void I2C_SendStopStart(I2C_T *i2c) +{ + HAL_MODIFY_REG(i2c->I2C_CTRL, I2C_WR_CTRL_MASK, I2C_STOP_BIT | I2C_START_BIT); +} + +__STATIC_INLINE void I2C_ClrIRQFlag(I2C_T *i2c) +{ + HAL_MODIFY_REG(i2c->I2C_CTRL, I2C_WR_CTRL_MASK, I2C_IRQ_FLAG_BIT); +} + +__STATIC_INLINE uint8_t I2C_GetIRQFlag(I2C_T *i2c) +{ + return !!HAL_GET_BIT(i2c->I2C_CTRL, I2C_IRQ_FLAG_BIT); +} + +__STATIC_INLINE void I2C_EnableACK(I2C_T *i2c) +{ + HAL_MODIFY_REG(i2c->I2C_CTRL, I2C_WR_CTRL_MASK, I2C_ACK_EN_BIT); +} + +__STATIC_INLINE void I2C_DisableACK(I2C_T *i2c) +{ + HAL_CLR_BIT(i2c->I2C_CTRL, I2C_WR_CTRL_MASK | I2C_ACK_EN_BIT); +} + +__STATIC_INLINE uint32_t I2C_GetIRQStatus(I2C_T *i2c) +{ + return HAL_GET_BIT(i2c->I2C_STATUS, I2C_STATUS_MASK); +} + +__STATIC_INLINE void I2C_SetClockReg(I2C_T *i2c, uint8_t clkM, uint8_t clkN) +{ + HAL_MODIFY_REG(i2c->I2C_CLK_CTRL, I2C_CLK_M_MASK | I2C_CLK_N_MASK, + (clkM << I2C_CLK_M_SHIFT) | (clkN << I2C_CLK_N_SHIFT)); +} + +__STATIC_INLINE void I2C_SetClockFreq(I2C_T *i2c, uint32_t clockFreq) +{ + uint8_t clkM = 0; + uint8_t clkN = 0; + uint8_t pow2N = 1; + + uint32_t APBClkDiv10; + uint32_t div; + uint32_t clockReal; + + APBClkDiv10 = HAL_CCM_BusGetAPBClock() / 10; + div = APBClkDiv10 / clockFreq; + if (div == 0) { + I2C_SetClockReg(i2c, clkM, clkN); + return; + } + + while (clkN <= I2C_CLK_N_MAX) { + clkM = div / pow2N - 1; + while (clkM <= I2C_CLK_M_MAX) { + clockReal = APBClkDiv10 / pow2N / (clkM + 1); + if (clockReal <= clockFreq) { + I2C_SetClockReg(i2c, clkM, clkN); + return; + } else { + clkM++; + } + } + clkN++; + pow2N *= 2; + } +} + +__STATIC_INLINE void I2C_SoftReset(I2C_T *i2c) +{ + HAL_SET_BIT(i2c->I2C_SOFT_RST, I2C_SOFT_RST_BIT); +} + +static void I2C_IRQHandler(I2C_T *i2c, I2C_Private *priv) +{ + uint8_t end = 0; + uint32_t IRQStatus = I2C_GetIRQStatus(i2c); + + HAL_I2C_DBG("IRQ Status: %#x\n", IRQStatus); + + switch (IRQStatus) { + case I2C_START_TRAN: + if (I2C_Is7BitAddrMode(priv)) { + if ((!I2C_IsMemMode(priv)) && (I2C_IsReadMode(priv))) + I2C_PutData(i2c, I2C_Get7BitAddrRd(priv)); + else + I2C_PutData(i2c, I2C_Get7BitAddrWr(priv)); + } else { + if ((!I2C_IsMemMode(priv)) && (I2C_IsReadMode(priv))) + I2C_PutData(i2c, I2C_Get10BitAddr1Rd(priv)); + else + I2C_PutData(i2c, I2C_Get10BitAddr1Wr(priv)); + } + break; + case I2C_RE_START_TRAN: + if (I2C_Is7BitAddrMode(priv)) + I2C_PutData(i2c, I2C_Get7BitAddrRd(priv)); + else + I2C_PutData(i2c, I2C_Get10BitAddr1Rd(priv)); + break; + case I2C_ADDR_WR_TRAN_ACK: + if (I2C_Is7BitAddrMode(priv)) { + if (I2C_IsMemMode(priv)) { + I2C_PutData(i2c, priv->memAddr); + } else { + I2C_PutData(i2c, *priv->buf); + priv->buf++; + priv->size--; + } + } else { + I2C_PutData(i2c, I2C_Get10BitAddr2(priv)); + } + break; + case I2C_ADDR_RD_TRAN_ACK: + if (!I2C_Is7BitAddrMode(priv)) + I2C_PutData(i2c, I2C_Get10BitAddr2(priv)); + if (priv->size == 1) + I2C_DisableACK(i2c); + break; + case I2C_SEC_ADDR_WR_ACK: + if (I2C_IsMemMode(priv)) { + I2C_PutData(i2c, priv->memAddr); + } else { + I2C_PutData(i2c, *priv->buf); + priv->buf++; + priv->size--; + } + break; + case I2C_MASTER_DATA_TRAN_ACK: + if (I2C_IsMemMode(priv) && I2C_IsReadMode(priv)) { + I2C_SendStart(i2c); + I2C_SetRestartBit(priv); + } else { + if (priv->size > 0) { + I2C_PutData(i2c, *priv->buf); + priv->buf++; + priv->size--; + } else { + end = 1; + } + } + break; + case I2C_MASTER_DATA_RECV_ACK: + *priv->buf = I2C_GetData(i2c); + priv->buf++; + priv->size--; + if (priv->size == 1) + I2C_DisableACK(i2c); + break; + case I2C_MASTER_DATA_RECV_NACK: + *priv->buf = I2C_GetData(i2c); + priv->buf++; + priv->size--; + end = 1; + break; + case I2C_ADDR_WR_TRAN_NACK: + HAL_ERR("Invalid IIC address\n"); + end = 1; + break; + case I2C_ADDR_RD_TRAN_NACK: + if (!I2C_IsMemMode(priv)) + HAL_ERR("Invalid IIC address\n"); + else + HAL_ERR("No ACK received after 2nd-address-send\n"); + end = 1; + break; + case I2C_MASTER_DATA_TRAN_NACK: + HAL_ERR("In writing, no ACK received\n"); + end = 1; + break; + default: + end = 1; + break; + } + + if (end) { + I2C_SendStop(i2c); + HAL_SemaphoreRelease(&priv->sem); + } + + I2C_ClrIRQFlag(i2c); + while (I2C_GetIRQFlag(i2c)) + ; + + return; +} + +static void I2C_SCCBIRQHandler(I2C_T *i2c, I2C_Private *priv) +{ + uint8_t end = 0; + uint32_t IRQStatus = I2C_GetIRQStatus(i2c); + + switch (IRQStatus) { + case I2C_START_TRAN: + if (I2C_IsRestart(priv)) + I2C_PutData(i2c, I2C_Get7BitAddrRd(priv)); + else + I2C_PutData(i2c, I2C_Get7BitAddrWr(priv)); + break; + case I2C_ADDR_WR_TRAN_ACK: + case I2C_ADDR_WR_TRAN_NACK: + I2C_PutData(i2c, priv->memAddr); + break; + case I2C_ADDR_RD_TRAN_ACK: + case I2C_ADDR_RD_TRAN_NACK: + I2C_DisableACK(i2c); + break; + case I2C_MASTER_DATA_TRAN_ACK: + case I2C_MASTER_DATA_TRAN_NACK: + if (I2C_IsReadMode(priv)) { + I2C_SendStopStart(i2c); + I2C_SetRestartBit(priv); + } else { + if (priv->size > 0) { + I2C_PutData(i2c, *priv->buf); + priv->buf++; + priv->size--; + } else { + end = 1; + } + } + break; + case I2C_MASTER_DATA_RECV_ACK: + case I2C_MASTER_DATA_RECV_NACK: + *priv->buf = I2C_GetData(i2c); + priv->buf++; + priv->size--; + end = 1; + break; + default: + end = 1; + break; + } + + if (end) { + I2C_SendStop(i2c); + HAL_SemaphoreRelease(&priv->sem); + } + + I2C_ClrIRQFlag(i2c); + while (I2C_GetIRQFlag(i2c)) + ; + + return; +} + +void TWI0_IRQHandler(void) +{ + if (I2C_IsSCCBMode(&gI2CPrivate[I2C0_ID])) + I2C_SCCBIRQHandler(I2C0, &gI2CPrivate[I2C0_ID]); + else + I2C_IRQHandler(I2C0, &gI2CPrivate[I2C0_ID]); +} + +void TWI1_IRQHandler(void) +{ + if (I2C_IsSCCBMode(&gI2CPrivate[I2C1_ID])) + I2C_SCCBIRQHandler(I2C1, &gI2CPrivate[I2C1_ID]); + else + I2C_IRQHandler(I2C1, &gI2CPrivate[I2C1_ID]); +} + +/** + * @brief Initialize the I2C according to the specified parameters + * @param[in] i2cID ID of the specified I2C + * @param[in] initParam Pointer to I2C_InitParam structure + * @retval HAL_Status, HAL_OK on success + */ +HAL_Status HAL_I2C_Init(I2C_ID i2cID, const I2C_InitParam *initParam) +{ + I2C_T *i2c; + I2C_Private *priv; + CCM_BusPeriphBit ccmPeriphBit; + IRQn_Type IRQn; + unsigned long flags; + + I2C_ASSERT_ID(i2cID); + + if (initParam->clockFreq == 0) { + HAL_ERR("clock frequency is zero.\n"); + return HAL_ERROR; + } + + flags = HAL_EnterCriticalSection(); + priv = I2C_GetI2CPriv(i2cID); + if (!I2C_IsInitState(priv)) + I2C_SetInitStateBit(priv); + else + priv = NULL; + HAL_ExitCriticalSection(flags); + + if (priv == NULL) { + HAL_WRN("i2c %d already inited\n", i2cID); + return HAL_BUSY; + } +#ifdef CONFIG_PM + if (!(hal_i2c_suspending & (1 << i2cID))) { + pm_register_ops(I2C_DEV(i2cID)); + HAL_Memcpy(&hal_i2c_param[i2cID], initParam, sizeof(I2C_InitParam)); + } +#endif + i2c = I2C_GetI2CInstance(i2cID); + + if (initParam->addrMode == I2C_ADDR_MODE_7BIT) + I2C_Set7BitAddrMode(priv); + else + I2C_Set10BitAddrMode(priv); + + priv->memAddr = 0; + priv->devAddr = 0; + priv->buf = NULL; + priv->size = 0; + HAL_MutexInit(&priv->mtx); + HAL_SemaphoreInitBinary(&priv->sem); + + /* config pinmux */ + HAL_BoardIoctl(HAL_BIR_PINMUX_INIT, HAL_MKDEV(HAL_DEV_MAJOR_I2C, i2cID), 0); + + /* enable i2c clock and release reset */ + ccmPeriphBit = (i2cID == I2C0_ID ? CCM_BUS_PERIPH_BIT_I2C0 : + CCM_BUS_PERIPH_BIT_I2C1); + HAL_CCM_BusEnablePeriphClock(ccmPeriphBit); + HAL_CCM_BusReleasePeriphReset(ccmPeriphBit); + + I2C_DisableIRQ(i2c); + I2C_DisableBus(i2c); + I2C_SetClockFreq(i2c, initParam->clockFreq); + I2C_EnableBus(i2c); + + /* enable NVIC IRQ */ + IRQn = (i2cID == I2C0_ID) ? I2C0_IRQn : I2C1_IRQn; + HAL_NVIC_SetPriority(IRQn, NVIC_PERIPHERAL_PRIORITY_DEFAULT); + HAL_NVIC_EnableIRQ(IRQn); + + return HAL_OK; +} + +/** + * @brief DeInitialize the specified I2C + * @param[in] i2cID ID of the specified I2C + * @retval HAL_Status, HAL_OK on success + */ +HAL_Status HAL_I2C_DeInit(I2C_ID i2cID) +{ + I2C_T *i2c; + I2C_Private *priv; + CCM_BusPeriphBit ccmPeriphBit; + unsigned long flags; + + I2C_ASSERT_ID(i2cID); + +#ifdef CONFIG_PM + if (!(hal_i2c_suspending & (1 << i2cID))) { + pm_unregister_ops(I2C_DEV(i2cID)); + } +#endif + + i2c = I2C_GetI2CInstance(i2cID); + priv = I2C_GetI2CPriv(i2cID); + + HAL_NVIC_DisableIRQ((i2cID == I2C0_ID) ? I2C0_IRQn : I2C1_IRQn); + + I2C_DisableBus(i2c); + + /* disable i2c clock and force reset */ + ccmPeriphBit = (i2cID == I2C0_ID ? CCM_BUS_PERIPH_BIT_I2C0 : + CCM_BUS_PERIPH_BIT_I2C1); + HAL_CCM_BusForcePeriphReset(ccmPeriphBit); + HAL_CCM_BusDisablePeriphClock(ccmPeriphBit); + + /* De-config pinmux */ + HAL_BoardIoctl(HAL_BIR_PINMUX_DEINIT, HAL_MKDEV(HAL_DEV_MAJOR_I2C, i2cID), 0); + + HAL_MutexDeinit(&priv->mtx); + HAL_SemaphoreDeinit(&priv->sem); + + flags = HAL_EnterCriticalSection(); + I2C_ClrInitStateBit(priv); + HAL_ExitCriticalSection(flags); + + return HAL_OK; +} + +static int32_t I2C_Master_common(I2C_ID i2cID, uint16_t devAddr, uint8_t memAddr, uint8_t *buf, int32_t size) +{ + I2C_T *i2c; + I2C_Private *priv; + + I2C_ASSERT_ID(i2cID); + + if (buf == NULL || size <= 0) { + HAL_ERR("buf %p, size %d\n", buf, size); + return -1; + } + + i2c = I2C_GetI2CInstance(i2cID); + priv = I2C_GetI2CPriv(i2cID); + + priv->devAddr = devAddr; + priv->memAddr = memAddr; + priv->buf = buf; + priv->size = size; + + I2C_EnableACK(i2c); + I2C_EnableIRQ(i2c); + I2C_SendStart(i2c); + + if (HAL_SemaphoreWait(&priv->sem, I2C_SEM_TIMEOUT_MS) != HAL_OK) + HAL_WRN("I2C wait semaphore failed, i2c ID %d\n", i2cID); + + while (I2C_GetStopBit(i2c)) + ; + I2C_DisableIRQ(i2c); + I2C_DisableACK(i2c); + + size -= priv->size; + priv->devAddr = 0; + priv->memAddr = 0; + priv->buf = NULL; + priv->size = 0; + + return size; +} + +/** + * @brief Transmit an amount of data in interrupt mode + * @param[in] i2cID ID of the specified I2C + * @param[in] devAddr Device address + * @param[in] buf Pointer to the data buffer + * @param[in] size Number of bytes to be transmitted + * @return Number of bytes transmitted, -1 on error + */ +int32_t HAL_I2C_Master_Transmit_IT(I2C_ID i2cID, uint16_t devAddr, uint8_t *buf, int32_t size) +{ + I2C_Private *priv = I2C_GetI2CPriv(i2cID); + + if (HAL_MutexLock(&priv->mtx, I2C_MTX_TIMEOUT_MS) != HAL_OK) { + HAL_WRN("I2C wait mutex failed, i2c ID %d\n", i2cID); + return 0; + } + + I2C_SetWriteMode(priv); + I2C_ClrSCCBMode(priv); + I2C_ClrMemMode(priv); + I2C_ClrRestartBit(priv); + + int32_t ret = I2C_Master_common(i2cID, devAddr, 0, buf, size); + + HAL_MutexUnlock(&priv->mtx); + + return ret; +} + +/** + * @brief Receive an amount of data in interrupt mode + * @param[in] i2cID ID of the specified I2C + * @param[in] devAddr Device address + * @param[in] buf Pointer to the data buffer + * @param[in] size Number of bytes to be received + * @return Number of bytes received, -1 on error + */ +int32_t HAL_I2C_Master_Receive_IT(I2C_ID i2cID, uint16_t devAddr, uint8_t *buf, int32_t size) +{ + I2C_Private *priv = I2C_GetI2CPriv(i2cID); + + if (HAL_MutexLock(&priv->mtx, I2C_MTX_TIMEOUT_MS) != HAL_OK) { + HAL_WRN("I2C wait mutex failed, i2c ID %d\n", i2cID); + return 0; + } + + I2C_SetReadMode(priv); + I2C_ClrSCCBMode(priv); + I2C_ClrMemMode(priv); + I2C_ClrRestartBit(priv); + + int32_t ret = I2C_Master_common(i2cID, devAddr, 0, buf, size); + + HAL_MutexUnlock(&priv->mtx); + + return ret; +} + +/** + * @brief Transmit an amount of data to specified memory or register of the + * slave device in interrupt mode + * @param[in] i2cID ID of the specified I2C + * @param[in] devAddr Device address + * @param[in] memAddr Memory or register address + * @param[in] buf Pointer to the data buffer + * @param[in] size Number of bytes to be transmitted + * @return Number of bytes transmitted, -1 on error + */ +int32_t HAL_I2C_Master_Transmit_Mem_IT(I2C_ID i2cID, uint16_t devAddr, uint8_t memAddr, uint8_t *buf, int32_t size) +{ + I2C_Private *priv = I2C_GetI2CPriv(i2cID); + + if (HAL_MutexLock(&priv->mtx, I2C_MTX_TIMEOUT_MS) != HAL_OK) { + HAL_WRN("I2C wait mutex failed, i2c ID %d\n", i2cID); + return 0; + } + + I2C_SetWriteMode(priv); + I2C_ClrSCCBMode(priv); + I2C_SetMemMode(priv); + I2C_ClrRestartBit(priv); + + int32_t ret = I2C_Master_common(i2cID, devAddr, memAddr, buf, size); + + HAL_MutexUnlock(&priv->mtx); + + return ret; +} + +/** + * @brief Receive an amount of data from specified memory or register of the + * slave device in interrupt mode + * @param[in] i2cID ID of the specified I2C + * @param[in] devAddr Device address + * @param[in] memAddr Memory or register address + * @param[in] buf Pointer to the data buffer + * @param[in] size Number of bytes to be received + * @return Number of bytes received, -1 on error + */ +int32_t HAL_I2C_Master_Receive_Mem_IT(I2C_ID i2cID, uint16_t devAddr, uint8_t memAddr, uint8_t *buf, int32_t size) +{ + I2C_Private *priv = I2C_GetI2CPriv(i2cID); + + if (HAL_MutexLock(&priv->mtx, I2C_MTX_TIMEOUT_MS) != HAL_OK) { + HAL_WRN("I2C wait mutex failed, i2c ID %d\n", i2cID); + return 0; + } + + I2C_SetReadMode(priv); + I2C_ClrSCCBMode(priv); + I2C_SetMemMode(priv); + I2C_ClrRestartBit(priv); + + int32_t ret = I2C_Master_common(i2cID, devAddr, memAddr, buf, size); + + HAL_MutexUnlock(&priv->mtx); + + return ret; +} + +/** + * @brief Transmit one byte data through SCCB protocol in interrupt mode + * @param[in] i2cID ID of the specified I2C + * @param[in] devAddr Device address + * @param[in] subAddr Sub-address + * @param[in] buf Pointer to the data buffer + * @return Number of bytes transmitted, -1 on error + */ +int32_t HAL_I2C_SCCB_Master_Transmit_IT(I2C_ID i2cID, uint8_t devAddr, uint8_t subAddr, uint8_t *buf) +{ + I2C_Private *priv = I2C_GetI2CPriv(i2cID); + + if (HAL_MutexLock(&priv->mtx, I2C_MTX_TIMEOUT_MS) != HAL_OK) { + HAL_WRN("I2C wait mutex failed, i2c ID %d\n", i2cID); + return 0; + } + + I2C_SetWriteMode(priv); + I2C_SetSCCBMode(priv); + I2C_ClrMemMode(priv); + I2C_ClrRestartBit(priv); + + int32_t ret = I2C_Master_common(i2cID, devAddr, subAddr, buf, 0x01); + + HAL_MutexUnlock(&priv->mtx); + + return ret; +} + +/** + * @brief Receive one byte data through SCCB protocol in interrupt mode + * @param[in] i2cID ID of the specified I2C + * @param[in] devAddr Device address + * @param[in] subAddr Sub-address + * @param[in] buf Pointer to the data buffer + * @return Number of bytes received, -1 on error + */ +int32_t HAL_I2C_SCCB_Master_Receive_IT(I2C_ID i2cID, uint8_t devAddr, uint8_t subAddr, uint8_t *buf) +{ + I2C_Private *priv = I2C_GetI2CPriv(i2cID); + + if (HAL_MutexLock(&priv->mtx, I2C_MTX_TIMEOUT_MS) != HAL_OK) { + HAL_WRN("I2C wait mutex failed, i2c ID %d\n", i2cID); + return 0; + } + + I2C_SetReadMode(priv); + I2C_SetSCCBMode(priv); + I2C_ClrMemMode(priv); + I2C_ClrRestartBit(priv); + + int32_t ret = I2C_Master_common(i2cID, devAddr, subAddr, buf, 0x01); + + HAL_MutexUnlock(&priv->mtx); + + return ret; +} + diff --git a/platform/mcu/xr871/src/driver/chip/hal_i2s.c b/platform/mcu/xr871/src/driver/chip/hal_i2s.c new file mode 100644 index 0000000000..716d80574f --- /dev/null +++ b/platform/mcu/xr871/src/driver/chip/hal_i2s.c @@ -0,0 +1,1353 @@ +/** + * @file hal_i2s.c + * @author XRADIO IOT WLAN Team + */ + +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "driver/chip/hal_dma.h" +#include "driver/chip/hal_i2s.h" +#include "hal_base.h" +#include "sys/io.h" +#include "pm/pm.h" + +#define I2D_DBG_ON 0 + +#if (I2D_DBG_ON == 1) +#define I2S_DEBUG(fmt, arg...) HAL_LOG(I2D_DBG_ON, "[I2S] "fmt, ##arg) +#else +#define I2S_DEBUG(fmt, arg...) +#endif +#define I2S_ERROR(fmt, arg...) HAL_LOG(1, "[I2S] "fmt, ##arg) + +typedef struct { + volatile bool isHwInit; + volatile bool txRunning; + volatile bool rxRunning; + + uint8_t *txBuf; + uint8_t *rxBuf; + uint8_t *readPointer; + uint8_t *writePointer; + uint32_t rxLength; + uint32_t txLength; + + DMA_Channel txDMAChan; + DMA_Channel rxDMAChan; + I2S_HWParam *hwParam; + I2S_DataParam pdataParam; + I2S_DataParam cdataParam; + volatile uint32_t txHalfCallCount; + volatile uint32_t rxHalfCallCount; + volatile uint32_t txEndCallCount; + volatile uint32_t rxEndCallCount; + uint8_t *txDmaPointer; + uint8_t *rxDmaPointer; + + HAL_Semaphore txReady; + HAL_Semaphore rxReady; + bool isTxSemaphore; + bool isRxSemaphore; + bool isTxInitiate; + bool isRxInitiate; + + HAL_Mutex devSetLock; + HAL_Mutex devTriggerLock; + + uint32_t audioPllParam; + uint32_t audioPllPatParam; +} I2S_Private; + +typedef struct { + uint32_t hosc; + uint32_t audio; + uint32_t pllParam; + uint32_t pllPatParam; +} HOSC_I2S_Type; + +typedef enum { + I2S_PLL_24M = 0U, + I2S_PLL_22M = 1U, +} I2S_PLLMode; + +typedef struct { + uint8_t clkDiv; + I2S_BCLKDIV bregVal; + I2S_MCLKDIV mregVal; +} CLK_DIVRegval; + +#define AUDIO_PLL_SRC (CCM_DAUDIO_MCLK_SRC_1X) +#define AUDIO_PLL_24 (24576000) +#define AUDIO_PLL_22 (22579200) +#define AUDIO_DEVICE_PLL AUDIO_PLL_22 + +#define I2S_MEMCPY memcpy +#define I2S_MALLOC malloc +#define I2S_FREE free +#define I2S_MEMSET memset + +#define UNDERRUN_THRESHOLD 3 +#define OVERRUN_THRESHOLD 3 + +#ifdef RESERVERD_MEMORY_FOR_I2S_TX +static uint8_t I2STX_BUF[I2S_BUF_LENGTH]; +#endif +#ifdef RESERVERD_MEMORY_FOR_I2S_RX +static uint8_t I2SRX_BUF[I2S_BUF_LENGTH]; +#endif + +void HAL_I2S_Trigger(bool enable,I2S_StreamDir dir); +static I2S_Private gI2sPrivate; +static uint32_t I2S_BUF_LENGTH = 0; + +/* + *default hw configuration + */ +static I2S_HWParam gHwParam = { + 0, /*0:I2S,1:PCM*/ + DAIFMT_CBS_CFS, + DAIFMT_I2S, + DAIFMT_NB_NF, + 32, /*16,32,64,128,256*/ + I2S_SHORT_FRAME, + I2S_TX_MSB_FIRST, + I2S_RX_MSB_FIRST, + 0x40, + 0xF, + {AUDIO_DEVICE_PLL,1}, +}; + +static CLK_DIVRegval DivRegval[] = { + {1, I2S_BCLKDIV_1, I2S_MCLKDIV_1}, + {2, I2S_BCLKDIV_2, I2S_MCLKDIV_2}, + {4, I2S_BCLKDIV_4, I2S_MCLKDIV_4}, + {6, I2S_BCLKDIV_6, I2S_MCLKDIV_6}, + {8, I2S_BCLKDIV_8, I2S_MCLKDIV_8}, + {12, I2S_BCLKDIV_12, I2S_MCLKDIV_12}, + {16, I2S_BCLKDIV_16, I2S_MCLKDIV_16}, + {24, I2S_BCLKDIV_24, I2S_MCLKDIV_24}, + {32, I2S_BCLKDIV_32, I2S_MCLKDIV_32}, + {48, I2S_BCLKDIV_48, I2S_MCLKDIV_48}, + {64, I2S_BCLKDIV_64, I2S_MCLKDIV_64}, + {96, I2S_BCLKDIV_96, I2S_MCLKDIV_96}, + {128, I2S_BCLKDIV_128, I2S_MCLKDIV_128}, + {176, I2S_BCLKDIV_176, I2S_MCLKDIV_176}, + {192, I2S_BCLKDIV_192, I2S_MCLKDIV_192}, + {} +}; + +static const HOSC_I2S_Type i2s_hosc_aud_type[] = { + {HOSC_CLOCK_26M, I2S_PLL_24M, PRCM_AUD_PLL24M_PARAM_HOSC26M, PRCM_AUD_PLL24M_PAT_PARAM_HOSC26M}, + {HOSC_CLOCK_26M, I2S_PLL_22M, PRCM_AUD_PLL22M_PARAM_HOSC26M, PRCM_AUD_PLL22M_PAT_PARAM_HOSC26M}, + {HOSC_CLOCK_24M, I2S_PLL_24M, PRCM_AUD_PLL24M_PARAM_HOSC24M, PRCM_AUD_PLL24M_PAT_PARAM_HOSC24M}, + {HOSC_CLOCK_24M, I2S_PLL_22M, PRCM_AUD_PLL22M_PARAM_HOSC24M, PRCM_AUD_PLL22M_PAT_PARAM_HOSC24M}, + {HOSC_CLOCK_40M, I2S_PLL_24M, PRCM_AUD_PLL24M_PARAM_HOSC40M, PRCM_AUD_PLL24M_PAT_PARAM_HOSC40M}, + {HOSC_CLOCK_40M, I2S_PLL_22M, PRCM_AUD_PLL22M_PARAM_HOSC40M, PRCM_AUD_PLL22M_PAT_PARAM_HOSC40M}, + {HOSC_CLOCK_52M, I2S_PLL_24M, PRCM_AUD_PLL24M_PARAM_HOSC52M, PRCM_AUD_PLL24M_PAT_PARAM_HOSC52M}, + {HOSC_CLOCK_52M, I2S_PLL_22M, PRCM_AUD_PLL22M_PARAM_HOSC52M, PRCM_AUD_PLL22M_PAT_PARAM_HOSC52M}, +}; + +/** + * @internal + * @brief Set i2s clock frequency + * @pll I2S clock identifier + * This parameter can be one of the following values: + * @arg @ref I2S_PLL_22M + * @arg @ref I2S_PLL_24M + * @retval return 0 means success otherwise fail + */ +uint32_t I2S_PLLAUDIO_Update(I2S_PLLMode pll) +{ + I2S_Private *i2sPrivate = &gI2sPrivate; + + if (pll != I2S_PLL_24M && pll != I2S_PLL_22M) + return -1; + + uint32_t hoscClock = HAL_PRCM_GetHFClock(); + + int i = 0; + for (i = 0; i < ARRAY_SIZE(i2s_hosc_aud_type); i++) { + if ((i2s_hosc_aud_type[i].hosc == hoscClock) && (i2s_hosc_aud_type[i].audio == pll)) { + i2sPrivate->audioPllParam = i2s_hosc_aud_type[i].pllParam; + i2sPrivate->audioPllPatParam = i2s_hosc_aud_type[i].pllPatParam; + break; + } + } + if (i == ARRAY_SIZE(i2s_hosc_aud_type)) { + I2S_ERROR("Update audio pll failed....\n"); + return -1; + } + return 0; +} + +/** + * @internal + * @brief Enable/disable I2S tx + * @retval None + */ +static void I2S_DisableTx() +{ + HAL_CLR_BIT(I2S->DA_CTL, I2S_TX_EN_BIT); +} + +static void I2S_EnableTx() +{ + HAL_SET_BIT(I2S->DA_CTL, I2S_TX_EN_BIT); +} + +/** + * @internal + * @brief Enable/disable I2S rx + * @retval None + */ +static void I2S_DisableRx() +{ + HAL_CLR_BIT(I2S->DA_CTL, I2S_RX_EN_BIT); +} + +static void I2S_EnableRx() +{ + HAL_SET_BIT(I2S->DA_CTL, I2S_RX_EN_BIT); +} + +/** + * @internal + * @brief set the i2s module clock frequency for a given peripheral clk + * @param isEnable: flag for i2s mclk output + * @param clkSource: Peripheral clock freq + * @param pll: the freq of mclk + * @retval HAL status + */ +static HAL_Status I2S_SET_Mclk(uint32_t isEnable, uint32_t clkSource, uint32_t pll) +{ + if (isEnable == 0) { + HAL_CLR_BIT(I2S->DA_CLKD, I2S_MCLK_OUT_EN_BIT); + return HAL_OK; + } + uint32_t mclkDiv = pll / clkSource; + CLK_DIVRegval *divRegval = DivRegval; + + do { + if (divRegval->clkDiv == mclkDiv) { + HAL_MODIFY_REG(I2S->DA_CLKD, I2S_MCLKDIV_MASK, divRegval->mregVal); + break; + } + divRegval++; + } while (divRegval->mregVal < I2S_MCLKDIV_192); + HAL_SET_BIT(I2S->DA_CLKD, I2S_MCLK_OUT_EN_BIT); + return HAL_OK; +} + +/** + * @internal + * @brief set the Sample Resolution + * @param param: pointer to a I2S_DataParam structure that contains + * data format information + * @retval HAL status + */ +static HAL_Status I2S_SET_SampleResolution(I2S_DataParam *param) +{ + if (!param) + return HAL_INVALID; + I2S_SampleResolution res = param->resolution; + + if (I2S_SR8BIT <= res && res <= I2S_SR32BIT) + HAL_MODIFY_REG(I2S->DA_FMT0, I2S_SR_MASK, res); + else { + I2S_ERROR("Invalid sample resolution (%d) failed\n",res); + return HAL_ERROR; + } + return HAL_OK; +} + +/** + * @internal + * @brief set the i2s bclk lrck freq + * @param param: pointer to a I2S_DataParam structure that contains + * data format information + * @param hwParam: pointer to a I2S_HWParam structure that contains + * the configuration for clk/mode/format. + * @retval HAL status + */ +static HAL_Status I2S_SET_ClkDiv(I2S_DataParam *param, I2S_HWParam *hwParam) +{ + int32_t ret = HAL_OK; + if (!param || !hwParam) + return HAL_INVALID; + uint32_t rate = 0; + I2S_SampleRate SR = param->sampleRate; + uint32_t Period = hwParam->lrckPeriod; + uint16_t bclkDiv = 0; + uint32_t audioPll = AUDIO_PLL_24; + + switch (SR) { + case I2S_SR8K:/* 8000Hz */ + rate = 8000; + break; + case I2S_SR12K:/* 12000Hz */ + rate = 12000; + break; + case I2S_SR16K:/* 16000Hz */ + rate = 16000; + break; + case I2S_SR24K:/* 24000Hz */ + rate = 24000; + break; + case I2S_SR32K:/* 32000Hz */ + rate = 32000; + break; + case I2S_SR48K:/* 48000Hz */ + rate = 48000; + break; + case I2S_SR11K: + rate = 11025; + break; + case I2S_SR22K: + rate = 22050; + break; + case I2S_SR44K: + rate = 44100; + break; + default: + I2S_ERROR("Invalid sample rate(%x) failed...\n",rate); + return HAL_INVALID; + } + + I2S_DEBUG("SAMPLE RATE:%d...\n",rate); + if ((rate % 1000) != 0) + audioPll = AUDIO_PLL_22; + + I2S_Private *i2sPrivate = &gI2sPrivate; + + /*set sysclk*/ + if (audioPll == AUDIO_PLL_24) { + I2S_PLLAUDIO_Update(I2S_PLL_24M); + HAL_PRCM_SetAudioPLLParam(i2sPrivate->audioPllParam); + HAL_PRCM_SetAudioPLLPatternParam(i2sPrivate->audioPllPatParam); + } else { + I2S_PLLAUDIO_Update(I2S_PLL_22M); + HAL_PRCM_SetAudioPLLParam(i2sPrivate->audioPllParam); + HAL_PRCM_SetAudioPLLPatternParam(i2sPrivate->audioPllPatParam); + } + + /*config bclk pll div*/ + if (!hwParam->i2sFormat) + bclkDiv = audioPll/(2*Period*rate); + else + bclkDiv = audioPll/(Period*rate); + + CLK_DIVRegval *divRegval = DivRegval; + do { + if (divRegval->clkDiv == bclkDiv) { + HAL_MODIFY_REG(I2S->DA_CLKD, I2S_BCLKDIV_MASK, divRegval->bregVal); + break; + } + divRegval++; + } while (divRegval->bregVal < I2S_BCLKDIV_192); + + return ret; +} + +/** + * @internal + * @brief set the i2s transfer format + * @param param: pointer to a I2S_HWParam structure that contains + * the configuration for clk/mode/format. + * @retval HAL status + */ +static HAL_Status I2S_SET_Format(I2S_HWParam *param) +{ + int32_t ret = HAL_OK; + if (!param) + return HAL_INVALID; + + /* config clk mode*/ + switch (param->clkMode) { + case DAIFMT_CBM_CFM: + HAL_MODIFY_REG(I2S->DA_CTL, I2S_BCLK_OUT_MASK|I2S_LRCK_OUT_MASK|I2S_SDO0_EN_BIT, + I2S_BCLK_INPUT|I2S_LRCK_INPUT|I2S_SDO0_EN_BIT); + + break; + case DAIFMT_CBS_CFS: + HAL_MODIFY_REG(I2S->DA_CTL, I2S_BCLK_OUT_MASK|I2S_LRCK_OUT_MASK|I2S_SDO0_EN_BIT, + I2S_BCLK_OUTPUT|I2S_LRCK_OUTPUT|I2S_SDO0_EN_BIT); + break; + default: + ret = HAL_INVALID; + I2S_ERROR("Invalid DAI format,failed (%d)...\n",param->clkMode); + break; + } + + /* config transfer format */ + switch (param->transferFormat) { + case DAIFMT_I2S: + HAL_MODIFY_REG(I2S->DA_CTL, I2S_MODE_SEL_MASK, I2S_LEFT_MODE); + HAL_MODIFY_REG(I2S->DA_TX0CHSEL, I2S_TXN_OFFSET_MASK, I2S_TX_ONEBCLK_OFFSET); + HAL_MODIFY_REG(I2S->DA_RXCHSEL, I2S_RXN_OFFSET_MASK, I2S_RX_ONEBCLK_OFFSET); + break; + case DAIFMT_RIGHT_J: + HAL_MODIFY_REG(I2S->DA_CTL, I2S_MODE_SEL_MASK, I2S_RIGHT_MODE); + break; + case DAIFMT_LEFT_J: + HAL_MODIFY_REG(I2S->DA_CTL, I2S_MODE_SEL_MASK, I2S_LEFT_MODE); + HAL_MODIFY_REG(I2S->DA_TX0CHSEL, I2S_TXN_OFFSET_MASK, I2S_TX_NO_OFFSET); + HAL_MODIFY_REG(I2S->DA_RXCHSEL, I2S_RXN_OFFSET_MASK, I2S_RX_NO_OFFSET); + break; + case DAIFMT_DSP_A: + HAL_MODIFY_REG(I2S->DA_CTL, I2S_MODE_SEL_MASK, I2S_PCM_MODE); + HAL_MODIFY_REG(I2S->DA_TX0CHSEL, I2S_TXN_OFFSET_MASK, I2S_TX_ONEBCLK_OFFSET); + HAL_MODIFY_REG(I2S->DA_RXCHSEL, I2S_RXN_OFFSET_MASK, I2S_RX_ONEBCLK_OFFSET); + break; + case DAIFMT_DSP_B: + HAL_MODIFY_REG(I2S->DA_CTL, I2S_MODE_SEL_MASK, I2S_PCM_MODE); + HAL_MODIFY_REG(I2S->DA_TX0CHSEL, I2S_TXN_OFFSET_MASK, I2S_TX_NO_OFFSET); + HAL_MODIFY_REG(I2S->DA_RXCHSEL, I2S_RXN_OFFSET_MASK, I2S_RX_NO_OFFSET); + break; + default: + ret = HAL_INVALID; + I2S_ERROR("Invalid transfer format,failed (%d)...\n",param->transferFormat); + break; + } + + /* config signal interval */ + switch (param->signalInterval) { + case DAIFMT_NB_NF: + HAL_CLR_BIT(I2S->DA_FMT0, I2S_LRCK_POLARITY_MASK); + HAL_CLR_BIT(I2S->DA_FMT0, I2S_BCLK_POLARITY_MASK); + break; + case DAIFMT_NB_IF: + HAL_SET_BIT(I2S->DA_FMT0, I2S_LRCK_POLARITY_MASK); + HAL_CLR_BIT(I2S->DA_FMT0, I2S_BCLK_POLARITY_MASK); + break; + case DAIFMT_IB_NF: + HAL_CLR_BIT(I2S->DA_FMT0, I2S_LRCK_POLARITY_MASK); + HAL_SET_BIT(I2S->DA_FMT0, I2S_BCLK_POLARITY_MASK); + break; + case DAIFMT_IB_IF: + HAL_SET_BIT(I2S->DA_FMT0, I2S_LRCK_POLARITY_MASK); + HAL_SET_BIT(I2S->DA_FMT0, I2S_BCLK_POLARITY_MASK); + break; + default: + ret = HAL_INVALID; + I2S_ERROR("Invalid signal Interval,failed (%d)...\n",param->signalInterval); + break; + } + return ret; +} + +/** + * @internal + * @brief set the number channels of i2s transfer + * @param param: pointer to a I2S_DataParam structure that contains + * data format information. + * @retval HAL status + */ +static HAL_Status I2S_SET_Channels(I2S_DataParam *param) +{ + uint8_t channel = 0; + if (!param) + return HAL_INVALID; + + if (param->direction == PLAYBACK) {/*play*/ + if (param->channels < I2S_TX_SLOT_NUM1 || param->channels > I2S_TX_SLOT_NUM8) { + I2S_ERROR("Invalid usr tx channels num,failed (%d)...\n",param->channels); + return HAL_INVALID; + } + HAL_MODIFY_REG(I2S->DA_CHCFG, I2S_TX_SLOT_NUM_MASK, ((param->channels - 1) << I2S_TX_SLOT_NUM_SHIFT)); + HAL_MODIFY_REG(I2S->DA_TX0CHSEL, I2S_TXN_CHANNEL_SEL_MASK, + ((param->channels -1) << I2S_TXN_CHANNEL_SEL_SHIFT)); + HAL_MODIFY_REG(I2S->DA_TX0CHSEL, I2S_TXN_CHANNEL_SLOT_ENABLE_MASK, + I2S_TXN_CHANNEL_SLOTS_ENABLE(param->channels)); + for (channel = 0; channel < param->channels; channel++) { + HAL_MODIFY_REG(I2S->DA_TX0CHMAP, I2S_TXN_CHX_MAP_MASK(channel),I2S_TXN_CHX_MAP(channel)); + } + + } else {/*record*/ + if (param->channels < I2S_RX_SLOT_NUM1 || param->channels > I2S_RX_SLOT_NUM8){ + I2S_ERROR("Invalid usr rx channels num,failed (%d)...\n",param->channels); + return HAL_INVALID; + } + + HAL_MODIFY_REG(I2S->DA_CHCFG, I2S_RX_SLOT_NUM_MASK, ((param->channels - 1) << I2S_RX_SLOT_NUM_SHIFT)); + HAL_MODIFY_REG(I2S->DA_RXCHSEL, I2S_RXN_CHANNEL_SEL_MASK, + ((param->channels - 1) << I2S_RXN_CHANNEL_SEL_SHIFT)); + for (channel = 0; channel < param->channels; channel++) { + HAL_MODIFY_REG(I2S->DA_RXCHMAP, I2S_RXN_CHX_MAP_MASK(channel), I2S_RXN_CHX_MAP(channel)); + } + } + return HAL_OK; +} + +static int I2S_DMA_BUFFER_CHECK_Threshold(uint8_t dir) +{ + I2S_Private *i2sPrivate = &gI2sPrivate; + if (dir == 0) { + if (i2sPrivate->txHalfCallCount >= UNDERRUN_THRESHOLD || + i2sPrivate->txEndCallCount >= UNDERRUN_THRESHOLD) { + I2S_ERROR("Tx : underrun and stop dma tx....\n"); + HAL_I2S_Trigger(false,PLAYBACK);/*stop*/ + i2sPrivate->txRunning =false; + i2sPrivate->writePointer = NULL; + i2sPrivate->txHalfCallCount = 0; + i2sPrivate->txEndCallCount = 0; + i2sPrivate->txDmaPointer = NULL; + return -1; + } + } else { + if (i2sPrivate->rxHalfCallCount >= OVERRUN_THRESHOLD || + i2sPrivate->rxEndCallCount >= OVERRUN_THRESHOLD) { + I2S_ERROR("Rx : overrun and stop dma rx....\n"); + HAL_I2S_Trigger(false,RECORD);/*stop*/ + i2sPrivate->rxRunning =false; + i2sPrivate->rxHalfCallCount = 0; + i2sPrivate->rxEndCallCount = 0; + i2sPrivate->readPointer = NULL; + i2sPrivate->rxDmaPointer = NULL; + return -1; + } + } + return 0; +} + +/** + * @internal + * @brief DMA I2S transmit/receive process half complete callback + * @param arg: pointer to a HAL_Semaphore structure that contains + * sem to synchronous data. + * @retval None + */ +static void I2S_DMAHalfCallback(void *arg) +{ + I2S_Private *i2sPrivate = &gI2sPrivate; + if (arg == &(i2sPrivate->txReady)) { + i2sPrivate->txHalfCallCount ++; + if (I2S_DMA_BUFFER_CHECK_Threshold(0) != 0) + return; + i2sPrivate->txDmaPointer = i2sPrivate->txBuf + I2S_BUF_LENGTH/2; + if (i2sPrivate->isTxSemaphore) { + i2sPrivate->isTxSemaphore = false; + HAL_SemaphoreRelease((HAL_Semaphore *)arg); + } + + } else { + i2sPrivate->rxHalfCallCount ++; + if (I2S_DMA_BUFFER_CHECK_Threshold(1) != 0) + return; + i2sPrivate->rxDmaPointer = i2sPrivate->rxBuf + I2S_BUF_LENGTH/2; + if (i2sPrivate->isRxSemaphore) { + i2sPrivate->isRxSemaphore = false; + HAL_SemaphoreRelease((HAL_Semaphore *)arg); + } + } +} + +/** + * @internal + * @brief DMA I2S transmit/receive process complete callback + * @param arg: pointer to a HAL_Semaphore structure that contains + * sem to synchronous data. + * @retval None + */ +static void I2S_DMAEndCallback(void *arg) +{ + I2S_Private *i2sPrivate = &gI2sPrivate; + if (arg == &(i2sPrivate->txReady)) { + i2sPrivate->txEndCallCount ++; + if (I2S_DMA_BUFFER_CHECK_Threshold(0) != 0) + return; + i2sPrivate->txDmaPointer = i2sPrivate->txBuf; + if (i2sPrivate->isTxSemaphore) { + i2sPrivate->isTxSemaphore = false; + HAL_SemaphoreRelease((HAL_Semaphore *)arg); + } + } else { + i2sPrivate->rxEndCallCount ++; + if (I2S_DMA_BUFFER_CHECK_Threshold(1) != 0) + return; + i2sPrivate->rxDmaPointer = i2sPrivate->rxBuf + I2S_BUF_LENGTH/2; + if (i2sPrivate->isRxSemaphore) { + i2sPrivate->isRxSemaphore = false; + HAL_SemaphoreRelease((HAL_Semaphore *)arg); + } + } +} + +/** + * @internal + * @brief Start the DMA Transfer. + * @param chan: the specified DMA Channel. + * @param srcAddr: The source memory Buffer address + * @param dstAddr: The destination memory Buffer address + * @param datalen: The length of data to be transferred from source to destination + * @retval none + */ +static void I2S_DMAStart(DMA_Channel chan, uint32_t srcAddr, uint32_t dstAddr, uint32_t datalen) +{ + HAL_DMA_Start(chan, srcAddr, dstAddr, datalen); +} + +/** + * @internal + * @brief stop the DMA Transfer. + * @param chan: the specified DMA Channel. + * @retval none + */ +static void I2S_DMAStop(DMA_Channel chan) +{ + HAL_DMA_Stop(chan); +} + +/** + * @internal + * @brief Sets the DMA Transfer parameter. + * @param channel: the specified DMA Channel. + * @param dir: Data transfer direction + * @retval none + */ +static void I2S_DMASet(DMA_Channel channel,I2S_StreamDir dir) +{ + I2S_Private *i2sPrivate = &gI2sPrivate; + DMA_ChannelInitParam dmaParam; + HAL_Memset(&dmaParam, 0, sizeof(dmaParam)); + if (dir == PLAYBACK) { + + dmaParam.cfg = HAL_DMA_MakeChannelInitCfg(DMA_WORK_MODE_CIRCULAR, + DMA_WAIT_CYCLE_2, + DMA_BYTE_CNT_MODE_REMAIN, + DMA_DATA_WIDTH_16BIT, + DMA_BURST_LEN_1, + DMA_ADDR_MODE_FIXED, + DMA_PERIPH_DAUDIO, + DMA_DATA_WIDTH_16BIT, + DMA_BURST_LEN_1, + DMA_ADDR_MODE_INC, + DMA_PERIPH_SRAM); + + dmaParam.endArg = &(i2sPrivate->txReady); + dmaParam.halfArg = &(i2sPrivate->txReady); + } else { + + dmaParam.cfg = HAL_DMA_MakeChannelInitCfg(DMA_WORK_MODE_CIRCULAR, + DMA_WAIT_CYCLE_2, + DMA_BYTE_CNT_MODE_REMAIN, + DMA_DATA_WIDTH_16BIT, + DMA_BURST_LEN_1, + DMA_ADDR_MODE_INC, + DMA_PERIPH_SRAM, + DMA_DATA_WIDTH_16BIT, + DMA_BURST_LEN_1, + DMA_ADDR_MODE_FIXED, + DMA_PERIPH_DAUDIO); + + dmaParam.endArg = &(i2sPrivate->rxReady); + dmaParam.halfArg = &(i2sPrivate->rxReady); + } + dmaParam.irqType = DMA_IRQ_TYPE_BOTH; + dmaParam.endCallback = I2S_DMAEndCallback; + dmaParam.halfCallback = I2S_DMAHalfCallback; + HAL_DMA_Init(channel, &dmaParam); +} + +/** + * @internal + * @brief Enable or disable the specified i2s tx module. + * @param enable: specifies enable or disable. + * @retval None + */ +static void tx_enable(bool enable) +{ + I2S_Private *i2sPrivate = &gI2sPrivate; + /*clear tx tifo*/ + HAL_SET_BIT(I2S->DA_FCTL, I2S_TXFIFO_RESET_BIT); + + if (enable) { + if (i2sPrivate->txDMAChan != DMA_CHANNEL_INVALID) + HAL_SET_BIT(I2S->DA_INT, I2S_TXFIFO_DMA_ITEN_BIT); + } else { + if (i2sPrivate->txDMAChan != DMA_CHANNEL_INVALID) + HAL_CLR_BIT(I2S->DA_INT, I2S_TXFIFO_DMA_ITEN_BIT); + } +} + +/** + * @internal + * @brief Enable or disable the specified i2s rx module. + * @param enable: specifies enable or disable. + * @retval None + */ +static void rx_enable(bool enable) +{ + I2S_Private *i2sPrivate = &gI2sPrivate; + /*clear rx tifo*/ + HAL_SET_BIT(I2S->DA_FCTL, I2S_RXFIFO_RESET_BIT); + + if (enable) { + if (i2sPrivate->rxDMAChan != DMA_CHANNEL_INVALID) + HAL_SET_BIT(I2S->DA_INT, I2S_RXFIFO_DMA_ITEN_BIT); + } else { + + if (i2sPrivate->rxDMAChan != DMA_CHANNEL_INVALID) + HAL_CLR_BIT(I2S->DA_INT, I2S_RXFIFO_DMA_ITEN_BIT); + } +} + +/** + * @internal + * @brief Enable or disable the specified i2s module with DMA module. + * @param enable: specifies enable or disable. + * @param dir: the direction of stream. + * @retval None + */ +void HAL_I2S_Trigger(bool enable,I2S_StreamDir dir) +{ + I2S_Private *i2sPrivate = &gI2sPrivate; + HAL_MutexLock(&i2sPrivate->devTriggerLock, OS_WAIT_FOREVER); + if (enable) { + if (dir == PLAYBACK) { + /*trigger tx*/ + tx_enable(enable); + + /* start dma*/ + if (i2sPrivate->txDMAChan != DMA_CHANNEL_INVALID) { + I2S_DMAStart(i2sPrivate->txDMAChan, (uint32_t)i2sPrivate->txBuf, + (uint32_t)&(I2S->DA_TXFIFO), i2sPrivate->txLength); + } + i2sPrivate->txRunning = true; + } else { + rx_enable(enable); + if (i2sPrivate->rxDMAChan != DMA_CHANNEL_INVALID) + I2S_DMAStart(i2sPrivate->rxDMAChan, (uint32_t)&(I2S->DA_RXFIFO), + (uint32_t)i2sPrivate->rxBuf, i2sPrivate->rxLength); + i2sPrivate->rxRunning = true; + } + } else { + if (dir == PLAYBACK) { + tx_enable(enable); + if (i2sPrivate->txDMAChan != DMA_CHANNEL_INVALID) + I2S_DMAStop(i2sPrivate->txDMAChan); + i2sPrivate->txRunning = false; + } else { + rx_enable(enable); + if (i2sPrivate->rxDMAChan != DMA_CHANNEL_INVALID) + I2S_DMAStop(i2sPrivate->rxDMAChan); + i2sPrivate->rxRunning = false; + } + } + #if 0 + int i =0 ; + printf("\n"); + for (i = 0; i < 0x68; i = i+4) { + printf("0x%x: 0x%08x",i,(*(uint32_t *)(0x40042c00+i))); + printf("\n"); + } + #endif + HAL_MutexUnlock(&i2sPrivate->devTriggerLock); +} + +/** + * @brief Transmit an amount of data with DMA module + * @param buf: pointer to the Transmit data buffer. + * @param Size: number of data sample to be sent: + * @note if the data size less than half of dmabuf, return HAL_INVALID. when the size + * several times greater than half of dmabuf, only transfer its(half length of + * dmabuf)integer multiple + * @retval length of data transmited + */ +int32_t HAL_I2S_Write_DMA(uint8_t *buf, uint32_t size) +{ + I2S_Private *i2sPrivate = &gI2sPrivate; + if (!buf || size <= 0) + return HAL_INVALID; + uint8_t *pdata = buf; + uint8_t *lastWritePointer = NULL; + uint32_t toWrite = 0,writeSize = I2S_BUF_LENGTH/2; + uint8_t err_flag; /* temp solution to avoid outputing debug message when irq disabled */ + + if (size < writeSize) { + // I2S_INFO("Tx : size is too small....\n"); + return HAL_INVALID; + } + while (size > 0) { + if (size < writeSize) + break; + if (i2sPrivate->txRunning == false) { + if (!i2sPrivate->writePointer) + i2sPrivate->writePointer = i2sPrivate->txBuf; + + lastWritePointer = i2sPrivate->writePointer; + + I2S_MEMCPY(lastWritePointer, pdata, writeSize); + pdata += writeSize; + lastWritePointer += writeSize; + toWrite += writeSize; + size -= writeSize; + if (lastWritePointer >= i2sPrivate->txBuf + I2S_BUF_LENGTH) + lastWritePointer = i2sPrivate->txBuf; + i2sPrivate->writePointer = lastWritePointer; + if (i2sPrivate->writePointer == i2sPrivate->txBuf) { + I2S_DEBUG("Tx: play start...\n"); + HAL_I2S_Trigger(true,PLAYBACK);/*play*/ + i2sPrivate->txRunning =true; + } + } else { + err_flag = 0; + /*disable irq*/ + HAL_DisableIRQ(); + + lastWritePointer = i2sPrivate->writePointer; + if (i2sPrivate->txHalfCallCount && i2sPrivate->txEndCallCount) { + err_flag = 1; + i2sPrivate->txHalfCallCount = 0; + i2sPrivate->txEndCallCount = 0; + + if (i2sPrivate->txDmaPointer == i2sPrivate->txBuf) { + lastWritePointer = i2sPrivate->txBuf + I2S_BUF_LENGTH/2; + } else { + lastWritePointer = i2sPrivate->txBuf; + } + } else if (i2sPrivate->txHalfCallCount) { + i2sPrivate->txHalfCallCount --; + } else if (i2sPrivate->txEndCallCount) { + i2sPrivate->txEndCallCount --; + } else { + /**enable irq**/ + i2sPrivate->isTxSemaphore = true; + HAL_EnableIRQ(); + HAL_SemaphoreWait(&(i2sPrivate->txReady), HAL_WAIT_FOREVER); + /**disable irq**/ + HAL_DisableIRQ(); + if (i2sPrivate->txHalfCallCount) + i2sPrivate->txHalfCallCount --; + if (i2sPrivate->txEndCallCount) + i2sPrivate->txEndCallCount --; + } + + I2S_MEMCPY(lastWritePointer, pdata, writeSize); + pdata += writeSize; + lastWritePointer += writeSize; + toWrite += writeSize; + size -= writeSize; + if (lastWritePointer >= i2sPrivate->txBuf + I2S_BUF_LENGTH) + lastWritePointer = i2sPrivate->txBuf; + i2sPrivate->writePointer = lastWritePointer; + + /**enable irq**/ + HAL_EnableIRQ(); + if (err_flag) { + I2S_ERROR("TxCount:(H:%d,F:%d)\n",i2sPrivate->txHalfCallCount, + i2sPrivate->txEndCallCount); + I2S_ERROR("Tx : underrun....\n"); + } + } + } + return toWrite; +} + +/** + * @brief receive an amount of data with DMA module + * @param buf: pointer to the receive data buffer. + * @param Size: number of data sample to be receive: + * @note if the data size less than half of dmabuf, return HAL_INVALID. when the size + * several times greater than half of dmabuf, only transfer its(half length of + * dmabuf)integer multiple + * @retval length of data received + */ +int32_t HAL_I2S_Read_DMA(uint8_t *buf, uint32_t size) +{ + I2S_Private *i2sPrivate = &gI2sPrivate; + if (!buf || size <= 0) + return HAL_INVALID; + uint8_t *pdata = buf; + uint8_t *lastReadPointer = NULL; + uint32_t readSize = I2S_BUF_LENGTH/2; + uint32_t toRead = 0; + uint8_t err_flag; /* temp solution to avoid outputing debug message when irq disabled */ + + if ((size / readSize) < 1) { + I2S_ERROR("Rx buf size too small...\n"); + return HAL_INVALID; + } + + while (size > 0) { + + if (size/readSize < 1) + break; + if (i2sPrivate->rxRunning == false) { + + if (!i2sPrivate->readPointer) + i2sPrivate->readPointer = i2sPrivate->rxBuf; + I2S_DEBUG("Rx: record start...\n"); + HAL_I2S_Trigger(true,RECORD); + + } else { + err_flag = 0; + /*disable irq*/ + HAL_DisableIRQ(); + lastReadPointer = i2sPrivate->readPointer; + if (i2sPrivate->rxHalfCallCount && i2sPrivate->rxEndCallCount) { + err_flag = 1; + i2sPrivate->rxHalfCallCount = 0; + i2sPrivate->rxEndCallCount = 0; + + if (i2sPrivate->rxDmaPointer == i2sPrivate->rxBuf) { + lastReadPointer = i2sPrivate->txBuf + I2S_BUF_LENGTH/2; + } else { + lastReadPointer = i2sPrivate->txBuf; + } + } else if (i2sPrivate->rxHalfCallCount) { + i2sPrivate->rxHalfCallCount --; + } else if (i2sPrivate->rxEndCallCount) { + i2sPrivate->rxEndCallCount --; + } else { + /**enable irq**/ + i2sPrivate->isRxSemaphore = true; + HAL_EnableIRQ(); + HAL_SemaphoreWait(&(i2sPrivate->rxReady), HAL_WAIT_FOREVER); + /**disable irq**/ + HAL_DisableIRQ(); + if (i2sPrivate->rxHalfCallCount) + i2sPrivate->rxHalfCallCount --; + if (i2sPrivate->rxEndCallCount) + i2sPrivate->rxEndCallCount --; + } + I2S_MEMCPY(pdata, lastReadPointer, readSize); + pdata += readSize; + lastReadPointer += readSize; + if (lastReadPointer >= i2sPrivate->rxBuf + I2S_BUF_LENGTH) + lastReadPointer = i2sPrivate->rxBuf; + i2sPrivate->readPointer = lastReadPointer; + /**enable irq**/ + HAL_EnableIRQ(); + if (err_flag) { + I2S_ERROR("RxCount:(H:%d,F:%d)\n",i2sPrivate->rxHalfCallCount, + i2sPrivate->rxEndCallCount); + I2S_ERROR("Rx : overrun....\n"); + } + size -= readSize; + toRead += readSize; + } + } + return toRead; +} + +/** + * @brief Open the I2S module according to the specified parameters + * in the I2S_DataParam. + * @param param: pointer to a I2S_DataParam structure that contains + * data format information + * @retval HAL status + */ +HAL_Status HAL_I2S_Open(I2S_DataParam *param) +{ + I2S_Private *i2sPrivate = &gI2sPrivate; + + I2S_DataParam *dataParam = param; + + if (param->direction == PLAYBACK) { + if (i2sPrivate->isTxInitiate == true) { + I2S_ERROR("Tx device opened already.open faied...\n"); + return HAL_ERROR; + } + i2sPrivate->isTxInitiate = true; + + I2S_MEMCPY(&(i2sPrivate->pdataParam), param, sizeof(*param)); + } else { + if (i2sPrivate->isRxInitiate == true) { + I2S_ERROR("Rx device opened already.open faied...\n"); + return HAL_ERROR; + } + i2sPrivate->isRxInitiate = true; + + I2S_MEMCPY(&(i2sPrivate->cdataParam), param, sizeof(*param)); + } + I2S_BUF_LENGTH = dataParam->bufSize; + + if (param->direction == PLAYBACK) { + i2sPrivate->txDMAChan = DMA_CHANNEL_INVALID; + i2sPrivate->txLength = I2S_BUF_LENGTH; + i2sPrivate->txHalfCallCount = 0; + i2sPrivate->txEndCallCount = 0; +#ifdef RESERVERD_MEMORY_FOR_I2S_TX + i2sPrivate->txBuf = I2STX_BUF; +#else + i2sPrivate->txBuf = I2S_MALLOC(I2S_BUF_LENGTH); + if(i2sPrivate->txBuf) + I2S_MEMSET(i2sPrivate->txBuf,0,I2S_BUF_LENGTH); + else { + I2S_ERROR("Malloc tx buf(for DMA),faild...\n"); + return HAL_ERROR; + } +#endif + /*request DMA channel*/ + i2sPrivate->txDMAChan = HAL_DMA_Request(); + if (i2sPrivate->txDMAChan == DMA_CHANNEL_INVALID) { + I2S_ERROR("Obtain I2S tx DMA channel,faild...\n"); +#ifndef RESERVERD_MEMORY_FOR_I2S_TX + I2S_FREE(i2sPrivate->txBuf); +#endif + return HAL_ERROR; + } else + I2S_DMASet(i2sPrivate->txDMAChan, PLAYBACK); + HAL_SemaphoreInitBinary(&i2sPrivate->txReady); + } else { + i2sPrivate->rxDMAChan = DMA_CHANNEL_INVALID; + i2sPrivate->rxLength = I2S_BUF_LENGTH; + i2sPrivate->rxHalfCallCount = 0; + i2sPrivate->rxEndCallCount = 0; +#ifdef RESERVERD_MEMORY_FOR_I2S_RX + i2sPrivate->rxBuf = I2SRX_BUF; +#else + i2sPrivate->rxBuf = I2S_MALLOC(I2S_BUF_LENGTH); + if(i2sPrivate->rxBuf) + I2S_MEMSET(i2sPrivate->rxBuf,0,I2S_BUF_LENGTH); + else { + I2S_ERROR("Malloc rx buf(for DMA),faild...\n"); + return HAL_ERROR; + } +#endif + i2sPrivate->rxDMAChan = HAL_DMA_Request(); + if (i2sPrivate->rxDMAChan == DMA_CHANNEL_INVALID) { + I2S_ERROR("Obtain I2S rx DMA channel,faild...\n"); +#ifndef RESERVERD_MEMORY_FOR_I2S_TX + I2S_FREE(i2sPrivate->rxBuf); +#endif + return HAL_ERROR; + } else + I2S_DMASet(i2sPrivate->rxDMAChan, RECORD); + HAL_SemaphoreInitBinary(&i2sPrivate->rxReady); + } + + HAL_MutexLock(&i2sPrivate->devSetLock, OS_WAIT_FOREVER); + /*set bclk*/ + I2S_SET_ClkDiv(dataParam, i2sPrivate->hwParam); + + /*set sample resolution*/ + I2S_SET_SampleResolution(dataParam); + + /*set channel*/ + I2S_SET_Channels(dataParam); + + if (param->direction == PLAYBACK) { + I2S_EnableTx(); + } else { + I2S_EnableRx(); + } + HAL_MutexUnlock(&i2sPrivate->devSetLock); + + return HAL_OK; +} + +/** + * @brief Close the I2S module + * @note The module is closed at the end of transaction to avoid power consumption + * @retval none + */ +HAL_Status HAL_I2S_Close(uint32_t dir) +{ + I2S_Private *i2sPrivate = &gI2sPrivate; + + if (dir == PLAYBACK) { + HAL_I2S_Trigger(false,PLAYBACK); + I2S_DisableTx(); + i2sPrivate->txRunning = false; + i2sPrivate->isTxInitiate = false; + if (i2sPrivate->txDMAChan != DMA_CHANNEL_INVALID) { + HAL_DMA_DeInit(i2sPrivate->txDMAChan); + HAL_DMA_Release(i2sPrivate->txDMAChan); + i2sPrivate->txDMAChan = DMA_CHANNEL_INVALID; + + } + I2S_MEMSET(&(i2sPrivate->pdataParam), 0, sizeof(I2S_DataParam)); +#ifndef RESERVERD_MEMORY_FOR_I2S_TX + I2S_FREE(i2sPrivate->txBuf); +#endif + HAL_SemaphoreDeinit(&i2sPrivate->txReady); + i2sPrivate->txBuf = NULL; + i2sPrivate->txLength = 0; + i2sPrivate->writePointer = NULL; + i2sPrivate->txHalfCallCount = 0; + i2sPrivate->txEndCallCount = 0; + } else { + HAL_I2S_Trigger(false,RECORD); + I2S_DisableRx(); + i2sPrivate->isRxInitiate = false; + i2sPrivate->rxRunning = false; + if (i2sPrivate->rxDMAChan != DMA_CHANNEL_INVALID) { + HAL_DMA_DeInit(i2sPrivate->rxDMAChan); + HAL_DMA_Release(i2sPrivate->rxDMAChan); + i2sPrivate->rxDMAChan = DMA_CHANNEL_INVALID; + + } + I2S_MEMSET(&(i2sPrivate->cdataParam), 0, sizeof(I2S_DataParam)); +#ifndef RESERVERD_MEMORY_FOR_I2S_TX + I2S_FREE(i2sPrivate->rxBuf); +#endif + HAL_SemaphoreDeinit(&i2sPrivate->rxReady); + i2sPrivate->rxBuf = NULL; + i2sPrivate->rxLength = 0; + i2sPrivate->readPointer = NULL; + + i2sPrivate->rxHalfCallCount = 0; + i2sPrivate->rxEndCallCount = 0; + } + return HAL_OK; +} + +/** + * @internal + * @brief I2S PINS Init + * @retval HAL status + */ +static inline HAL_Status I2S_PINS_Init() +{ + return HAL_BoardIoctl(HAL_BIR_PINMUX_INIT, HAL_MKDEV(HAL_DEV_MAJOR_I2S, 0), 0); +} + +/** + * @internal + * @brief I2S PINS DeInit + * @retval HAL status + */ +static inline HAL_Status I2S_PINS_Deinit() +{ + return HAL_BoardIoctl(HAL_BIR_PINMUX_DEINIT, HAL_MKDEV(HAL_DEV_MAJOR_I2S, 0), 0); +} + +/** + * @internal + * @brief I2S hardware Init + * @param param: pointer to a I2S_HWParam structure that contains + * the configuration for clk/mode/format. + * @retval HAL status + */ +static inline HAL_Status I2S_HwInit(I2S_HWParam *param) +{ + if (!param) + return HAL_INVALID; + + /*config device clk source*/ + if (param->codecClk.isDevclk != 0) { + I2S_SET_Mclk(true,param->codecClk.clkSource, AUDIO_DEVICE_PLL); + } + + /* set lrck period /frame mode */ + HAL_MODIFY_REG(I2S->DA_FMT0, I2S_LRCK_PERIOD_MASK|I2S_LRCK_WIDTH_MASK| + I2S_SW_SEC_MASK, + I2S_LRCK_PERIOD(param->lrckPeriod)| + param->frameMode|I2S_SLOT_WIDTH_BIT32); + + /* set first transfer bit */ + HAL_MODIFY_REG(I2S->DA_FMT1, I2S_TX_MLS_MASK|I2S_RX_MLS_MASK| + I2S_PCM_TXMODE_MASK|I2S_PCM_RXMODE_MASK|I2S_SEXT_MASK, + param->txMsbFirst|param->rxMsbFirst| + I2S_TX_LINEAR_PCM|I2S_RX_LINEAR_PCM|I2S_ZERO_SLOT); + /* global enable */ + HAL_SET_BIT(I2S->DA_CTL, I2S_GLOBE_EN_BIT); + + HAL_MODIFY_REG(I2S->DA_FCTL, I2S_RXFIFO_LEVEL_MASK|I2S_TXFIFO_LEVEL_MASK| + I2S_RX_FIFO_MODE_SHIFT|I2S_TX_FIFO_MODE_MASK, + I2S_RXFIFO_TRIGGER_LEVEL(param->rxFifoLevel)| + I2S_TXFIFO_TRIGGER_LEVEL(param->txFifoLevel)| + I2S_RX_FIFO_MODE3|I2S_TX_FIFO_MODE1); + return I2S_SET_Format(param); +} + +/** + * @internal + * @brief I2S hardware DeInit + * @param param: pointer to a I2S_HWParam structure + * @retval HAL status + */ +static inline HAL_Status I2S_HwDeInit(I2S_HWParam *param) +{ + if (!param) + return HAL_INVALID; + /* global disable */ + HAL_CLR_BIT(I2S->DA_CTL, I2S_GLOBE_EN_BIT); + I2S_SET_Mclk(false, 0, 0); + return HAL_OK; +} + +#ifdef CONFIG_PM +static int i2s_suspend(struct soc_device *dev, enum suspend_state_t state) +{ + I2S_Private *i2sPrivate = &gI2sPrivate; + switch (state) { + case PM_MODE_SLEEP: + case PM_MODE_STANDBY: + case PM_MODE_HIBERNATION: + case PM_MODE_POWEROFF: + HAL_MutexLock(&i2sPrivate->devSetLock, OS_WAIT_FOREVER); + I2S_HwDeInit(i2sPrivate->hwParam); + HAL_MutexUnlock(&i2sPrivate->devSetLock); + + I2S_PINS_Deinit(); + HAL_CCM_DAUDIO_DisableMClock(); + HAL_CCM_BusDisablePeriphClock(CCM_BUS_PERIPH_BIT_DAUDIO); + HAL_PRCM_DisableAudioPLL(); + HAL_PRCM_DisableAudioPLLPattern(); + break; + default: + break; + } + return 0; +} + +static int i2s_resume(struct soc_device *dev, enum suspend_state_t state) +{ + I2S_Private *i2sPrivate = &gI2sPrivate; + + switch (state) { + case PM_MODE_SLEEP: + case PM_MODE_STANDBY: + case PM_MODE_HIBERNATION: + I2S_PINS_Init(); + /*init and enable clk*/ + I2S_PLLAUDIO_Update(I2S_PLL_22M); + HAL_PRCM_SetAudioPLLParam(i2sPrivate->audioPllParam); + HAL_PRCM_SetAudioPLLPatternParam(i2sPrivate->audioPllPatParam); + HAL_PRCM_EnableAudioPLL(); + HAL_PRCM_EnableAudioPLLPattern(); + + HAL_CCM_BusEnablePeriphClock(CCM_BUS_PERIPH_BIT_DAUDIO); + HAL_CCM_BusReleasePeriphReset(CCM_BUS_PERIPH_BIT_DAUDIO); + HAL_CCM_DAUDIO_SetMClock(AUDIO_PLL_SRC); + HAL_CCM_DAUDIO_EnableMClock(); + + HAL_MutexLock(&i2sPrivate->devSetLock, OS_WAIT_FOREVER); + I2S_HwInit(i2sPrivate->hwParam); + HAL_MutexUnlock(&i2sPrivate->devSetLock); + break; + default: + break; + } + return 0; +} + +static struct soc_device_driver i2s_drv = { + .name = "I2S", + .suspend = i2s_suspend, + .resume = i2s_resume, +}; + +static struct soc_device i2s_dev = { + .name = "I2S", + .driver = &i2s_drv, +}; + +#define I2S_DEV (&i2s_dev) +#else +#define I2S_DEV NULL +#endif + +/** + * @brief Initializes the I2S module according to the specified parameters + * in the I2S_Param. + * @param param: pointer to a I2S_Param structure that contains + * the configuration information for I2S + * @retval HAL status + */ +HAL_Status HAL_I2S_Init(I2S_Param *param) + +{ + int32_t ret = 0; + if (!param) + return HAL_INVALID; + I2S_Private *i2sPrivate = &gI2sPrivate; + + if (i2sPrivate->isHwInit == true) + return HAL_OK; + I2S_MEMSET(i2sPrivate,0,sizeof(*i2sPrivate)); + i2sPrivate->isHwInit = true; + + if (param->hwParam == NULL) + i2sPrivate->hwParam = &gHwParam; + else + i2sPrivate->hwParam = param->hwParam; + + HAL_MutexInit(&i2sPrivate->devSetLock); + HAL_MutexInit(&i2sPrivate->devTriggerLock); + + I2S_PINS_Init(); + + /*init and enable clk*/ + I2S_PLLAUDIO_Update(I2S_PLL_22M); + HAL_PRCM_SetAudioPLLParam(i2sPrivate->audioPllParam); + HAL_PRCM_EnableAudioPLL(); + HAL_PRCM_SetAudioPLLPatternParam(i2sPrivate->audioPllPatParam); + HAL_PRCM_EnableAudioPLLPattern(); + HAL_CCM_BusEnablePeriphClock(CCM_BUS_PERIPH_BIT_DAUDIO); + HAL_CCM_BusReleasePeriphReset(CCM_BUS_PERIPH_BIT_DAUDIO); + HAL_CCM_DAUDIO_SetMClock(AUDIO_PLL_SRC); + HAL_CCM_DAUDIO_EnableMClock(); + + HAL_MutexLock(&i2sPrivate->devSetLock, OS_WAIT_FOREVER); + ret = I2S_HwInit(i2sPrivate->hwParam); + +#ifdef CONFIG_PM + pm_register_ops(I2S_DEV); +#endif + HAL_MutexUnlock(&i2sPrivate->devSetLock); + return ret; +} + +#if 0 +void HAL_I2S_REG_DEBUG() +{ + int i = 0; + for (i = 0; i < 0x58; i = i+4) + printf("REG:0X%x,VAL:0X%x\n",i,(*((__IO uint32_t *)(0x40042c00+i)))); +} +#endif + +/** + * @brief DeInitializes the I2S module + * + * @retval none + */ +void HAL_I2S_DeInit() +{ + I2S_Private *i2sPrivate = &gI2sPrivate; + HAL_MutexLock(&i2sPrivate->devSetLock, OS_WAIT_FOREVER); + i2sPrivate->isHwInit = false; + I2S_HwDeInit(i2sPrivate->hwParam); + HAL_MutexUnlock(&i2sPrivate->devSetLock); + + HAL_MutexDeinit(&i2sPrivate->devSetLock); + HAL_MutexDeinit(&i2sPrivate->devTriggerLock); + I2S_PINS_Deinit(); + + HAL_CCM_DAUDIO_DisableMClock(); + HAL_CCM_BusDisablePeriphClock(CCM_BUS_PERIPH_BIT_DAUDIO); + HAL_PRCM_DisableAudioPLL(); + HAL_PRCM_DisableAudioPLLPattern(); + + I2S_MEMSET(i2sPrivate,0,sizeof(struct I2S_Private *)); +} diff --git a/platform/mcu/xr871/src/driver/chip/hal_irrx.c b/platform/mcu/xr871/src/driver/chip/hal_irrx.c new file mode 100644 index 0000000000..61b627becc --- /dev/null +++ b/platform/mcu/xr871/src/driver/chip/hal_irrx.c @@ -0,0 +1,426 @@ +/** + * @file hal_irrx.c + * @author XRADIO IOT WLAN Team + */ + +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include "driver/chip/hal_irrx.h" +#include "driver/chip/ir_nec.h" +#include "hal_base.h" +#include "pm/pm.h" + +#define HAL_DBG_IRRX 0 + +#if (HAL_DBG_IRRX == 1) +#define HAL_IRRX_DBG(fmt, arg...) HAL_LOG(HAL_DBG_ON && HAL_DBG_IRRX, "[IRRX] "fmt, ##arg) +#define irrx_hex_dump_bytes(...) print_hex_dump_bytes(__VA_ARGS__) +#define IRRX_INF HAL_IRRX_DBG +#define IRRX_WRN HAL_WRN +#define IRRX_ERR HAL_ERR +#else +#define HAL_IRRX_DBG(fmt, arg...) +#define irrx_hex_dump_bytes(...) +#define IRRX_INF(...) +#define IRRX_WRN(...) +#define IRRX_ERR(...) +#endif + +/* reg 0x00: Bit definition for cir receiver control register */ +#define IRRX_GLOBAL_EN (0x00000001) /* bit0 */ +#define IRRX_RX_EN (0x00000002) /* bit1 */ +#define IRRX_CIR_EN (0x00000030) /* bit4~bit5 */ + +/* reg 0x2c: Bit definition for cir receiver interrupt control */ +#define IRRX_RXFIFO_MASK (0x000000FF) /* bit0~bit7 */ +#define IR_FIFO_SIZE (64) +#define IR_FIFO_TRIGER (32) + +#define IRRX_IT_ROI (0x00000001) /* bit0 receiver fifo overrun interrupt enable */ +#define IRRX_IT_RPEI (0x00000002) /* bit1 receiver packet end interrupt enable */ +#define IRRX_IT_RAI (0x00000010) /* bit4 RX FIFO Available interrupt enable */ +#define IRRX_IT_DRQ (0x00000020) /* bit5 RX FIFO DMA enable */ +#define IRRX_IT_MASK (IRRX_IT_ROI | IRRX_IT_RPEI | IRRX_IT_RAI | IRRX_IT_DRQ) + +#define IRRX_SET_RAL_MASK (0x00003F00) //bit8~bit13 RX FIFO available received byte level +#define IRRX_RAL_INDEX (8) + +#define IRRX_STATUS_STAT (0x00000080) /* bit7 */ +#define IRRX_STATUS_RAC_MASK (0x00003F00) /* bit8~bit14 */ + +/* reg 0x30: Bit definition for cir receiver status */ +#define IRRX_FLAG_ROI (0x00000001) /* Rx FIFO Overflow */ +#define IRRX_FLAG_RPE (0x00000002) /* Rx Packet End */ +#define IRRX_FLAG_RA (0x00000010) /* Rx FIFO Data Available */ +#define IRRX_FLAG_MASK (IRRX_FLAG_ROI | IRRX_FLAG_RPE | IRRX_FLAG_RA) + +/* reg 0x34: Bit definition for cir receiver sample clock select prescaler */ +#define IRRX_SCS_DIV_64 (0x00000000) +#define IRRX_SCS_DIV_128 (0x00000001) +#define IRRX_SCS_DIV_256 (0x00000002) +#define IRRX_SCS_DIV_512 (0x00000003) +#define IRRX_SCS_DIV_NONE (0x01000000) +#define IRRX_SCS_DIV_MASK (0x00010003) /* clear mask */ + +#define IRRX_NTHR_MASK (0x000000FC) /* bit2~bit7 */ +#define IRRX_NTHR_INDEX (2) + +#define IRRX_ITHR_MASK (0x0000FF00) /* bit8~bit15 */ +#define IRRX_ITHR_INDEX (8) + +#define IRRX_ATHR_MASK (0x007F0000) /* bit16~bit22 */ +#define IRRX_ATHR_INDEX (16) + +#define IRRX_ATHC_CLOCKUNIT_1 (0) +#define IRRX_ATHC_CLOCKUNIT_128 (1) +#define IRRX_ATHC_INDEX (23) + +#if defined (IR_CLK_32K_USED) +#define IRRX_32K_SAMPLE_CLK IRRX_SCS_DIV_NONE /* sample_freq = 32768Hz/1=32768Hz (30.5us) */ +#define IRRX_32K_RXFILT_VAL (11) /* NosieTreshold, Filter Threshold = 11*30.5 = ~335.5us < 500us */ +#define IRRX_32K_RXIDLE_VAL (3) /* IdleTreshold, Idle Threshold = (3+1)*128*30.5 = ~16.4ms > 9ms */ +#define IRRX_32K_APB_PERIPH_CLK_SRC CCM_APB_PERIPH_CLK_SRC_LFCLK +#define IRRX_32K_CCM_PERIPH_CLK_DIV_N CCM_PERIPH_CLK_DIV_N_1 +#define IRRX_32K_CCM_PERIPH_CLK_DIV_M CCM_PERIPH_CLK_DIV_M_1 +#else +#define IRRX_26M_SAMPLE_CLK IRRX_SCS_DIV_128 /* 26MHz/8/128=25390.6Hz (39.4us) */ +#define IRRX_26M_RXFILT_VAL (8) /* NosieTreshold, Filter Threshold = 8*39.4 = ~315us < 500us */ +#define IRRX_26M_RXIDLE_VAL (3) /* IdleTreshold, Idle Threshold = (3+1)*128*39.4 = ~20.2ms > 9ms */ +#define IRRX_26M_APB_PERIPH_CLK_SRC CCM_APB_PERIPH_CLK_SRC_HFCLK +#define IRRX_26M_CCM_PERIPH_CLK_DIV_N CCM_PERIPH_CLK_DIV_N_1 +#define IRRX_26M_CCM_PERIPH_CLK_DIV_M CCM_PERIPH_CLK_DIV_M_8 + +#define IRRX_24M_SAMPLE_CLK IRRX_SCS_DIV_128 /* 24MHz/8/128=23437.5Hz (42.7us) */ +#define IRRX_24M_RXFILT_VAL (8) /* NosieTreshold, Filter Threshold = 8*42.7 = ~341us < 500us */ +#define IRRX_24M_RXIDLE_VAL (2) /* IdleTreshold, Idle Threshold = (2+1)*128*42.7 = ~16.4ms > 9ms */ +#define IRRX_24M_APB_PERIPH_CLK_SRC CCM_APB_PERIPH_CLK_SRC_HFCLK +#define IRRX_24M_CCM_PERIPH_CLK_DIV_N CCM_PERIPH_CLK_DIV_N_1 +#define IRRX_24M_CCM_PERIPH_CLK_DIV_M CCM_PERIPH_CLK_DIV_M_8 +#endif + +typedef struct +{ + __IO uint32_t CR; /* 0x00, IRRX Control */ + uint32_t RESERVED0[3]; + __IO uint32_t CFR; /* 0x10, IRRX Configure */ + uint32_t RESERVED1[3]; + __IO uint32_t DR; /* 0x20, IRRX FIFO */ + uint32_t RESERVED2[2]; + __IO uint32_t ICR; /* 0x2C, IRRX Interrupt Control */ + __IO uint32_t SR; /* 0x30, IRRX Status */ + __IO uint32_t CCR; /* 0x34, IRRX Control Configure */ +} IRRX_TypeDef; + +/* IRRX State structures definition */ +typedef enum +{ + IRRX_STATE_RESET = 0x00, /* Peripheral is not yet Initialized */ + IRRX_STATE_READY = 0x02, /* Peripheral Initialized and ready for use */ + IRRX_STATE_BUSY = 0x04, /* An internal process is ongoing */ + IRRX_STATE_ERROR = 0x08 /* Error */ +} IRRX_StateTypeDef; + +#define IRRX_RAW_BUF_SIZE 128 /* 256 */ + +/* IRRX handle Structure definition */ +struct IRRX_HandleDef +{ + IRRX_TypeDef *Instance; /* IRRX registers base address */ + uint32_t rxCnt; /* IRRX Rx Transfer layer Counter */ + uint8_t rxBuff[IRRX_RAW_BUF_SIZE]; /* Pointer to IRRX Rx transfer Buffer */ + IRRX_StateTypeDef State; /* IRRX communication state */ + IRRX_RxCpltCallback rxCpltCallback; +}; + +static HAL_Status IRRX_ReadFIFO_RawData(IRRX_HandleTypeDef *irrx, uint32_t ralcnt) +{ + uint32_t i, tmp; + + if (ralcnt == 0) + return HAL_INVALID; + + for (i = 0; i < ralcnt; i++) { + if (irrx->rxCnt >= IRRX_RAW_BUF_SIZE) + tmp = irrx->Instance->DR; + else + irrx->rxBuff[irrx->rxCnt++] = (uint8_t)(irrx->Instance->DR); + } + + (void)tmp; + + return HAL_OK; +} + +static IRRX_HandleTypeDef hal_irrx; + +static void IRRX_IRQHandler(void) +{ + uint32_t intsta, dcnt; + IRRX_HandleTypeDef *irrx = &hal_irrx; + + intsta = irrx->Instance->SR; + irrx->Instance->SR = intsta & IRRX_FLAG_MASK; + IRRX_INF("%s,%d %x\n", __func__, __LINE__, intsta); + + dcnt = (intsta & IRRX_SET_RAL_MASK) >> IRRX_RAL_INDEX; + IRRX_ReadFIFO_RawData(irrx, dcnt); + + irrx->State = IRRX_STATE_READY; + + if (intsta & IRRX_FLAG_RPE) { /* Packet End */ + uint32_t code; + int32_t code_valid; + + if (irrx->rxCnt >= IRRX_RAW_BUF_SIZE) { + IRRX_INF("Raw Buffer Full!!\n"); + irrx->rxCnt = 0; + return ; + } + + code = IRRX_NECPacket_DeCode(irrx->rxBuff, irrx->rxCnt); + irrx->rxCnt = 0; + code_valid = IRRX_NECCode_Valid(code); + + if (code_valid) { /* report keycode */ + irrx->rxCpltCallback(code & 0xff, (code >> 16) & 0xff); + } /* else retry other protocal */ + } + + if (intsta & IRRX_FLAG_ROI) { /* FIFO Overflow */ + IRRX_INF("Rx FIFO Overflow!!\n"); + irrx->rxCnt = 0; + } +} + +static void IRRX_SetConfig(IRRX_TypeDef *Instance, IRRX_InitTypeDef *param) +{ +#if !defined (IR_CLK_32K_USED) + uint32_t clk = HAL_GetHFClock(); +#endif + Instance->CR |= IRRX_CIR_EN; /* Enable IR Mode */ + +#if defined (IR_CLK_32K_USED) + Instance->CCR = (IRRX_32K_RXFILT_VAL << IRRX_NTHR_INDEX) | \ + (IRRX_32K_RXIDLE_VAL << IRRX_ITHR_INDEX) | \ + (IRRX_32K_ACTIVE_T << IRRX_ATHR_INDEX) | \ + (IRRX_32K_ACTIVE_T_C << IRRX_ATHC_INDEX) | \ + IRRX_32K_SAMPLE_CLK; +#else + if (clk == HOSC_CLOCK_26M) { + Instance->CCR = (IRRX_26M_RXFILT_VAL << IRRX_NTHR_INDEX) | \ + (IRRX_26M_RXIDLE_VAL << IRRX_ITHR_INDEX) | \ + (IRRX_26M_ACTIVE_T << IRRX_ATHR_INDEX) | \ + (IRRX_26M_ACTIVE_T_C << IRRX_ATHC_INDEX) | \ + IRRX_26M_SAMPLE_CLK; + } else if (clk == HOSC_CLOCK_24M) { + Instance->CCR = (IRRX_24M_RXFILT_VAL << IRRX_NTHR_INDEX) | \ + (IRRX_24M_RXIDLE_VAL << IRRX_ITHR_INDEX) | \ + (IRRX_24M_ACTIVE_T << IRRX_ATHR_INDEX) | \ + (IRRX_24M_ACTIVE_T_C << IRRX_ATHC_INDEX) | \ + IRRX_24M_SAMPLE_CLK; + } else { + IRRX_ERR("%s unknow clk type(%d)!\n", __func__, clk); + } +#endif + Instance->ICR &= ~IRRX_SET_RAL_MASK; + Instance->ICR |= (IR_FIFO_TRIGER - 1) << IRRX_RAL_INDEX; + + Instance->CFR = param->PulsePolariyInvert; + Instance->SR = IRRX_FLAG_MASK; + + Instance->ICR |= IRRX_IT_ROI | IRRX_IT_RPEI | IRRX_IT_RAI; + + Instance->CR |= IRRX_GLOBAL_EN | IRRX_RX_EN; /* enable ir module */ +} + +#ifdef CONFIG_PM +static IRRX_InitTypeDef hal_irrx_param; +static IRRX_StateTypeDef hal_irrx_suspending = 0; + +static int irrx_suspend(struct soc_device *dev, enum suspend_state_t state) +{ + hal_irrx_suspending = 1; + + switch (state) { + case PM_MODE_SLEEP: + break; + case PM_MODE_STANDBY: + case PM_MODE_HIBERNATION: + case PM_MODE_POWEROFF: + HAL_IRRX_DeInit(dev->platform_data); + IRRX_INF("%s okay\n", __func__); + break; + default: + break; + } + + return 0; +} + +static int irrx_resume(struct soc_device *dev, enum suspend_state_t state) +{ + switch (state) { + case PM_MODE_SLEEP: + break; + case PM_MODE_STANDBY: + case PM_MODE_HIBERNATION: + HAL_IRRX_Init(&hal_irrx_param); + IRRX_INF("%s okay\n", __func__); + break; + default: + break; + } + + hal_irrx_suspending = 0; + + return 0; +} + +static struct soc_device_driver irrx_drv = { + .name = "irrx", + .suspend_noirq = irrx_suspend, + .resume_noirq = irrx_resume, +}; + +static struct soc_device irrx_dev = { + .name = "irrx", + .driver = &irrx_drv, +}; + +#define IRRX_DEV (&irrx_dev) +#else +#define IRRX_DEV NULL +#endif + +/** + * @brief Initializes the IRRX peripheral. + * @param param: + * @arg param->[in] The configuration information. + * @retval IRRX handler. + */ +IRRX_HandleTypeDef *HAL_IRRX_Init(IRRX_InitTypeDef *param) +{ +#if !defined (IR_CLK_32K_USED) + uint32_t clk = HAL_GetHFClock(); +#endif + IRRX_HandleTypeDef *irrx = &hal_irrx; + + HAL_ASSERT_PARAM(param->rxCpltCallback); + + irrx->Instance = (IRRX_TypeDef *)IRRX_BASE; + irrx->rxCpltCallback = param->rxCpltCallback; + + if (irrx->State != IRRX_STATE_RESET) { + IRRX_ERR("%s err state:%d\n", __func__, irrx->State); + return NULL; + } + + HAL_BoardIoctl(HAL_BIR_PINMUX_INIT, HAL_MKDEV(HAL_DEV_MAJOR_IRRX, 0), 0); + +#if defined (IR_CLK_32K_USED) + HAL_CCM_IRRX_SetMClock(IRRX_32K_APB_PERIPH_CLK_SRC, + IRRX_32K_CCM_PERIPH_CLK_DIV_N, \ + IRRX_32K_CCM_PERIPH_CLK_DIV_M); +#else + if (clk == HOSC_CLOCK_26M) { + HAL_CCM_IRRX_SetMClock(IRRX_26M_APB_PERIPH_CLK_SRC, + IRRX_26M_CCM_PERIPH_CLK_DIV_N, \ + IRRX_26M_CCM_PERIPH_CLK_DIV_M); + } else if (clk == HOSC_CLOCK_24M) { + HAL_CCM_IRRX_SetMClock(IRRX_24M_APB_PERIPH_CLK_SRC, + IRRX_24M_CCM_PERIPH_CLK_DIV_N, \ + IRRX_24M_CCM_PERIPH_CLK_DIV_M); + } else { + IRRX_ERR("%s unknow clk type(%d)!\n", __func__, clk); + } +#endif + HAL_CCM_IRRX_EnableMClock(); + HAL_CCM_BusForcePeriphReset(CCM_BUS_PERIPH_BIT_IRRX); + HAL_CCM_BusReleasePeriphReset(CCM_BUS_PERIPH_BIT_IRRX); + HAL_CCM_BusEnablePeriphClock(CCM_BUS_PERIPH_BIT_IRRX); + + IRRX_SetConfig(irrx->Instance, param); + irrx->Instance->SR = IRRX_FLAG_MASK; /* Clear All Rx Interrupt Status */ + + irrx->State = IRRX_STATE_READY; + irrx->rxCnt = 0; +#ifdef CONFIG_PM + if (!hal_irrx_suspending) { + memcpy(&hal_irrx_param, param, sizeof(IRRX_InitTypeDef)); + IRRX_DEV->platform_data = irrx; + pm_register_ops(IRRX_DEV); + } +#endif + HAL_NVIC_SetIRQHandler(IRRX_IRQn, IRRX_IRQHandler); + NVIC_EnableIRQ(IRRX_IRQn); + + return irrx; +} + +/** + * @brief DeInitializes the IRRX peripheral. + * @param irrx: + * @arg irrx->[in] IRRX handler. + * @return None. + */ +void HAL_IRRX_DeInit(IRRX_HandleTypeDef *irrx) +{ + HAL_ASSERT_PARAM(irrx); + + if (irrx->State != IRRX_STATE_READY) { + IRRX_ERR("%s err state:%d\n", __func__, irrx->State); + return ; + } + + NVIC_DisableIRQ(IRRX_IRQn); + +#ifdef CONFIG_PM + if (!hal_irrx_suspending) { + pm_unregister_ops(IRRX_DEV); + IRRX_DEV->platform_data = NULL; + } +#endif + + /* Disable the Peripheral */ + irrx->Instance->CR &= ~IRRX_GLOBAL_EN; + + HAL_CCM_BusDisablePeriphClock(CCM_BUS_PERIPH_BIT_IRRX); + HAL_CCM_BusForcePeriphReset(CCM_BUS_PERIPH_BIT_IRRX); + HAL_CCM_IRRX_DisableMClock(); + + HAL_BoardIoctl(HAL_BIR_PINMUX_DEINIT, HAL_MKDEV(HAL_DEV_MAJOR_IRRX, 0), 0); + + irrx->State = IRRX_STATE_RESET; +} + +#undef IRRX_INF +#undef IRRX_WRN +#undef IRRX_ERR diff --git a/platform/mcu/xr871/src/driver/chip/hal_irtx.c b/platform/mcu/xr871/src/driver/chip/hal_irtx.c new file mode 100644 index 0000000000..736214b70b --- /dev/null +++ b/platform/mcu/xr871/src/driver/chip/hal_irtx.c @@ -0,0 +1,502 @@ +/** + * @file hal_irtx.c + * @author XRADIO IOT WLAN Team + */ + +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include "driver/chip/hal_irtx.h" +#include "driver/chip/ir_nec.h" +#include "hal_base.h" +#include "sys/interrupt.h" +#include "pm/pm.h" + +#define HAL_DBG_IRTX 0 + +#if (HAL_DBG_IRTX == 1) +#define HAL_IRTX_DBG(fmt, arg...) HAL_LOG(HAL_DBG_ON && HAL_DBG_IRTX, "[IRTX] "fmt, ##arg) +#define irtx_hex_dump_bytes(...) print_hex_dump_bytes(__VA_ARGS__) +#define IRTX_INF HAL_IRTX_DBG +#define IRTX_WRN HAL_WRN +#define IRTX_ERR HAL_ERR +#else +#define HAL_IRTX_DBG(fmt, arg...) +#define irtx_hex_dump_bytes(...) +#define IRTX_INF HAL_IRTX_DBG +#define IRTX_WRN(...) +#define IRTX_ERR(...) +#endif + +/* Bit definition for cir Transmit Global register */ +#define IRTX_RESET (1<<1) + +#define IRTX_TXEN (1) + +/* Bit definition for cir Transmit Control register */ +#define IRTX_RCS_DIV_2 (1<<1) +#define IRTX_RCS_DIV_4 (2<<1) +#define IRTX_RCS_DIV_8 (3<<1) +#define IRTX_RCS_DIV_64 (4<<1) +#define IRTX_RCS_DIV_128 (5<<1) +#define IRTX_RCS_DIV_256 (6<<1) +#define IRTX_RCS_DIV_512 (7<<1) +#define IRTX_RCS_DIV_MASK (7<<1) + +#define IRTX_CSS_EN (1<<7) + +/* Bit definition for cir Transmit Interrupt Control register */ +#define IRTX_IT_TAI (1<<1) /* TX FIFO Available Interrupt Enable */ +#define IRTX_IT_TPE (1) /* Transmit Packet End Interrupt Enable for Cyclical Pulse */ +#define IRTX_IT_TUI (1) /* Transmit FIFO under run Interrupt Enable for Non-cyclical Pulse */ + +/* Bit definition for cir Transmit Status register */ +#define IRTX_FLAG_TPE (1) /* Transmitter Packet End Flag for Cyclical Pulse */ +#define IRTX_FLAG_TUR (1) /* Transmitter FIFO Under Run Flag for Non-cyclical Pulse */ +#define IRTX_FLAG_TAI (1<<1) /* TX FIFO Available Interrupt Flag */ + +#define IRTX_STCT (1<<3) /* Status of CIR Transmitter */ + +#define IRTX_CAVE_FREQ (38000) /* actually is 6.5M/IRTX_MC_VALUE=38011.7Hz, T=26.3uS */ +#if (defined IR_CLK_32K_USED) +#define IRTX_32K_APB_PERIPH_CLK_SRC CCM_APB_PERIPH_CLK_SRC_LFCLK +#define IRTX_32K_CCM_PERIPH_CLK_DIV_N CCM_PERIPH_CLK_DIV_N_1 +#define IRTX_32K_CCM_PERIPH_CLK_DIV_M CCM_PERIPH_CLK_DIV_M_1 +#define IRTX_32K_CCM_PERIPH_CLK (32768) +#define IRTX_32K_MC_VALUE (1) /* 1 */ +#define IRTX_32K_SAMPLE IRTX_RCS_DIV_2 /* IRTX_CCM_PERIPH_CLK/2=16.4KHz, Ts=61uS */ +#else +#define IRTX_26M_APB_PERIPH_CLK_SRC CCM_APB_PERIPH_CLK_SRC_HFCLK +#define IRTX_26M_CCM_PERIPH_CLK_DIV_N CCM_PERIPH_CLK_DIV_N_1 +#define IRTX_26M_CCM_PERIPH_CLK_DIV_M CCM_PERIPH_CLK_DIV_M_8 /* 26M/8=3.25M */ +#define IRTX_26M_CCM_PERIPH_CLK (26000000/8) +#define IRTX_26M_MC_VALUE (IRTX_26M_CCM_PERIPH_CLK/IRTX_CAVE_FREQ) /* 3.25MHz/IRTX_CAVE_FREQ=85 */ +#define IRTX_26M_SAMPLE IRTX_RCS_DIV_128 /* IRTX_CCM_PERIPH_CLK/128=25.4KHz, Ts=39.4uS */ + +#define IRTX_24M_APB_PERIPH_CLK_SRC CCM_APB_PERIPH_CLK_SRC_HFCLK +#define IRTX_24M_CCM_PERIPH_CLK_DIV_N CCM_PERIPH_CLK_DIV_N_1 +#define IRTX_24M_CCM_PERIPH_CLK_DIV_M CCM_PERIPH_CLK_DIV_M_4 /* 24M/4=6M */ +#define IRTX_24M_CCM_PERIPH_CLK (24000000/4) +#define IRTX_24M_MC_VALUE (IRTX_24M_CCM_PERIPH_CLK/IRTX_CAVE_FREQ) /* 6MHz/IRTX_CAVE_FREQ=157 */ +#define IRTX_24M_SAMPLE IRTX_RCS_DIV_256 /* IRTX_CCM_PERIPH_CLK/256=23.4KHz, Ts=43.7uS */ +#endif + +#define IR_TX_FIFO_SIZE (128) + +typedef struct +{ + __IO uint32_t TGR; /* 0x00 IRTX Global Register */ + __IO uint32_t TMCR; /* 0x04 IRTX Modulationg Control register */ + __IO uint32_t TCR; /* 0x08 IRTX Control Register */ + __IO uint32_t IDCNT_H; /* 0x0C IRTX Idle Duration threshold Resgister High */ + __IO uint32_t IDCNT_L; /* 0x10 IRTX Idle Duration threshold Resgister Low */ + __IO uint32_t TICNT_H; /* 0x14 IRTX Idle Counter Resgister High */ + __IO uint32_t TICNT_L; /* 0x18 IRTX Idle Counter Resgister Low */ + uint32_t RESERVED0[1]; + __IO uint32_t TER; /* 0x20 IRTX FIFO empty Level Register */ + __IO uint32_t TICR; /* 0x24 IRTX Interrupt Control Register */ + __IO uint32_t TACR; /* 0x28 IRTX FIFO Available Counter Register */ + __IO uint32_t SR; /* 0x2C IRTX Status Register */ + __IO uint32_t TXTR; /* 0x30 IRTX Threshold Register */ + __IO uint32_t DMACR; /* 0x34 IRTX DMA Control Register */ + uint32_t RESERVED1[2]; + uint32_t RESERVED2[4]; /* 0x40 */ + uint32_t RESERVED3[4]; /* 0x50 */ + uint32_t RESERVED4[4]; /* 0x60 */ + uint32_t RESERVED5[4]; /* 0x70 */ + __IO uint32_t DR; /* 0x80 IRTX Trainsmit FIFO Data Register */ +} IRTX_TypeDef; + +/* IRTX State structures definition */ +typedef enum +{ + IRTX_STATE_RESET = 0x00, /* Peripheral is not initialized */ + IRTX_STATE_READY = 0x01, /* Peripheral Initialized and ready for use */ + IRTX_STATE_BUSY = 0x02, /* An internal process is ongoing */ + IRTX_STATE_ERROR = 0x04 /* Error */ +} IRTX_StateTypeDef; + +#define IRTX_BUFFER_SIZE 256 + +/* IRTX handle Structure definition */ +struct IRTX_HandleDef +{ + IRTX_TypeDef *Instance; /* IRTX registers base address */ + IRTX_TTS_Type SendModeType; /* signal or cyclical mode */ + uint32_t CyclicalCnt; /* count of cyclical mode */ + uint32_t CyclicalTxCnt; /* sended count of cyclical mode */ + uint16_t TxNum; /* Used when send data more than 128 bytes */ + uint8_t txBuff[IRTX_BUFFER_SIZE]; /* Pointer to IRRX Rx transfer Buffer */ + uint16_t txCnt; /* IRRX Rx Transfer layer Counter */ + IRTX_StateTypeDef State; /* IRTX communication state */ + IRTX_Proto_Code Protos[IRTX_PROTO_NUM]; +}; + +static IRTX_HandleTypeDef hal_irtx; + +static void HAL_IRTX_CycMode_Stop(IRTX_HandleTypeDef *irtx) +{ + if (irtx->SendModeType == IRTX_TTS_CYCLICAL) { + irtx->Instance->TCR &= ~IRTX_CSS_EN; + irtx->Instance->TGR |= IRTX_RESET; + while (irtx->Instance->TGR & IRTX_RESET) + ; + + irtx->State = IRTX_STATE_READY; + } +} + +static void IRTX_IRQHandler(void) +{ + uint32_t intsta; + IRTX_HandleTypeDef *irtx = &hal_irtx; + + intsta = irtx->Instance->SR; + irtx->Instance->SR = intsta; + //IRTX_INF("%s,%d %x\n", __func__, __LINE__, intsta); + + if (irtx->SendModeType == IRTX_TTS_CYCLICAL) { + irtx->Instance->TICNT_L = 0; + irtx->Instance->TICNT_H = 0; + if (++irtx->CyclicalTxCnt >= irtx->CyclicalCnt) { + HAL_IRTX_CycMode_Stop(irtx); + } + return ; + } + + if (intsta & IRTX_FLAG_TPE) { + irtx->Instance->TCR &= ~IRTX_CSS_EN; + irtx->Instance->TICR &= ~IRTX_FLAG_TPE; + irtx->State = IRTX_STATE_READY; + } + if (intsta & IRTX_FLAG_TAI) { + while (irtx->TxNum < irtx->txCnt) { + if (irtx->Instance->TACR == 0) { + IRTX_WRN("%s,%d\n", __func__, __LINE__); + return ; + } + irtx->Instance->DR = irtx->txBuff[irtx->TxNum++]; + } + if (irtx->TxNum >= irtx->txCnt) + irtx->Instance->TICR &= ~IRTX_FLAG_TAI; + } +} + +static void IRTX_SetConfig(IRTX_TypeDef *Instance, IRTX_InitTypeDef *Init) +{ +#if !defined (IR_CLK_32K_USED) + uint32_t clk = HAL_GetHFClock(); +#endif + + HAL_ASSERT_PARAM(((Init->ModulateDutyLevel) == IRTX_DRMC_TIME_1) || \ + ((Init->ModulateDutyLevel) == IRTX_DRMC_TIME_2) || \ + ((Init->ModulateDutyLevel) == IRTX_DRMC_TIME_3)); + HAL_ASSERT_PARAM(((Init->SendModeType) == IRTX_TTS_NONE) || \ + ((Init->SendModeType) == IRTX_TTS_CYCLICAL)); + HAL_ASSERT_PARAM(((Init->PulsePolarity) == IRTX_TPPI_NONE) || \ + ((Init->PulsePolarity) == IRTX_TPPI_INVERT)); + +#if defined (IR_CLK_32K_USED) + Instance->TMCR = IRTX_32K_MC_VALUE; + + Instance->TCR &= ~IRTX_RCS_DIV_MASK; + Instance->TCR |= IRTX_32K_SAMPLE; +#else + if (clk == HOSC_CLOCK_26M) { + Instance->TMCR = IRTX_26M_MC_VALUE; + + Instance->TCR &= ~IRTX_RCS_DIV_MASK; + Instance->TCR |= IRTX_26M_SAMPLE; + } else if (clk == HOSC_CLOCK_24M) { + Instance->TMCR = IRTX_24M_MC_VALUE; + + Instance->TCR &= ~IRTX_RCS_DIV_MASK; + Instance->TCR |= IRTX_24M_SAMPLE; + } +#endif + + Instance->TGR &= ~IRTX_DRMC_TIME_MASK; + Instance->TGR |= Init->ModulateDutyLevel; + + Instance->TCR &= ~IRTX_TTS_MASK; + Instance->TCR |= Init->SendModeType; + + Instance->TGR &= ~IRTX_TPPI_MASK; + Instance->TGR |= Init->PulsePolarity; + + Instance->IDCNT_H = (Init->IdleDurationCnt >> 8) & 0x0F; + Instance->IDCNT_L = Init->IdleDurationCnt & 0xff; + + Instance->TGR |= Init->InternalModulation; + + Instance->TER &= ~0x000000FF; + Instance->TER |= IR_TX_FIFO_SIZE; + + Instance->SR = IRTX_FLAG_TAI | IRTX_FLAG_TPE; + + Instance->TXTR = IR_TX_FIFO_SIZE; + + Instance->TGR |= IRTX_TXEN; +} + +#define MIN(x , y) (((x) < (y)) ? (x) : (y)) + +/** + * @brief Send a code by IRTX peripheral. + * @param irtx: + * @arg irtx-> IRTX handler. + * @param protos_sel: + * @arg protos_sel->[in] The protocal used. + * @param ir_tx_code: + * @arg ir_tx_code->[in] The add and key(add|~addr|key|~key) will send. + * @retval None. + */ +void HAL_IRTX_Transmit(IRTX_HandleTypeDef *irtx, uint32_t protos_sel, uint32_t ir_tx_code) +{ + uint32_t i = 0; + unsigned long flags; + + IRTX_INF("%s %x\n", __func__, ir_tx_code); + if (irtx->State != IRTX_STATE_READY) { + IRTX_WRN("wait ir tx ready!\n"); + return ; + } + irtx->State = IRTX_STATE_BUSY; + irtx->CyclicalTxCnt = 0; + + HAL_ASSERT_PARAM(irtx->Protos[protos_sel]); + irtx->txCnt = irtx->Protos[protos_sel](irtx->txBuff, ir_tx_code); + + irtx->Instance->TICR &= ~(IRTX_IT_TAI | IRTX_IT_TPE); + irtx->Instance->SR = IRTX_FLAG_TAI | IRTX_FLAG_TPE; + + if (irtx->SendModeType == IRTX_TTS_CYCLICAL) { + if (irtx->txCnt > IR_TX_FIFO_SIZE) { + IRTX_ERR("send too much bytes in cyclical mode!\n"); + return ; + } + flags = arch_irq_save(); + for (i = 0; i < irtx->txCnt; i++) { + while (irtx->Instance->TACR == 0) { + IRTX_INF("%s: fifo full\n", __func__); + } + irtx->Instance->DR = irtx->txBuff[i]; + } + arch_irq_restore(flags); + + irtx->Instance->TICR |= IRTX_IT_TPE; + irtx->Instance->TCR |= IRTX_CSS_EN; + } else { + uint32_t tmp_tthreshold, tmp_ethreshold; + + if (irtx->txCnt > IR_TX_FIFO_SIZE) { + tmp_tthreshold = IR_TX_FIFO_SIZE - 4; + tmp_ethreshold = IR_TX_FIFO_SIZE >> 1; + } else { + tmp_tthreshold = irtx->txCnt - 1; + tmp_ethreshold = irtx->txCnt >> 1; + } + irtx->Instance->TXTR = tmp_tthreshold; + irtx->Instance->TER &= ~0x000000FF; + irtx->Instance->TER |= tmp_ethreshold; + irtx->TxNum = 0; + flags = arch_irq_save(); + while (irtx->TxNum < MIN(IR_TX_FIFO_SIZE, irtx->txCnt)) { + while (irtx->Instance->TACR == 0) { + IRTX_INF("%s fifo full\n", __func__); + } + i = 0; + irtx->Instance->DR = irtx->txBuff[irtx->TxNum++]; + } + arch_irq_restore(flags); + if (irtx->TxNum < irtx->txCnt) + irtx->Instance->TICR |= IRTX_IT_TAI; + irtx->Instance->TICR |= IRTX_IT_TPE; /* non-cycle transmit threshold */ + } +} + +#ifdef CONFIG_PM +static IRTX_InitTypeDef hal_irtx_param; +static IRTX_StateTypeDef hal_irtx_suspending = 0; + +static int irtx_suspend(struct soc_device *dev, enum suspend_state_t state) +{ + hal_irtx_suspending = 1; + + switch (state) { + case PM_MODE_SLEEP: + case PM_MODE_STANDBY: + case PM_MODE_HIBERNATION: + HAL_IRTX_DeInit(dev->platform_data); + IRTX_INF("%s okay\n", __func__); + break; + default: + break; + } + + return 0; +} + +static int irtx_resume(struct soc_device *dev, enum suspend_state_t state) +{ + switch (state) { + case PM_MODE_SLEEP: + case PM_MODE_STANDBY: + case PM_MODE_HIBERNATION: + HAL_IRTX_Init(&hal_irtx_param); + IRTX_INF("%s okay\n", __func__); + break; + default: + break; + } + + hal_irtx_suspending = 0; + + return 0; +} + +static struct soc_device_driver irtx_drv = { + .name = "irtx", + .suspend_noirq = irtx_suspend, + .resume_noirq = irtx_resume, +}; + +static struct soc_device irtx_dev = { + .name = "irtx", + .driver = &irtx_drv, +}; + +#define IRTX_DEV (&irtx_dev) +#else +#define IRTX_DEV NULL +#endif + +/** + * @brief Initializes the IRTX peripheral. + * @param param: + * @arg param->[in] The configuration information. + * @retval IRTX handler. + */ +IRTX_HandleTypeDef *HAL_IRTX_Init(IRTX_InitTypeDef *param) +{ +#if !defined (IR_CLK_32K_USED) + uint32_t clk = HAL_GetHFClock(); +#endif + IRTX_HandleTypeDef *irtx = &hal_irtx; + + irtx->SendModeType = param->SendModeType; + if (irtx->SendModeType == IRTX_TTS_CYCLICAL) { + HAL_ASSERT_PARAM(param->CyclicalCnt > 0); + irtx->CyclicalCnt = param->CyclicalCnt; + } + + irtx->Instance = (IRTX_TypeDef *)IRTX_BASE; + if (irtx->State == IRTX_STATE_RESET) { + HAL_BoardIoctl(HAL_BIR_PINMUX_INIT, HAL_MKDEV(HAL_DEV_MAJOR_IRTX, 0), 0); + } +#if defined (IR_CLK_32K_USED) + HAL_CCM_IRTX_SetMClock(IRTX_32K_APB_PERIPH_CLK_SRC, + IRTX_32K_CCM_PERIPH_CLK_DIV_N, \ + IRTX_32K_CCM_PERIPH_CLK_DIV_M); +#else + if (clk == HOSC_CLOCK_26M) { + HAL_CCM_IRTX_SetMClock(IRTX_26M_APB_PERIPH_CLK_SRC, + IRTX_26M_CCM_PERIPH_CLK_DIV_N, \ + IRTX_26M_CCM_PERIPH_CLK_DIV_M); + } else if (clk == HOSC_CLOCK_24M) { + HAL_CCM_IRTX_SetMClock(IRTX_24M_APB_PERIPH_CLK_SRC, + IRTX_24M_CCM_PERIPH_CLK_DIV_N, \ + IRTX_24M_CCM_PERIPH_CLK_DIV_M); + } else { + IRTX_ERR("%s unknow clk type(%d)!\n", __func__, clk); + return NULL; + } +#endif + + HAL_CCM_IRTX_EnableMClock(); + HAL_CCM_BusForcePeriphReset(CCM_BUS_PERIPH_BIT_IRTX); + HAL_CCM_BusReleasePeriphReset(CCM_BUS_PERIPH_BIT_IRTX); + HAL_CCM_BusEnablePeriphClock(CCM_BUS_PERIPH_BIT_IRTX); + + IRTX_SetConfig(irtx->Instance, param); + + IRTX_PROTOS_FUN_INIT(irtx); +#ifdef CONFIG_PM + if (!hal_irtx_suspending) { + memcpy(&hal_irtx_param, param, sizeof(IRTX_InitTypeDef)); + IRTX_DEV->platform_data = irtx; + pm_register_ops(IRTX_DEV); + } +#endif + + HAL_NVIC_SetIRQHandler(IRTX_IRQn, IRTX_IRQHandler); + NVIC_EnableIRQ(IRTX_IRQn); + + irtx->State = IRTX_STATE_READY; + + return irtx; +} + +/** + * @brief DeInitializes the IRTX peripheral. + * @param irtx: + * @arg irtx->IRTX handler. + * @retval None. + */ +void HAL_IRTX_DeInit(IRTX_HandleTypeDef *irtx) +{ + if (irtx->State == IRTX_STATE_BUSY) { + IRTX_ERR("try deinit when not busy\n"); + return ; + } + + if (irtx->State != IRTX_STATE_READY) + return ; + + irtx->Instance->TGR &= ~IRTX_TXEN; + + NVIC_DisableIRQ(IRTX_IRQn); + +#ifdef CONFIG_PM + if (!hal_irtx_suspending) { + pm_unregister_ops(IRTX_DEV); + IRTX_DEV->platform_data = NULL; + } +#endif + + HAL_CCM_BusDisablePeriphClock(CCM_BUS_PERIPH_BIT_IRTX); + HAL_CCM_BusForcePeriphReset(CCM_BUS_PERIPH_BIT_IRTX); + HAL_CCM_IRTX_DisableMClock(); + + /* DeInit the low level hardware */ + HAL_BoardIoctl(HAL_BIR_PINMUX_DEINIT, HAL_MKDEV(HAL_DEV_MAJOR_IRTX, 0), 0); + + irtx->State = IRTX_STATE_RESET; +} diff --git a/platform/mcu/xr871/src/driver/chip/hal_mbox.c b/platform/mcu/xr871/src/driver/chip/hal_mbox.c new file mode 100644 index 0000000000..bb4b225261 --- /dev/null +++ b/platform/mcu/xr871/src/driver/chip/hal_mbox.c @@ -0,0 +1,394 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "driver/chip/hal_mbox.h" +#include "hal_base.h" + +/* useful macros */ +#define MBOX_REG_BITS 32 + +#define MBOX_IRQ_ALL_BITS ((1 << (MBOX_QUEUE_NUM << 1)) - 1) + +#define MBOX_GET_IRQ_BIT_POS(queue, dir) \ + ((queue << 1) + (dir == MBOX_DIR_TX ? 1 : 0)) + + +#define N_CCM_BUS_PERIPH_BIT_MSGBOX HAL_BIT(2) +#define N_CCM_BUS_PERIPH_CLK_CTRL_REG (*((__IO uint32_t *)(0xA0041024U))) +#define N_CCM_BUS_PERIPH_RST_CTRL_REG (*((__IO uint32_t *)(0xA0041028U))) + +void HAL_MBOX_Init(MBOX_T *mbox) +{ + if (mbox == MBOX_A) { + HAL_CCM_BusEnablePeriphClock(CCM_BUS_PERIPH_BIT_MSGBOX); + HAL_CCM_BusReleasePeriphReset(CCM_BUS_PERIPH_BIT_MSGBOX); + } else { + HAL_SET_BIT(N_CCM_BUS_PERIPH_CLK_CTRL_REG, N_CCM_BUS_PERIPH_BIT_MSGBOX); + HAL_SET_BIT(N_CCM_BUS_PERIPH_RST_CTRL_REG, N_CCM_BUS_PERIPH_BIT_MSGBOX); + } +} + +void HAL_MBOX_DeInit(MBOX_T *mbox) +{ + if (mbox == MBOX_A) { + HAL_CCM_BusForcePeriphReset(CCM_BUS_PERIPH_BIT_MSGBOX); + HAL_CCM_BusDisablePeriphClock(CCM_BUS_PERIPH_BIT_MSGBOX); + } else { + HAL_CLR_BIT(N_CCM_BUS_PERIPH_RST_CTRL_REG, N_CCM_BUS_PERIPH_BIT_MSGBOX); + HAL_CLR_BIT(N_CCM_BUS_PERIPH_CLK_CTRL_REG, N_CCM_BUS_PERIPH_BIT_MSGBOX); + } +} + +#if HAL_MBOX_PM_PATCH + +#ifdef __CONFIG_ARCH_APP_CORE + +#include "driver/chip/hal_dma.h" + +void HAL_MBOX_QueueInit(MBOX_T *mbox, MBOX_User user, MBOX_Queue queue, MBOX_Direction dir) +{ + uint32_t regIdx; + uint32_t rxBit, txBit; + + HAL_ASSERT_PARAM(user < MBOX_USER_NUM); + HAL_ASSERT_PARAM(queue < MBOX_QUEUE_NUM); + HAL_ASSERT_PARAM(dir < MBOX_DIR_NUM); + + /* NB: MUST set the users of queue's TX/RX at the same time */ + + rxBit = (queue << 3); + txBit = rxBit + 4; + + regIdx = rxBit / MBOX_REG_BITS; + rxBit = rxBit % MBOX_REG_BITS; + txBit = txBit % MBOX_REG_BITS; + + /* TODO: may need spinlock to lock this operation */ + if (user == MBOX_USER0) { + HAL_MODIFY_REG(mbox->CTRL[regIdx], + HAL_BIT(rxBit) | HAL_BIT(txBit), + dir == MBOX_DIR_TX ? HAL_BIT(rxBit) : HAL_BIT(txBit)); + } else { + HAL_MODIFY_REG(mbox->CTRL[regIdx], + HAL_BIT(rxBit) | HAL_BIT(txBit), + dir == MBOX_DIR_TX ? HAL_BIT(txBit) : HAL_BIT(rxBit)); + } +} + +static void MBOX_WriteRegister_DMA(volatile uint32_t *reg, uint32_t val) +{ + DMA_ChannelInitParam dmaParam; + DMA_Channel dmaChan; + static volatile uint32_t data; + + dmaChan = HAL_DMA_Request(); + if (dmaChan == DMA_CHANNEL_INVALID) { + HAL_ERR("no free dma channel\n"); + return; + } + + data = val; + HAL_Memset(&dmaParam, 0, sizeof(dmaParam)); + dmaParam.cfg = HAL_DMA_MakeChannelInitCfg(DMA_WORK_MODE_SINGLE, + DMA_WAIT_CYCLE_1, + DMA_BYTE_CNT_MODE_REMAIN, + DMA_DATA_WIDTH_32BIT, + DMA_BURST_LEN_1, + DMA_ADDR_MODE_FIXED, + DMA_PERIPH_SRAM, + DMA_DATA_WIDTH_32BIT, + DMA_BURST_LEN_1, + DMA_ADDR_MODE_FIXED, + DMA_PERIPH_SRAM); + dmaParam.irqType = DMA_IRQ_TYPE_END; + dmaParam.endCallback = NULL; + dmaParam.endArg = NULL; + HAL_DMA_Init(dmaChan, &dmaParam); + HAL_DMA_Start(dmaChan, (uint32_t)&data, (uint32_t)reg, 1); + while (HAL_DMA_GetByteCount(dmaChan) != 0) { } + HAL_DMA_Stop(dmaChan); + HAL_DMA_DeInit(dmaChan); + HAL_DMA_Release(dmaChan); +} + +void HAL_MBOX_QueueEnableIRQ(MBOX_T *mbox, MBOX_User user, MBOX_Queue queue, MBOX_Direction dir) +{ + uint32_t bit; + + HAL_ASSERT_PARAM(user < MBOX_USER_NUM); + HAL_ASSERT_PARAM(queue < MBOX_QUEUE_NUM); + HAL_ASSERT_PARAM(dir < MBOX_DIR_NUM); + + bit = MBOX_GET_IRQ_BIT_POS(queue, dir); + + if (user == MBOX_USER0) { + HAL_SET_BIT(mbox->IRQ0_EN, HAL_BIT(bit)); + } else { + uint32_t irq_en = mbox->IRQ1_EN; + HAL_SET_BIT(irq_en, HAL_BIT(bit)); + MBOX_WriteRegister_DMA(&mbox->IRQ1_EN, irq_en); + } +} + +void HAL_MBOX_QueueDisableIRQ(MBOX_T *mbox, MBOX_User user, MBOX_Queue queue, MBOX_Direction dir) +{ + uint32_t bit; + + HAL_ASSERT_PARAM(user < MBOX_USER_NUM); + HAL_ASSERT_PARAM(queue < MBOX_QUEUE_NUM); + HAL_ASSERT_PARAM(dir < MBOX_DIR_NUM); + + bit = MBOX_GET_IRQ_BIT_POS(queue, dir); + + if (user == MBOX_USER0) { + HAL_CLR_BIT(mbox->IRQ0_EN, HAL_BIT(bit)); + } else { + uint32_t irq_en = mbox->IRQ1_EN; + HAL_CLR_BIT(irq_en, HAL_BIT(bit)); + MBOX_WriteRegister_DMA(&mbox->IRQ1_EN, irq_en); + } +} + +#endif /* __CONFIG_ARCH_APP_CORE */ + +void HAL_MBOX_EnableIRQ(MBOX_T *mbox) +{ + IRQn_Type IRQn; + + IRQn = (mbox == MBOX_A) ? MBOX_A_IRQn : MBOX_N_IRQn; + HAL_NVIC_SetPriority(IRQn, NVIC_PERIPHERAL_PRIORITY_DEFAULT); + HAL_NVIC_EnableIRQ(IRQn); +} + +void HAL_MBOX_DisableIRQ(MBOX_T *mbox) +{ + IRQn_Type IRQn; + + IRQn = (mbox == MBOX_A) ? MBOX_A_IRQn : MBOX_N_IRQn; + HAL_NVIC_SetPriority(IRQn, NVIC_PERIPHERAL_PRIORITY_DEFAULT); + HAL_NVIC_DisableIRQ(IRQn); +} + +#else /* HAL_MBOX_PM_PATCH */ + +void HAL_MBOX_QueueInit(MBOX_T *mbox, MBOX_User user, MBOX_Queue queue, MBOX_Direction dir) +{ + uint32_t regIdx; + uint32_t bit; + + HAL_ASSERT_PARAM(user < MBOX_USER_NUM); + HAL_ASSERT_PARAM(queue < MBOX_QUEUE_NUM); + HAL_ASSERT_PARAM(dir < MBOX_DIR_NUM); + + bit = (queue << 3) + (dir == MBOX_DIR_TX ? 4 : 0); + regIdx = bit / MBOX_REG_BITS; + bit = bit % MBOX_REG_BITS; + + /* TODO: may need spinlock to lock this operation */ + if (user == MBOX_USER0) { + HAL_CLR_BIT(mbox->CTRL[regIdx], HAL_BIT(bit)); + } else { + HAL_SET_BIT(mbox->CTRL[regIdx], HAL_BIT(bit)); + } +} + +void HAL_MBOX_QueueEnableIRQ(MBOX_T *mbox, MBOX_User user, MBOX_Queue queue, MBOX_Direction dir) +{ + uint32_t bit; + IRQn_Type IRQn; + uint32_t irqEnBits; + + HAL_ASSERT_PARAM(user < MBOX_USER_NUM); + HAL_ASSERT_PARAM(queue < MBOX_QUEUE_NUM); + HAL_ASSERT_PARAM(dir < MBOX_DIR_NUM); + + bit = MBOX_GET_IRQ_BIT_POS(queue, dir); + + if (user == MBOX_USER0) { + irqEnBits = mbox->IRQ0_EN; + HAL_SET_BIT(mbox->IRQ0_EN, HAL_BIT(bit)); + } else { + irqEnBits = mbox->IRQ1_EN; + HAL_SET_BIT(mbox->IRQ1_EN, HAL_BIT(bit)); + } + + if (irqEnBits == 0) { + IRQn = (mbox == MBOX_A) ? MBOX_A_IRQn : MBOX_N_IRQn; + HAL_NVIC_SetPriority(IRQn, NVIC_PERIPHERAL_PRIORITY_DEFAULT); + HAL_NVIC_EnableIRQ(IRQn); + } +} + +void HAL_MBOX_QueueDisableIRQ(MBOX_T *mbox, MBOX_User user, MBOX_Queue queue, MBOX_Direction dir) +{ + uint32_t bit; + IRQn_Type IRQn; + uint32_t irqEnBits; + + HAL_ASSERT_PARAM(user < MBOX_USER_NUM); + HAL_ASSERT_PARAM(queue < MBOX_QUEUE_NUM); + HAL_ASSERT_PARAM(dir < MBOX_DIR_NUM); + + bit = MBOX_GET_IRQ_BIT_POS(queue, dir); + + if (user == MBOX_USER0) { + HAL_CLR_BIT(mbox->IRQ0_EN, HAL_BIT(bit)); + irqEnBits = mbox->IRQ0_EN; + } else { + HAL_CLR_BIT(mbox->IRQ1_EN, HAL_BIT(bit)); + irqEnBits = mbox->IRQ1_EN; + } + + if (irqEnBits == 0) { + IRQn = (mbox == MBOX_A) ? MBOX_A_IRQn : MBOX_N_IRQn; + HAL_NVIC_SetPriority(IRQn, NVIC_PERIPHERAL_PRIORITY_DEFAULT); + HAL_NVIC_DisableIRQ(IRQn); + } +} + +#endif /* HAL_MBOX_PM_PATCH */ + +void HAL_MBOX_QueueDeInit(MBOX_T *mbox, MBOX_User user, MBOX_Queue queue, MBOX_Direction dir) +{ + /* Nothing to do */ +} + +int HAL_MBOX_QueueIsPendingIRQ(MBOX_T *mbox, MBOX_User user, MBOX_Queue queue, MBOX_Direction dir) +{ + uint32_t bit; + + HAL_ASSERT_PARAM(user < MBOX_USER_NUM); + HAL_ASSERT_PARAM(queue < MBOX_QUEUE_NUM); + HAL_ASSERT_PARAM(dir < MBOX_DIR_NUM); + + bit = MBOX_GET_IRQ_BIT_POS(queue, dir); + + if (user == MBOX_USER0) { + return HAL_GET_BIT(mbox->IRQ0_STATUS, HAL_BIT(bit)); + } else { + return HAL_GET_BIT(mbox->IRQ1_STATUS, HAL_BIT(bit)); + } +} + +void HAL_MBOX_QueueClrPendingIRQ(MBOX_T *mbox, MBOX_User user, MBOX_Queue queue, MBOX_Direction dir) +{ + uint32_t bit; + + HAL_ASSERT_PARAM(user < MBOX_USER_NUM); + HAL_ASSERT_PARAM(queue < MBOX_QUEUE_NUM); + HAL_ASSERT_PARAM(dir < MBOX_DIR_NUM); + + bit = MBOX_GET_IRQ_BIT_POS(queue, dir); + + if (user == MBOX_USER0) { + HAL_SET_BIT(mbox->IRQ0_STATUS, HAL_BIT(bit)); + } else { + HAL_SET_BIT(mbox->IRQ1_STATUS, HAL_BIT(bit)); + } +} + +int HAL_MBOX_QueueIsFull(MBOX_T *mbox, MBOX_Queue queue) +{ + HAL_ASSERT_PARAM(queue < MBOX_QUEUE_NUM); + + return HAL_GET_BIT(mbox->FIFO_STATUS[queue], MBOX_QUEUE_FULL_BIT); +} + +uint32_t HAL_MBOX_QueueGetMsgNum(MBOX_T *mbox, MBOX_Queue queue) +{ + HAL_ASSERT_PARAM(queue < MBOX_QUEUE_NUM); + + return HAL_GET_BIT_VAL(mbox->MSG_STATUS[queue], + MBOX_QUEUE_MSG_NUM_SHIFT, + MBOX_QUEUE_MSG_NUM_VMASK); +} + +/* NB: befor put @msg to @queue, make sure @queue is not full */ +void HAL_MBOX_QueuePutMsg(MBOX_T *mbox, MBOX_Queue queue, uint32_t msg) +{ + mbox->MSG[queue] = msg; +} + +/* NB: befor get @msg from @queue, make sure @queue has message */ +uint32_t HAL_MBOX_QueueGetMsg(MBOX_T *mbox, MBOX_Queue queue) +{ + return mbox->MSG[queue]; +} + +__weak void MBOX_IRQCallback(MBOX_T *mbox, MBOX_Queue queue, MBOX_Direction dir) +{ +} + +static void MBOX_IRQHandler(MBOX_T *mbox, MBOX_User user) +{ + uint32_t i; + uint32_t irqPending, irqPendingBackup; + uint32_t isRxPending, isTxPending; + + if (user == MBOX_USER0) { + irqPending = mbox->IRQ0_STATUS & mbox->IRQ0_EN & MBOX_IRQ_ALL_BITS; /* get pending bits */ + HAL_MBOX_DBG("mbox %p, user %d, STATUS 0x%x, EN 0x%x, irqPending 0x%x\n", + mbox, user, mbox->IRQ0_STATUS, mbox->IRQ0_EN, irqPending); + } else { + irqPending = mbox->IRQ1_STATUS & mbox->IRQ1_EN & MBOX_IRQ_ALL_BITS; /* get pending bits */ + HAL_MBOX_DBG("mbox %p, user %d, STATUS 0x%x, EN 0x%x, irqPending 0x%x\n", + mbox, user, mbox->IRQ1_STATUS, mbox->IRQ1_EN, irqPending); + } + irqPendingBackup = irqPending; + + for (i = MBOX_QUEUE_0; i < MBOX_QUEUE_NUM && irqPending != 0; ++i) { + isRxPending = (irqPending & HAL_BIT(0)); + if (isRxPending) { + MBOX_IRQCallback(mbox, i, MBOX_DIR_RX); + } + + isTxPending = (irqPending & HAL_BIT(1)); + if (isTxPending) { + MBOX_IRQCallback(mbox, i, MBOX_DIR_TX); + } + + irqPending >>= 2; + } + + if (user == MBOX_USER0) { + mbox->IRQ0_STATUS = irqPendingBackup; /* clear pending bits */ + } else { + mbox->IRQ1_STATUS = irqPendingBackup; /* clear pending bits */ + } +} + +void MBOX_A_IRQHandler(void) +{ + MBOX_IRQHandler(MBOX_A, MBOX_USER0); +} + +void MBOX_N_IRQHandler(void) +{ + MBOX_IRQHandler(MBOX_N, MBOX_USER1); +} diff --git a/platform/mcu/xr871/src/driver/chip/hal_nvic.c b/platform/mcu/xr871/src/driver/chip/hal_nvic.c new file mode 100644 index 0000000000..42dc4b1048 --- /dev/null +++ b/platform/mcu/xr871/src/driver/chip/hal_nvic.c @@ -0,0 +1,321 @@ +/** + * @file hal_nvic.c + * @author XRADIO IOT WLAN Team + */ + +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "hal_base.h" +#include "pm/pm.h" +#include "sys/param.h" +#include "sys/xr_debug.h" + +/** + * @brief Set the handler for the specified interrupt + * @param[in] IRQn interrupt number + * @param[in] handler Handler of the specified interrupt + * @return None + */ +void HAL_NVIC_SetIRQHandler(IRQn_Type IRQn, NVIC_IRQHandler handler) +{ + uint32_t *vectors = (uint32_t *)SCB->VTOR; + + vectors[IRQn + NVIC_PERIPH_IRQ_OFFSET] = (uint32_t)handler; +} + +/** + * @brief Get the handler of the specified interrupt + * @param[in] IRQn interrupt number + * @return IRQ Handler of the specified interrupt + */ +NVIC_IRQHandler HAL_NVIC_GetIRQHandler(IRQn_Type IRQn) +{ + uint32_t *vectors = (uint32_t*)SCB->VTOR; + + return (NVIC_IRQHandler)(vectors[IRQn + NVIC_PERIPH_IRQ_OFFSET]); +} + +/** + * @brief Set priority grouping of the NVIC interrupt controller + * @param [in] priorityGroup Priority grouping field + * @return None + */ +void HAL_NVIC_SetPriorityGrouping(uint32_t priorityGroup) +{ + NVIC_SetPriorityGrouping(priorityGroup); +} + +/** + * @brief Get Priority Grouping of the NVIC interrupt controller + * @return Priority grouping field (SCB->AIRCR [10:8] PRIGROUP field). + */ +uint32_t HAL_NVIC_GetPriorityGrouping(void) +{ + return NVIC_GetPriorityGrouping(); +} + +/** + * @brief Set the interrupt priority of the specified interrupt + * @param[in] IRQn Interrupt number + * @param[in] priority Interrupt priority of the specified interrupt + * @return None + */ +void HAL_NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority) +{ + NVIC_SetPriority(IRQn, priority); +} + +/** + * @brief Get the interrupt priority of the specified interrupt + * @param[in] IRQn Interrupt number + * @return Interrupt priority of the specified interrupt + */ +uint32_t HAL_NVIC_GetPriority(IRQn_Type IRQn) +{ + return NVIC_GetPriority(IRQn); +} + +/** + * @brief Enable external (device-specific) interrupt + * @param[in] IRQn External interrupt number. Value cannot be negative. + * @return None + */ +void HAL_NVIC_EnableIRQ(IRQn_Type IRQn) +{ + NVIC_EnableIRQ(IRQn); +} + +/** + * @brief Disable external (device-specific) interrupt + * @param[in] IRQn External interrupt number. Value cannot be negative. + * @return None + */ +void HAL_NVIC_DisableIRQ(IRQn_Type IRQn) +{ + NVIC_DisableIRQ(IRQn); +} + +/** + * @brief Set the pending bit of the specified external (device-specific) + * interrupt + * @param[in] IRQn External interrupt number. Value cannot be negative. + * @return None + */ +void HAL_NVIC_SetPendingIRQ(IRQn_Type IRQn) +{ + NVIC_SetPendingIRQ(IRQn); +} + +/** + * @brief Get the pending status of the specified external (device-specific) + * interrupt + * @param[in] IRQn External interrupt number. Value cannot be negative. + * @return 0 Interrupt status is not pending + * @return 1 Interrupt status is pending + */ +int HAL_NVIC_IsPendingIRQ(IRQn_Type IRQn) +{ + return (int)(NVIC_GetPendingIRQ(IRQn)); +} + +/** + * @brief Clear the pending bit of the specified external (device-specific) + * interrupt + * @param[in] IRQn External interrupt number. Value cannot be negative. + * @return None + */ +void HAL_NVIC_ClearPendingIRQ(IRQn_Type IRQn) +{ + NVIC_ClearPendingIRQ(IRQn); +} + +#ifdef CONFIG_PM +struct nvic_regs { + uint32_t vector_table; /* Vector Table Offset */ + uint32_t int_ctrl_state; /* Interrupt Control and State */ + uint32_t app_int; /* Application Interrupt Reset control */ + uint32_t sys_ctrl; /* System control */ + uint32_t config_ctrl; /* Configuration control */ + uint32_t sys_pri[3]; /* System Handler Priority */ + uint32_t sys_hcrs; /* System Handler control and state register */ + uint32_t systick_ctrl; /* SysTick Control Status */ + uint32_t systick_reload; /* SysTick Reload */ + uint32_t int_en[DIV_ROUND_UP(NVIC_VECTOR_TABLE_SIZE, 32)]; /* Interrupt set enable */ + uint8_t int_priority[NVIC_PERIPH_IRQ_NUM * 4]; /* Interrupt priority */ +#ifdef __CONFIG_CPU_CM4F + uint32_t cpacr; /* Coprocessor Access Control Register */ + uint32_t fpccr; /* Floating-Point Context Control Register */ + uint32_t fpcar; /* Floating-Point Context Address Register */ +#endif +}; + +static struct nvic_regs nvic_reg_store; + +static int nvic_suspend(struct soc_device *dev, enum suspend_state_t state) +{ + uint32_t i = 0; + volatile uint32_t *reg_en_addr; + volatile uint8_t *reg_ip_addr; + struct nvic_regs *nvic_back = &nvic_reg_store; + + switch (state) { + case PM_MODE_SLEEP: + break; + case PM_MODE_STANDBY: + case PM_MODE_HIBERNATION: + /* Save the NVIC control registers */ + nvic_back->vector_table = SCB->VTOR; + nvic_back->int_ctrl_state = SCB->ICSR; + nvic_back->app_int = SCB->AIRCR; + nvic_back->sys_ctrl = SCB->SCR; + nvic_back->config_ctrl = SCB->CCR; + reg_en_addr = (volatile uint32_t *)SCB->SHP; + for (i = 0; i < 3; i++) { + nvic_back->sys_pri[i] = reg_en_addr[i]; + } + nvic_back->sys_hcrs = SCB->SHCSR; + + /* Systick registers */ + nvic_back->systick_ctrl = SysTick->CTRL; + nvic_back->systick_reload = SysTick->LOAD; + + /* Save the interrupt enable registers */ + reg_en_addr = NVIC->ISER; + for (i = 0; i < DIV_ROUND_UP(NVIC_VECTOR_TABLE_SIZE, 32); i++) { + nvic_back->int_en[i] = reg_en_addr[i]; + //NVIC->ICER[i] = 0xffffffff; /* disable all ints */ + } + + /* Save the interrupt priority registers */ + reg_ip_addr = NVIC->IP; + for (i = 0; i < sizeof(nvic_back->int_priority); i++) { + nvic_back->int_priority[i] = reg_ip_addr[i]; + } +#ifdef __CONFIG_CPU_CM4F + nvic_back->cpacr = SCB->CPACR; + nvic_back->fpccr = FPU->FPCCR; + nvic_back->fpcar = FPU->FPCAR; +#endif + HAL_DBG("%s okay\n", __func__); + break; + default: + break; + } + + return 0; +} + +static int nvic_resume(struct soc_device *dev, enum suspend_state_t state) +{ + uint32_t i = 0; + volatile uint32_t *reg_en_addr; + volatile uint8_t *reg_ip_addr; + struct nvic_regs *nvic_back = &nvic_reg_store; + + switch (state) { + case PM_MODE_SLEEP: + break; + case PM_MODE_STANDBY: + case PM_MODE_HIBERNATION: + /* Restore the NVIC control registers */ + SCB->VTOR = nvic_back->vector_table; + SCB->AIRCR = (nvic_back->app_int & 0x0ffff) | (0x5FA << SCB_AIRCR_VECTKEY_Pos); + SCB->SCR = nvic_back->sys_ctrl; + SCB->CCR = nvic_back->config_ctrl; + reg_en_addr = (volatile uint32_t *)SCB->SHP; + for (i = 0; i < 3; i++) { + reg_en_addr[i] = nvic_back->sys_pri[i]; + } + SCB->SHCSR = nvic_back->sys_hcrs; + + /* Systick registers */ + SysTick->CTRL = nvic_back->systick_ctrl; + SysTick->LOAD = nvic_back->systick_reload; + + /* Restore the interrupt priority registers */ + reg_ip_addr = NVIC->IP; + for (i = 0; i < sizeof(nvic_back->int_priority); i++) { + reg_ip_addr[i] = nvic_back->int_priority[i]; + } + + /* Restore the interrupt enable registers */ + reg_en_addr = NVIC->ISER; + for (i = 0; i < DIV_ROUND_UP(NVIC_VECTOR_TABLE_SIZE, 32); i++) { + reg_en_addr[i] = nvic_back->int_en[i]; + } +#ifdef __CONFIG_CPU_CM4F + SCB->CPACR = nvic_back->cpacr; + FPU->FPCCR = nvic_back->fpccr; + FPU->FPCAR = nvic_back->fpcar; +#endif + __asm(" dsb \n"); + __asm(" isb \n"); + HAL_DBG("%s okay\n", __func__); + break; + default: + break; + } + + return 0; +} + +void nvic_print_regs(void) +{ + //hex_dump_bytes(&nvic_reg_store, sizeof(nvic_reg_store)); +} + +static struct soc_device_driver nvic_drv = { + .name = "nvic", + .suspend_noirq = nvic_suspend, + .resume_noirq = nvic_resume, +}; + +static struct soc_device nvic_dev = { + .name = "nvic", + .driver = &nvic_drv, +}; + +#define NVIC_DEV (&nvic_dev) +#endif + +/** + * @brief Initialize the NVIC module + * @return None + */ +void HAL_NVIC_Init(void) +{ + /* Enable some system fault exceptions */ + SCB->SHCSR |= SCB_SHCSR_USGFAULTENA_Msk | SCB_SHCSR_BUSFAULTENA_Msk | + SCB_SHCSR_MEMFAULTENA_Msk; + +#ifdef CONFIG_PM + pm_register_ops(NVIC_DEV); +#endif +} diff --git a/platform/mcu/xr871/src/driver/chip/hal_os.h b/platform/mcu/xr871/src/driver/chip/hal_os.h new file mode 100644 index 0000000000..f8e5498767 --- /dev/null +++ b/platform/mcu/xr871/src/driver/chip/hal_os.h @@ -0,0 +1,148 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _DRIVER_CHIP_HAL_OS_H_ +#define _DRIVER_CHIP_HAL_OS_H_ + +#include "kernel/os/os.h" +#include "sys/interrupt.h" +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* IRQ disable/enable */ +__STATIC_INLINE void HAL_DisableIRQ(void) +{ + arch_irq_disable(); +} + +__STATIC_INLINE void HAL_EnableIRQ(void) +{ + arch_irq_enable(); +} + +/* Critical Sections */ +__STATIC_INLINE unsigned long HAL_EnterCriticalSection(void) +{ + return arch_irq_save(); // temp implementation +} + +__STATIC_INLINE void HAL_ExitCriticalSection(unsigned long flags) +{ + arch_irq_restore(flags); // temp implementation +} + +/* Semaphore */ +typedef OS_Semaphore_t HAL_Semaphore; + +__STATIC_INLINE HAL_Status HAL_SemaphoreInit(HAL_Semaphore *sem, uint32_t initCount, uint32_t maxCount) +{ + return OS_SemaphoreCreate(sem, initCount, maxCount) == OS_OK ? HAL_OK : HAL_ERROR; +} + +__STATIC_INLINE HAL_Status HAL_SemaphoreInitBinary(HAL_Semaphore *sem) +{ + return OS_SemaphoreCreateBinary(sem) == OS_OK ? HAL_OK : HAL_ERROR; +} + +__STATIC_INLINE HAL_Status HAL_SemaphoreDeinit(HAL_Semaphore *sem) +{ + return OS_SemaphoreDelete(sem) == OS_OK ? HAL_OK : HAL_ERROR; +} + +__STATIC_INLINE HAL_Status HAL_SemaphoreWait(HAL_Semaphore *sem, uint32_t msec) +{ + return OS_SemaphoreWait(sem, msec) == OS_OK ? HAL_OK : HAL_ERROR; +} + +__STATIC_INLINE HAL_Status HAL_SemaphoreRelease(HAL_Semaphore *sem) +{ + return OS_SemaphoreRelease(sem) == OS_OK ? HAL_OK : HAL_ERROR; +} + +/* Mutex */ +typedef OS_Mutex_t HAL_Mutex; + +__STATIC_INLINE HAL_Status HAL_MutexInit(HAL_Mutex *mtx) +{ + return OS_MutexCreate(mtx) == OS_OK ? HAL_OK : HAL_ERROR; +} + +__STATIC_INLINE HAL_Status HAL_MutexDeinit(HAL_Mutex *mtx) +{ + return OS_MutexDelete(mtx) == OS_OK ? HAL_OK : HAL_ERROR; +} + +__STATIC_INLINE HAL_Status HAL_MutexLock(HAL_Mutex *mtx, uint32_t msec) +{ + return OS_MutexLock(mtx, msec) == OS_OK ? HAL_OK : HAL_ERROR; +} + +__STATIC_INLINE HAL_Status HAL_MutexUnlock(HAL_Mutex *mtx) +{ + return OS_MutexUnlock(mtx) == OS_OK ? HAL_OK : HAL_ERROR; +} + +/* time */ +__STATIC_INLINE uint32_t HAL_Ticks(void) +{ + return OS_GetTicks(); +} + +__STATIC_INLINE void HAL_MSleep(uint32_t msec) +{ + OS_MSleep(msec); +} + +#define HAL_SecsToTicks(sec) OS_SecsToTicks(sec) +#define HAL_MSecsToTicks(msec) OS_MSecsToTicks(msec) +#define HAL_TicksToMSecs(t) OS_TicksToMSecs(t) +#define HAL_TicksToSecs(t) OS_TicksToSecs(t) + +#define HAL_TimeAfter(a, b) OS_TimeAfter(a, b) +#define HAL_TimeBefore(a, b) OS_TimeAfter(b, a) +#define HAL_TimeAfterEqual(a, b) OS_TimeAfterEqual(a, b) +#define HAL_TimeBeforeEqual(a, b) OS_TimeAfterEqual(b, a) + +/* memory */ +#define HAL_Malloc(l) malloc(l) +#define HAL_Free(p) free(p) +#define HAL_Memcpy(d, s, l) memcpy(d, s, l) +#define HAL_Memset(d, c, l) memset(d, c, l) +#define HAL_Memcmp(a, b, l) memcmp(a, b, l) +#define HAL_Memmove(d, s, n) memmove(d, s, n) + +#ifdef __cplusplus +} +#endif + +#endif /* _DRIVER_CHIP_HAL_OS_H_ */ diff --git a/platform/mcu/xr871/src/driver/chip/hal_prcm.c b/platform/mcu/xr871/src/driver/chip/hal_prcm.c new file mode 100644 index 0000000000..3febe995ad --- /dev/null +++ b/platform/mcu/xr871/src/driver/chip/hal_prcm.c @@ -0,0 +1,665 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "driver/chip/hal_prcm.h" +#include "hal_base.h" + +/* + * Power + * - DCDC + * - Power switch + * - LDO + */ +void HAL_PRCM_SetDCDCVoltage(PRCM_DCDCVolt volt) +{ + HAL_MODIFY_REG(PRCM->SYS_DCDC_CTRL, PRCM_DCDC_VOLT_MASK, volt); +} + +uint32_t HAL_PRCM_GetSysPowerEnableFlags(void) +{ + return HAL_GET_BIT(PRCM->SYS_LDO_SW_CTRL, + PRCM_SYS1_PWR1_EN_BIT | + PRCM_SYS1_PWR2_EN_BIT | + PRCM_SYS2_PWR3_EN_BIT | + PRCM_SYS3_PWR4_EN_BIT | + PRCM_SYS3_PWR5_EN_BIT | + PRCM_SYS1_SRAM_PWR1_EN_BIT | + PRCM_SYS2_SRAM_PWR2_EN_BIT | + PRCM_SYS3_SRAM_PWR3_EN_BIT); +} + +void HAL_PRCM_EnableSys2Power(void) +{ + HAL_SET_BIT(PRCM->SYS_LDO_SW_CTRL, + PRCM_SYS2_PWR3_EN_BIT | PRCM_SYS2_SRAM_PWR2_EN_BIT); +} + +void HAL_PRCM_DisableSys2Power(void) +{ + HAL_CLR_BIT(PRCM->SYS_LDO_SW_CTRL, + PRCM_SYS2_PWR3_EN_BIT | PRCM_SYS2_SRAM_PWR2_EN_BIT); +} + +void HAL_PRCM_SetLDO1Voltage(PRCM_LDO1Volt volt) +{ + HAL_MODIFY_REG(PRCM->SYS_LDO_SW_CTRL, PRCM_LDO1_VOLT_MASK, volt); +} + +uint32_t HAL_PRCM_GetLDOEnableFlags(void) +{ + return HAL_GET_BIT(PRCM->SYS_LDO_SW_CTRL, + PRCM_LDO1_EN_BIT | + PRCM_PLL_LDO_EN_BIT | + PRCM_SRAM_LDO_EN_BIT); +} + +/* + * Clock + */ +void HAL_PRCM_SetLFCLKSource(PRCM_LFCLKSrc src) +{ + /* always enable inter 32K */ + uint32_t clr_mask = PRCM_LFCLK_SRC_MASK | PRCM_LFCLK_EXT32K_EN_BIT; + uint32_t set_mask = src | PRCM_LFCLK_INTER32K_EN_BIT; + if (src == PRCM_LFCLK_SRC_EXT32K) { + set_mask |= PRCM_LFCLK_EXT32K_EN_BIT; + } + HAL_MODIFY_REG(PRCM->SYS_LFCLK_CTRL, clr_mask, set_mask); +} + +void HAL_PRCM_SetHOSCType(PRCM_HOSCType type) +{ + HAL_MODIFY_REG(PRCM->SYS_HOSC_CTRL, PRCM_HOSC_TYPE_MASK, type); +} + +uint32_t HAL_PRCM_GetHOSCType(void) +{ + return HAL_GET_BIT(PRCM->SYS_HOSC_CTRL, PRCM_HOSC_TYPE_MASK); +} + +uint32_t HAL_PRCM_GetHFClock(void) +{ + static const uint32_t PRCM_HOSCClock[] = + { HOSC_CLOCK_26M, HOSC_CLOCK_40M, HOSC_CLOCK_24M, HOSC_CLOCK_52M }; + + uint32_t val; + + val = HAL_GET_BIT_VAL(PRCM->SYS_HOSC_CTRL, + PRCM_HOSC_TYPE_SHIFT, + PRCM_HOSC_TYPE_VMASK); + return PRCM_HOSCClock[val]; +} + +uint32_t HAL_PRCM_GetInter32KFreq(void) +{ + return (10 * HAL_GET_BIT_VAL(PRCM->SYS_RCOSC_CALIB_CTRL, + PRCM_RCOSC_CALIB_FREQ_SHIFT, + PRCM_RCOSC_CALIB_FREQ_VMASK)); +} + +uint32_t HAL_PRCM_EnableInter32KCalib(void) +{ + return HAL_SET_BIT(PRCM->SYS_RCOSC_CALIB_CTRL, PRCM_RCOSC_CALIB_EN_BIT); +} + +uint32_t HAL_PRCM_DisableInter32KCalib(void) +{ + return HAL_CLR_BIT(PRCM->SYS_RCOSC_CALIB_CTRL, PRCM_RCOSC_CALIB_EN_BIT); +} + +#if 0 +void HAL_PRCM_SetSysPLLParam(PRCM_SysPLLParam param) +{ + HAL_MODIFY_REG(PRCM->SYS_PLL_CTRL, PRCM_SYS_PLL_PARAM_MASK, param); +} + +void HAL_PRCM_EnableSysPLL(void) +{ + HAL_SET_BIT(PRCM->SYS_PLL_CTRL, PRCM_SYS_PLL_EN_BIT); +} +#else +void HAL_PRCM_SetSysPLL(PRCM_SysPLLParam param) +{ + PRCM->SYS_PLL_CTRL = PRCM_SYS_PLL_EN_BIT | param; /* NB: enable system PLL */ +} +#endif + +void HAL_PRCM_DisableSysPLL(void) +{ + HAL_CLR_BIT(PRCM->SYS_PLL_CTRL, PRCM_SYS_PLL_EN_BIT); +} + +void HAL_PRCM_SetCPUAClk(PRCM_CPUClkSrc src, PRCM_SysClkFactor factor) +{ + switch (src) { + case PRCM_CPU_CLK_SRC_HFCLK: + HAL_MODIFY_REG(PRCM->SYS_CLK1_CTRL, PRCM_CPU_CLK_SRC_MASK, PRCM_CPU_CLK_SRC_HFCLK); + break; + case PRCM_CPU_CLK_SRC_LFCLK: + HAL_MODIFY_REG(PRCM->SYS_CLK1_CTRL, PRCM_CPU_CLK_SRC_MASK, PRCM_CPU_CLK_SRC_LFCLK); + break; + case PRCM_CPU_CLK_SRC_SYSCLK: + default: +#if 1 + HAL_MODIFY_REG(PRCM->SYS_CLK1_CTRL, PRCM_SYS_CLK_FACTOR_MASK, factor); + HAL_SET_BIT(PRCM->SYS_CLK1_CTRL, PRCM_SYS_CLK_EN_BIT); + HAL_MODIFY_REG(PRCM->SYS_CLK1_CTRL, PRCM_CPU_CLK_SRC_MASK, PRCM_CPU_CLK_SRC_SYSCLK); +#else + PRCM->SYS_CLK1_CTRL = PRCM_SYS_CLK_EN_BIT | factor | PRCM_CPU_CLK_SRC_SYSCLK; +#endif + break; + } +} + +void HAL_PRCM_DisCLK1(PRCM_SysClkFactor factor) +{ + HAL_MODIFY_REG(PRCM->SYS_CLK1_CTRL, PRCM_SYS_CLK_FACTOR_MASK, factor); + HAL_CLR_BIT(PRCM->SYS_CLK1_CTRL, PRCM_SYS_CLK_EN_BIT); +} + +uint32_t HAL_PRCM_GetCPUAClk(void) +{ + uint32_t reg = PRCM->SYS_CLK1_CTRL; + uint32_t freq, div; + + switch (reg & PRCM_CPU_CLK_SRC_MASK) { + case PRCM_CPU_CLK_SRC_HFCLK: + freq = HAL_GetHFClock(); + break; + case PRCM_CPU_CLK_SRC_LFCLK: + freq = HAL_GetLFClock(); + break; + case PRCM_CPU_CLK_SRC_SYSCLK: + default: + div = HAL_GET_BIT_VAL(reg, + PRCM_SYS_CLK_FACTOR_SHIFT, + PRCM_SYS_CLK_FACTOR_VMASK) + 1; + freq = SYS_PLL_CLOCK / div; + break; + } + return freq; +} + +void HAL_PRCM_SetAudioPLLParam(PRCM_AudPLLParam param) +{ + PRCM->AUD_PLL_CTRL = param; /* NB: it will disable system PLL */ +} + +void HAL_PRCM_EnableAudioPLL(void) +{ + HAL_SET_BIT(PRCM->AUD_PLL_CTRL, PRCM_AUD_PLL_EN_BIT); +} + +void HAL_PRCM_DisableAudioPLL(void) +{ + HAL_CLR_BIT(PRCM->AUD_PLL_CTRL, PRCM_AUD_PLL_EN_BIT); +} + +void HAL_PRCM_SetDevClock(PRCM_DevClkFactor factor) +{ + PRCM->DEV_CLK_CTRL = factor; +} + +uint32_t HAL_PRCM_GetDevClock(void) +{ + uint32_t div = HAL_GET_BIT_VAL(PRCM->DEV_CLK_CTRL, + PRCM_DEV_CLK_FACTOR_SHIFT, + PRCM_DEV_CLK_FACTOR_VMASK) + 1; + return (SYS_PLL_CLOCK / div); +} + +void HAL_PRCM_SetAudioPLLPatternParam(PRCM_AudPLLPatParam param) +{ + PRCM->AUD_PLL_PAT_CTRL = param; /* NB: it will disable system PLL */ +} + +void HAL_PRCM_EnableAudioPLLPattern(void) +{ + HAL_SET_BIT(PRCM->AUD_PLL_PAT_CTRL, PRCM_AUD_DIG_DELT_PAT_EN_BIT); +} + +void HAL_PRCM_DisableAudioPLLPattern(void) +{ + HAL_CLR_BIT(PRCM->AUD_PLL_PAT_CTRL, PRCM_AUD_DIG_DELT_PAT_EN_BIT); +} + +int HAL_PRCM_IsCPUAResetRelease(void) +{ + return HAL_GET_BIT(PRCM->SYS1_CTRL, PRCM_CPUA_RESET_BIT); +} + +int HAL_PRCM_IsSys1ResetRelease(void) +{ + return HAL_GET_BIT(PRCM->SYS1_CTRL, PRCM_SYS1_RESET_BIT); +} + +void HAL_PRCM_AllowCPUNDeepSleep(void) +{ + HAL_CLR_BIT(PRCM->SYS1_STATUS, PRCM_CPUN_DEEPSLEEP_LOCK_BIT); +} + +void HAL_PRCM_DisallowCPUNDeepSleep(void) +{ + HAL_SET_BIT(PRCM->SYS1_STATUS, PRCM_CPUN_DEEPSLEEP_LOCK_BIT); +} + +int HAL_PRCM_IsCPUNDeepSleepAllowed(void) +{ + return !HAL_GET_BIT(PRCM->SYS1_STATUS, PRCM_CPUN_DEEPSLEEP_LOCK_BIT); +} + +int HAL_PRCM_IsCPUASleep(void) +{ + return HAL_GET_BIT(PRCM->SYS1_STATUS, PRCM_CPUA_SLEEP_STATUS_BIT); +} + +int HAL_PRCM_IsCPUADeepSleep(void) +{ + return HAL_GET_BIT(PRCM->SYS1_STATUS, PRCM_CPUA_DEEPSLEEP_STATUS_BIT); +} + +int HAL_PRCM_IsSys1Alive(void) +{ + return HAL_GET_BIT(PRCM->SYS1_STATUS, PRCM_SYS1_ALIVE_BIT); +} + +void HAL_PRCM_DisableSys2(void) +{ + HAL_CLR_BIT(PRCM->SYS2_CTRL, PRCM_SYS2_ISOLATION_EN_BIT | + PRCM_CPUN_RESET_BIT | + PRCM_SYS2_RESET_BIT); +} + +void HAL_PRCM_EnableSys2Isolation(void) +{ + HAL_CLR_BIT(PRCM->SYS2_CTRL, PRCM_SYS2_ISOLATION_EN_BIT); +} + +void HAL_PRCM_DisableSys2Isolation(void) +{ + HAL_SET_BIT(PRCM->SYS2_CTRL, PRCM_SYS2_ISOLATION_EN_BIT); +} + +void HAL_PRCM_ForceCPUNReset(void) +{ + HAL_CLR_BIT(PRCM->SYS2_CTRL, PRCM_CPUN_RESET_BIT); +} + +void HAL_PRCM_ReleaseCPUNReset(void) +{ + HAL_SET_BIT(PRCM->SYS2_CTRL, PRCM_CPUN_RESET_BIT); +} + +int HAL_PRCM_IsCPUNReleased(void) +{ + return HAL_GET_BIT(PRCM->SYS2_CTRL, PRCM_CPUN_RESET_BIT); +} + +void HAL_PRCM_ForceSys2Reset(void) +{ + HAL_CLR_BIT(PRCM->SYS2_CTRL, PRCM_SYS2_RESET_BIT); +} + +void HAL_PRCM_ReleaseSys2Reset(void) +{ + HAL_SET_BIT(PRCM->SYS2_CTRL, PRCM_SYS2_RESET_BIT); +} + +int HAL_PRCM_IsCPUADeepSleepAllowed(void) +{ + return !HAL_GET_BIT(PRCM->SYS2_STATUS, PRCM_CPUA_DEEPSLEEP_LOCK_BIT); +} + +int HAL_PRCM_IsCPUNSleep(void) +{ + return HAL_GET_BIT(PRCM->SYS2_STATUS, PRCM_CPUN_SLEEP_STATUS_BIT); +} + +int HAL_PRCM_IsCPUNDeepSleep(void) +{ + return HAL_GET_BIT(PRCM->SYS2_STATUS, PRCM_CPUN_DEEPSLEEP_STATUS_BIT); +} + +int HAL_PRCM_IsSys2Alive(void) +{ + return HAL_GET_BIT(PRCM->SYS2_STATUS, PRCM_SYS2_ALIVE_BIT); +} + +int HAL_PRCM_IsSys3Alive(void) +{ + return HAL_GET_BIT(PRCM->SYS3_STATUS, PRCM_SYS3_ALIVE_BIT); +} + +void HAL_PRCM_SetSys1WakeupPowerFlags(uint32_t flags) +{ + PRCM->SYS1_WAKEUP_CTRL = flags & PRCM_SYS_WS_PWR_FLAGS_MASK; +} + +uint32_t HAL_PRCM_GetSys2WakeupPowerFlags(void) +{ + return (PRCM->SYS2_WAKEUP_CTRL & PRCM_SYS_WS_PWR_FLAGS_MASK); +} + +uint32_t HAL_PRCM_GetSys1SleepPowerFlags(void) +{ + return (PRCM->SYS1_SLEEP_CTRL & PRCM_SYS_WS_PWR_FLAGS_MASK); +} + +void HAL_PRCM_SetSys1SleepPowerFlags(uint32_t flags) +{ + PRCM->SYS1_SLEEP_CTRL = flags & PRCM_SYS_WS_PWR_FLAGS_MASK; +} + +uint32_t HAL_PRCM_GetSys2SleepPowerFlags(void) +{ + return (PRCM->SYS2_SLEEP_CTRL & PRCM_SYS_WS_PWR_FLAGS_MASK); +} + +void HAL_PRCM_SetSRAMVoltage(PRCM_SRAMVolt workVolt, PRCM_SRAMVolt retenVolt) +{ + PRCM->SRAM_VOLT_CTRL = + ((workVolt << PRCM_SRAM_WORK_VOLT_SHIFT) & PRCM_SRAM_WORK_VOLT_MASK) | + ((retenVolt << PRCM_SRAM_RETEN_VOLT_SHIFT) & PRCM_SRAM_RETEN_VOLT_MASK); +} + +void HAL_PRCM_SetBANDGAPSTABLE_TIME(uint32_t time) +{ + PRCM->BANDGAP_STABLE_REF_TIME = (time & PRCM_BANDGAP_STABLE_REF_TIME_MASK); +} + +uint32_t HAL_PRCM_GetBANDGAPSTABLE_TIME(void) +{ + return (PRCM->BANDGAP_STABLE_REF_TIME & PRCM_BANDGAP_STABLE_REF_TIME_MASK); +} + +void HAL_PRCM_SetDCDCSTABLE_TIME(uint32_t time) +{ + PRCM->DCDC_STABLE_REF_TIME = (time & PRCM_DCDC_STABLE_REF_TIME_MASK); +} + +uint32_t HAL_PRCM_GetDCDCSTABLE_TIME(void) +{ + return (PRCM->DCDC_STABLE_REF_TIME & PRCM_DCDC_STABLE_REF_TIME_MASK); +} + +void HAL_PRCM_SetCPUABootFlag(PRCM_CPUABootFlag flag) +{ + PRCM->CPUA_BOOT_FLAG = PRCM_CPUA_BOOT_FLAG_WR_LOCK | flag; +} + +uint32_t HAL_PRCM_GetCPUABootFlag(void) +{ + return HAL_GET_BIT(PRCM->CPUA_BOOT_FLAG, PRCM_CPUA_BOOT_FLAG_MASK); +} + +void HAL_PRCM_SetCPUABootAddr(uint32_t addr) +{ + PRCM->CPUA_BOOT_ADDR = addr; +} + +uint32_t HAL_PRCM_GetCPUABootAddr(void) +{ + return PRCM->CPUA_BOOT_ADDR; +} + +void HAL_PRCM_SetCPUABootArg(uint32_t arg) +{ + PRCM->CPUA_BOOT_ARG = arg; +} + +uint32_t HAL_PRCM_GetCPUABootArg(void) +{ + return PRCM->CPUA_BOOT_ARG; +} + +void HAL_PRCM_SetCPUAPrivateData(uint32_t data) +{ + PRCM->CPUA_PRIV_REG = data; +} + +uint32_t HAL_PRCM_GetCPUAPrivateData(void) +{ + return PRCM->CPUA_PRIV_REG; +} + +uint32_t HAL_PRCM_GetWakeupTimerEnable(void) +{ + return (PRCM->CPUA_WAKE_TIMER_CNT & PRCM_CPUx_WAKE_TIMER_EN_BIT); +} + +void HAL_PRCM_WakeupTimerEnable(void) +{ + HAL_SET_BIT(PRCM->CPUA_WAKE_TIMER_CNT, PRCM_CPUx_WAKE_TIMER_EN_BIT); +} + +void HAL_PRCM_WakeupTimerDisable(void) +{ + HAL_CLR_BIT(PRCM->CPUA_WAKE_TIMER_CNT, PRCM_CPUx_WAKE_TIMER_EN_BIT); +} + +uint32_t HAL_PRCM_WakeupTimerGetCurrentValue(void) +{ + return (PRCM->CPUA_WAKE_TIMER_CNT & PRCM_CPUx_WAKE_TIMER_CUR_VAL_MASK); +} + +uint32_t HAL_PRCM_GetWakeupTimerPending(void) +{ + return (PRCM->CPUA_WAKE_TIMER_CMP & PRCM_CPUx_WAKE_TIMER_PENDING_BIT); +} + +void HAL_PRCM_ClearWakeupTimerPending(void) +{ + HAL_SET_BIT(PRCM->CPUA_WAKE_TIMER_CMP, PRCM_CPUx_WAKE_TIMER_PENDING_BIT); +} + +void HAL_PRCM_WakeupTimerSetCompareValue(uint32_t val) +{ + PRCM->CPUA_WAKE_TIMER_CMP = val & PRCM_CPUx_WAKE_TIMER_CMP_VAL_MASK; +} + +uint32_t HAL_PRCM_WakeupTimerGetCompareValue(void) +{ + return (PRCM->CPUA_WAKE_TIMER_CMP & PRCM_CPUx_WAKE_TIMER_CMP_VAL_MASK); +} + +#if 0 +uint32_t HAL_PRCM_GetCPUNWakeupTimerCurrentValue(void) +{ + return (PRCM->CPUN_WAKE_TIMER_CNT & PRCM_CPUx_WAKE_TIMER_CUR_VAL_MASK); +} + +void HAL_PRCM_SetCPUNWakeupTimerCompareValue(uint32_t val) +{ + PRCM->CPUN_WAKE_TIMER_CMP = val & PRCM_CPUx_WAKE_TIMER_CMP_VAL_MASK); +} + +uint32_t HAL_PRCM_GetCPUNWakeupTimerCompareValue(void) +{ + return (PRCM->CPUN_WAKE_TIMER_CMP & PRCM_CPUx_WAKE_TIMER_CMP_VAL_MASK); +} +#endif + +void HAL_PRCM_WakeupIOEnable(uint32_t ioMask) +{ + HAL_SET_BIT(PRCM->CPUA_WAKE_IO_EN, ioMask); +} + +void HAL_PRCM_WakeupIODisable(uint32_t ioMask) +{ + HAL_CLR_BIT(PRCM->CPUA_WAKE_IO_EN, ioMask); +} + +void HAL_PRCM_WakeupIOSetRisingEvent(uint32_t ioMask) +{ + HAL_SET_BIT(PRCM->CPUA_WAKE_IO_MODE, ioMask); +} + +void HAL_PRCM_WakeupIOSetFallingEvent(uint32_t ioMask) +{ + HAL_CLR_BIT(PRCM->CPUA_WAKE_IO_MODE, ioMask); +} + +uint32_t HAL_PRCM_WakeupIOGetEventStatus(void) +{ + return HAL_GET_BIT(PRCM->CPUA_WAKE_IO_STATUS, PRCM_WAKE_IO_MASK); +} + +int HAL_PRCM_WakeupIOIsEventDetected(uint32_t ioMask) +{ + return HAL_GET_BIT(PRCM->CPUA_WAKE_IO_STATUS, ioMask); +} + +void HAL_PRCM_WakeupIOClearEventDetected(uint32_t ioMask) +{ + HAL_SET_BIT(PRCM->CPUA_WAKE_IO_STATUS, ioMask); +} + +void HAL_PRCM_WakeupIOEnableCfgHold(uint32_t ioMask) +{ + HAL_SET_BIT(PRCM->CPUA_WAKE_IO_HOLD, ioMask); +} + +void HAL_PRCM_WakeupIODisableCfgHold(uint32_t ioMask) +{ + HAL_CLR_BIT(PRCM->CPUA_WAKE_IO_HOLD, ioMask); +} + +void HAL_PRCM_WakeupIOEnableGlobal(void) +{ + HAL_SET_BIT(PRCM->CPUA_WAKE_IO_GLOBAL_EN, PRCM_WAKE_IO_GLOBAL_EN_BIT); +} + +void HAL_PRCM_WakeupIODisableGlobal(void) +{ + HAL_CLR_BIT(PRCM->CPUA_WAKE_IO_GLOBAL_EN, PRCM_WAKE_IO_GLOBAL_EN_BIT); +} + +void HAL_PRCM_Start(void) +{ + HAL_CLR_BIT(PRCM->CPUA_PRCM_REG, PRCM_CPUA_PRCM_REG_BIT); +} + +#if 0 +void HAL_PRCM_EnableCPUAWakeupTimer(void) +{ + HAL_SET_BIT(PRCM->CPUA_WAKE_TIMER_CNT, PRCM_CPUx_WAKE_TIMER_EN_BIT); +} + +void HAL_PRCM_DisableCPUAWakeupTimer(void) +{ + HAL_CLR_BIT(PRCM->CPUA_WAKE_TIMER_CNT, PRCM_CPUx_WAKE_TIMER_EN_BIT); +} + +uint32_t HAL_PRCM_GetCPUAWakeupTimerCurrentValue(void) +{ + return (PRCM->CPUA_WAKE_TIMER_CNT & PRCM_CPUx_WAKE_TIMER_CUR_VAL_MASK); +} + +void HAL_PRCM_SetCPUAWakeupTimerCompareValue(uint32_t val) +{ + PRCM->CPUA_WAKE_TIMER_CMP = val & PRCM_CPUx_WAKE_TIMER_CMP_VAL_MASK; +} + +uint32_t HAL_PRCM_GetCPUAWakeupTimerCompareValue(void) +{ + return (PRCM->CPUA_WAKE_TIMER_CMP & PRCM_CPUx_WAKE_TIMER_CMP_VAL_MASK); +} + +#if 0 +uint32_t HAL_PRCM_GetCPUNWakeupTimerCurrentValue(void) +{ + return (PRCM->CPUN_WAKE_TIMER_CNT & PRCM_CPUx_WAKE_TIMER_CUR_VAL_MASK); +} + +void HAL_PRCM_SetCPUNWakeupTimerCompareValue(uint32_t val) +{ + PRCM->CPUN_WAKE_TIMER_CMP = val & PRCM_CPUx_WAKE_TIMER_CMP_VAL_MASK); +} + +uint32_t HAL_PRCM_GetCPUNWakeupTimerCompareValue(void) +{ + return (PRCM->CPUN_WAKE_TIMER_CMP & PRCM_CPUx_WAKE_TIMER_CMP_VAL_MASK); +} +#endif + +void HAL_PRCM_EnableWakeupIO(uint32_t ioMask) +{ + HAL_SET_BIT(PRCM->CPUA_WAKE_IO_EN, ioMask); +} + +void HAL_PRCM_DisableWakeupIO(uint32_t ioMask) +{ + HAL_CLR_BIT(PRCM->CPUA_WAKE_IO_EN, ioMask); +} + +void HAL_PRCM_SetWakeupIORisingEvent(uint32_t ioMask) +{ + HAL_SET_BIT(PRCM->CPUA_WAKE_IO_MODE, ioMask); +} + +void HAL_PRCM_SetWakeupIOFallingEvent(uint32_t ioMask) +{ + HAL_CLR_BIT(PRCM->CPUA_WAKE_IO_MODE, ioMask); +} + +uint32_t HAL_PRCM_GetWakeupIOEventStatus(void) +{ + return HAL_GET_BIT(PRCM->CPUA_WAKE_IO_STATUS, PRCM_WAKE_IO_MASK); +} + +int HAL_PRCM_IsWakeupIOEventDetected(uint32_t ioMask) +{ + return HAL_GET_BIT(PRCM->CPUA_WAKE_IO_STATUS, ioMask); +} + +void HAL_PRCM_ClearWakeupIOEventDetected(uint32_t ioMask) +{ + HAL_SET_BIT(PRCM->CPUA_WAKE_IO_STATUS, ioMask); +} + +void HAL_PRCM_EnableWakeupIOCfgHold(uint32_t ioMask) +{ + HAL_SET_BIT(PRCM->CPUA_WAKE_IO_HOLD, ioMask); +} + +void HAL_PRCM_DisableWakeupIOCfgHold(uint32_t ioMask) +{ + HAL_CLR_BIT(PRCM->CPUA_WAKE_IO_HOLD, ioMask); +} + +void HAL_PRCM_EnableWakeupIOGlobal(void) +{ + HAL_SET_BIT(PRCM->CPUA_WAKE_IO_GLOBAL_EN, PRCM_WAKE_IO_GLOBAL_EN_BIT); +} + +void HAL_PRCM_DisableWakeupIOGlobal(void) +{ + HAL_CLR_BIT(PRCM->CPUA_WAKE_IO_GLOBAL_EN, PRCM_WAKE_IO_GLOBAL_EN_BIT); +} +#endif diff --git a/platform/mcu/xr871/src/driver/chip/hal_pwm.c b/platform/mcu/xr871/src/driver/chip/hal_pwm.c new file mode 100644 index 0000000000..e6863ab773 --- /dev/null +++ b/platform/mcu/xr871/src/driver/chip/hal_pwm.c @@ -0,0 +1,955 @@ +/** + * @file hal_pwm.c + * @author XRADIO IOT WLAN Team + */ + +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "kernel/os/os.h" +#include "hal_base.h" +#include "driver/chip/hal_pwm.h" + +#define DBG_PWM 0 +#define PWM_DBG(fmt, arg...) \ + HAL_LOG(HAL_DBG_ON && DBG_PWM, "[HAL PWM] "fmt, ##arg) + + +#define MAXCNTRVAL 65535 +#define RISECH(ch) (HAL_BIT(ch) * HAL_BIT(ch)) +#define FALLCH(ch) (HAL_BIT(ch) * HAL_BIT(ch) * 2) +#define PWM_IRQ_ALL_BITS ((1 << (PWM_CH_NUM << 1)) - 1) + +static uint32_t Cap_priv = 0; +static uint8_t IoInitCount = 0; +static PWM_IrqParam PWM_IrqPrivate[8]; + +/** + * @internal + * @brief eanble pwm module. + * @note This function is used to enable the pwm module source clock. + * @param none. + * @retval none. + */ +static void PWM_ModuleEnable() +{ + HAL_CCM_BusDisablePeriphClock(CCM_BUS_PERIPH_BIT_PWM); + HAL_CCM_BusForcePeriphReset(CCM_BUS_PERIPH_BIT_PWM); + HAL_CCM_BusReleasePeriphReset(CCM_BUS_PERIPH_BIT_PWM); + HAL_CCM_BusEnablePeriphClock(CCM_BUS_PERIPH_BIT_PWM); +} + +/** + * @internal + * @brief disable pwm module. + * @note This function is used to disable the pwm module source clock. + * @param none. + * @retval none. + */ +static void PWM_ModuleDisable() +{ + HAL_CCM_BusDisablePeriphClock(CCM_BUS_PERIPH_BIT_PWM); +} + +static void PWM_EnableModuleIRQ() +{ + HAL_NVIC_SetPriority(PWM_ECT_IRQn, NVIC_PERIPHERAL_PRIORITY_DEFAULT); + HAL_NVIC_EnableIRQ(PWM_ECT_IRQn); +} + +static void PWM_DisableModuleIRQ() +{ + HAL_NVIC_DisableIRQ(PWM_ECT_IRQn); +} + +static PWM_GROUP_ID PWM_ChToGroup(PWM_CH_ID ch_id) +{ + if (ch_id >= PWM_CH_NUM) + return PWM_GROUP_NULL; + + return ch_id / 2; +} + +static PWM_CH_ID PWM_GroupToCh0(PWM_GROUP_ID group_id) +{ + if (group_id >= PWM_GROUP_NUM) + return PWM_GROUP_NULL; + + return group_id * 2; +} + +static PWM_CH_ID PWM_GroupToCh1(PWM_GROUP_ID group_id) +{ + if (group_id >= PWM_GROUP_NUM) + return PWM_GROUP_NULL; + + return group_id * 2 + 1; +} + +static uint32_t PWM_ReadGroupClkFreq(PWM_GROUP_ID group_id) +{ + if (group_id >= PWM_GROUP_NUM) + return 0; + + uint32_t src_clk_freq = 0; + __IO uint32_t *reg = &PWM->PCCR[group_id]; + uint32_t clk = *reg & PWM_SRC_CLK_SELECT; + uint32_t div = *reg & PWM_SRC_CLK_DIV_V; + + if (clk == 0) { + src_clk_freq = HAL_GetHFClock(); + PWM_DBG("%s, %d HRCLK\n", __func__, __LINE__); + } else { + src_clk_freq = HAL_GetAPBClock(); + PWM_DBG("%s, %d APBCLK\n", __func__, __LINE__); + } + + src_clk_freq /= (1 << div); + + return src_clk_freq; +} + + +static int PWM_ChClkDiv(PWM_CH_ID ch_id, PWM_ChInitParam *param) +{ + if (ch_id >= PWM_CH_NUM) + return 0; + + int ch_clk_freq = 1; + uint32_t minFreq = 0; + uint8_t ch_div = 0; + uint32_t temp1 = 0, temp2 = 0; + __IO uint32_t *reg = NULL; + uint32_t src_clk_freq = PWM_ReadGroupClkFreq(PWM_ChToGroup(ch_id)); + + PWM_DBG("SRC_CLK freq = %d\n", src_clk_freq); + + if ((src_clk_freq % MAXCNTRVAL) > 0) + temp1 = 1; + if (((src_clk_freq + temp1) % 256) > 0) + temp2 = 1; + + minFreq = (src_clk_freq / MAXCNTRVAL + temp1)/ 256 + temp2; + + if (param->hz > src_clk_freq || param->hz < minFreq) + ch_clk_freq = 0; + + if (param->hz > (src_clk_freq / MAXCNTRVAL + temp1)) + ch_div = 0; + else { + ch_div = (src_clk_freq / MAXCNTRVAL + temp1) % param->hz; + if (ch_div) + ch_div = (src_clk_freq / MAXCNTRVAL + temp1) / param->hz; + else + ch_div = (src_clk_freq / MAXCNTRVAL + temp1) / param->hz - 1; + } + + + PWM_DBG("ch div = %d\n", ch_div); + + reg = &PWM->CH_REG[ch_id].PCR; + + if (ch_div > 255) + return 0; + + uint32_t temp = *reg; + temp &= ~PWM_PCR_PRESCAL; + temp |= ch_div; + *reg = temp; //Source clock Frequency Division + + if (ch_clk_freq != 0) + ch_clk_freq = src_clk_freq / (ch_div + 1); + + return ch_clk_freq; +} + + +static void PWM_ChSetPolarity(PWM_CH_ID ch_id, PWM_ChInitParam *param) +{ + if (ch_id >= PWM_CH_NUM) + return; + + __IO uint32_t *reg = NULL; + + reg = &PWM->CH_REG[ch_id].PCR; + + if (param->polarity == PWM_LOWLEVE) + HAL_CLR_BIT(*reg, PWM_PCR_ACT_STA); + else if (param->polarity == PWM_HIGHLEVE) + HAL_SET_BIT(*reg, PWM_PCR_ACT_STA); +} + +static void PWM_ChSetMode(PWM_CH_ID ch_id, PWM_ChInitParam *param) +{ + if (ch_id >= PWM_CH_NUM) + return; + + __IO uint32_t *reg = NULL; + reg = &PWM->CH_REG[ch_id].PCR; + + if (param->mode == PWM_PLUSE_MODE) + HAL_SET_BIT(*reg, PWM_PCR_MODE); + else if (param->mode == PWM_CYCLE_MODE) + HAL_CLR_BIT(*reg, PWM_PCR_MODE); +} + +static int PWM_OutPeriodRady(PWM_CH_ID ch_id) +{ + if (ch_id >= PWM_CH_NUM) + return -1; + __IO uint32_t *reg = NULL; + reg = &PWM->CH_REG[ch_id].PCR; + + return HAL_GET_BIT(*reg, PWM_PCR_PERIODRDY); + +} + +static void PWM_OutSetCycle(PWM_CH_ID ch_id, uint16_t value) +{ + + if (ch_id >= PWM_CH_NUM) + return; + + __IO uint32_t *reg = &PWM->CH_REG[ch_id].PPR;; + + uint32_t temp = *reg; + temp &= ~PWM_PPR_ENTIER_CYCLE; + temp |= (value << 16); + *reg = temp; +} + +static int PWM_OutModeInit(PWM_CH_ID ch_id, PWM_ChInitParam *param) +{ + int ch_cycle_value = 0; + + ch_cycle_value = PWM_ChClkDiv(ch_id, param); + + PWM_DBG("ch freq = %d\n", ch_cycle_value); + + if (ch_cycle_value == 0) + return -1; + + if (param->hz > (ch_cycle_value / 2)) + return -1; + + ch_cycle_value /= param->hz; + + PWM_DBG("ch_cycle_value = %d\n", ch_cycle_value); + + PWM_ChSetPolarity(ch_id, param); + PWM_ChSetMode(ch_id, param); + + while (PWM_OutPeriodRady(ch_id) == 1) + OS_MSleep(10); + + PWM_OutSetCycle(ch_id, ch_cycle_value - 1); + + return ch_cycle_value; +} + +static int PWM_InputInit(PWM_CH_ID ch_id, PWM_ChInitParam *param) +{ + int clk_freq = PWM_ChClkDiv(ch_id, param); + + if (clk_freq == 0) { + HAL_ERR("Hz out of range\n"); + return -1; + } + + PWM_ChSetPolarity(ch_id, param); + PWM_ChSetMode(ch_id, param); + + return 0; +} + +static int PWM_EnableClock(PWM_CH_ID ch_id, uint8_t en) +{ + PWM_GROUP_ID group = PWM_ChToGroup(ch_id); + + __IO uint32_t *reg = NULL; + + if (group >= PWM_GROUP_NUM) + return -1; + + reg = &PWM->PCCR[group]; + + if (en == 1) + HAL_SET_BIT(*reg, PWM_CH_CLK_GATING); + else + HAL_CLR_BIT(*reg, PWM_CH_CLK_GATING); + + return 0; +} + +static void PWM_Init(PWM_CH_ID ch_id) +{ + int is_init = IoInitCount & (1 << ch_id); + + if (!is_init) { + HAL_BoardIoctl(HAL_BIR_PINMUX_INIT, HAL_MKDEV(HAL_DEV_MAJOR_PWM, ch_id), 0); + IoInitCount |= 1 << ch_id; + } +} + +static void PWM_DeInit(PWM_CH_ID ch_id) +{ + + + int is_init = IoInitCount & (1 << ch_id); + PWM_CH_ID brother; + + if (ch_id % 2) + brother = ch_id - 1; + else + brother = ch_id + 1; + + int brother_is_init = IoInitCount & (1 << brother); + + if(is_init) { + HAL_BoardIoctl(HAL_BIR_PINMUX_DEINIT, HAL_MKDEV(HAL_DEV_MAJOR_PWM, ch_id), 0); + + if (!brother_is_init) + PWM_EnableClock(ch_id, 0); + + IoInitCount &= ~(1 << ch_id); + + if (IoInitCount == 0) { + PWM_DBG("pwm module disable\n"); + PWM_ModuleDisable(); + } + } +} + +__STATIC_INLINE void PWM_EnableOutputIRQ(PWM_CH_ID ch_id, uint8_t en) +{ + if (en) + HAL_SET_BIT(PWM->PIER, HAL_BIT(ch_id)); + else + HAL_CLR_BIT(PWM->PIER, HAL_BIT(ch_id)); +} + +__STATIC_INLINE void PWM_EnableInuptRiseEdgeIRQ(PWM_CH_ID ch_id, uint8_t en) +{ + if (en) + HAL_SET_BIT(PWM->CIER, RISECH(ch_id)); + else + HAL_CLR_BIT(PWM->CIER, RISECH(ch_id)); +} + +__STATIC_INLINE void PWM_EnableInuputFallEdgeIRQ(PWM_CH_ID ch_id, uint8_t en) +{ + if (en) + HAL_SET_BIT(PWM->CIER, FALLCH(ch_id)); + else + HAL_CLR_BIT(PWM->CIER, FALLCH(ch_id)); +} + +__STATIC_INLINE void PWM_EnableInputBothEdgeIRQ(PWM_CH_ID ch_id, uint8_t en) +{ + if (en) + HAL_SET_BIT(PWM->CIER, (RISECH(ch_id) | FALLCH(ch_id))); + else + HAL_CLR_BIT(PWM->CIER, (RISECH(ch_id) | FALLCH(ch_id))); +} + +static uint32_t PWM_CycleValue(PWM_CH_ID ch_id) +{ + __IO uint32_t *reg; + + reg = &PWM->CH_REG[ch_id].PPR; + + return (*reg & PWM_PPR_ENTIER_CYCLE) >> 16; +} + +static int PWM_CycleIsReady(PWM_CH_ID ch_id) +{ + __IO uint32_t* reg; + + reg = &PWM->CH_REG[ch_id].PCR; + + return HAL_GET_BIT(*reg, PWM_PCR_PERIODRDY); +} + +static void PWM_SetActCycle(PWM_CH_ID ch_id, uint16_t cycle) +{ + __IO uint32_t *reg; + reg = &PWM->CH_REG[ch_id].PPR; + + uint32_t p = *reg; + p &= ~PWM_PPR_ACT_CYCLE; + p |= cycle; + *reg = p; +} + +static void PWM_EnableDeadZone(PWM_GROUP_ID group_id, uint8_t en) +{ + __IO uint32_t *reg = NULL; + + reg = &PWM->PDZCR[group_id]; + + if (en) + HAL_SET_BIT(*reg, PWM_CH_DZ_EN); + else + HAL_CLR_BIT(*reg, PWM_CH_DZ_EN); +} + +static int PWM_FallEdgeLock(PWM_CH_ID ch_id) +{ + __IO uint32_t *reg = &PWM->CH_REG[ch_id].CCR;; + + return *reg & PWM_CCR_CFLF; +} + +static void PWM_ClearFallEdgeLock(PWM_CH_ID ch_id) +{ + __IO uint32_t *reg = &PWM->CH_REG[ch_id].CCR; + + *reg |= PWM_CCR_CFLF; +} + +static int PWM_RiseEdgeLock(PWM_CH_ID ch_id) +{ + __IO uint32_t *reg = &PWM->CH_REG[ch_id].CCR; + + return *reg & PWM_CCR_CRLF; +} + +static void PWM_ClearRiseEdgeLock(PWM_CH_ID ch_id) +{ + __IO uint32_t *reg = &PWM->CH_REG[ch_id].CCR; + if(reg == NULL) + return; + *reg |= PWM_CCR_CRLF; +} + +static uint16_t PWM_CRLRValue(PWM_CH_ID ch_id) +{ + __IO uint32_t *reg = &PWM->CH_REG[ch_id].CRLR; + + return ((uint16_t)(*reg & PWM_CRLR)); +} + +static uint16_t PWM_CFLRValue(PWM_CH_ID ch_id) +{ + __IO uint32_t *reg = &PWM->CH_REG[ch_id].CFLR; + + return ((uint16_t)(*reg & PWM_CFLR)); +} + +static void PWM_OutIRQHandle() +{ + uint32_t i; + uint32_t status; + uint32_t is_irq; + PWM_IrqParam *irq; + + status = PWM->PISR & PWM->PIER & PWM_IRQ_ALL_BITS; /* get pending bits */ + PWM->PISR = status; /* clear IRQ bits */ + irq = PWM_IrqPrivate; + + for (i = PWM_GROUP0_CH0; i < PWM_CH_NUM && status != 0; i++) { + is_irq = status & HAL_BIT(0); + if (is_irq && irq[i].callback) { + PWM_IrqEvent event = PWM_IRQ_OUTPUT; + irq[i].callback(irq[i].arg, event); + } + status >>= 1; + } +} + +static void PWM_InputIRQHandle() +{ + uint32_t i; + uint32_t status; + uint32_t rise_edge; + uint32_t fall_edge; + PWM_IrqParam *irq; + status = PWM->CISR & PWM->CIER & PWM_IRQ_ALL_BITS; /* get pending bits */ + PWM->CISR = status; /* clear IRQ bits */ + irq = PWM_IrqPrivate; + + for (i = PWM_GROUP0_CH0; i < PWM_CH_NUM && status != 0; i++) { + rise_edge = status & HAL_BIT(0); + if (rise_edge && irq[i].callback) { + PWM_IrqEvent event = PWM_IRQ_RISEEDGE; + irq[i].callback(irq[i].arg, event); + } + fall_edge = status & HAL_BIT(1); + if (fall_edge && irq[i].callback) { + PWM_IrqEvent event = PWM_IRQ_FALLEDGE; + irq[i].callback(irq[i].arg, event); + } + status >>= 2; + } +} + +void PWM_ECT_IRQHandler() +{ + if (PWM->CIER > 0) + PWM_InputIRQHandle(); + + if (PWM->PIER > 0) + PWM_OutIRQHandle(); +} + +/** + * @brief Configure the pwm group source clock. + * @note This function is used to configure the group source clock , + * each group contains two channels. + * For example : The group_0's clock is effect channel0 and channel 1. + * @param group_id: The pwm group id. + * @param param: + * @arg param->clk:The source clk of this group. + * @arg param->div:The division for source clk. + * @retval HAL_Status: The status of driver + */ +HAL_Status HAL_PWM_GroupClkCfg(PWM_GROUP_ID group_id, PWM_ClkParam *param) +{ + if (group_id >= PWM_GROUP_NUM) + return HAL_ERROR; + + if (IoInitCount == 0) { + PWM_ModuleEnable(); + } + + __IO uint32_t *reg = &PWM->PCCR[group_id]; + + if (param->clk == PWM_CLK_HOSC) { + *reg &= ~PWM_SRC_CLK_SELECT; + } else if (param->clk == PWM_CLK_APB1){ + HAL_SET_BIT(*reg, PWM_SRC_CLK_SELECT); + HAL_CLR_BIT(*reg, HAL_BIT(8)); + } else + return HAL_ERROR; + + if (param->div > PWM_SRC_CLK_DIV_256 || param->div < 0) + return HAL_ERROR; + + uint32_t temp = *reg; + temp &= ~PWM_SRC_CLK_DIV_V; + temp |= param->div; + *reg = temp; + + return HAL_OK; +} + +/** + * @brief Init the pwm channel. + * @note This function is used to configure the channel pin(IO mode), output frequency , + * polarity and rum mode. + * @param ch_id: The pwm channel id. + * @param param: + * @arg param->mode:The channels run mode. + * @arg param->polarity:The channels polarity. + * @arg param->hz:The channels output frequency. + * @retval max_duty_ratio: The channel max duty cycle value. if the mode is + * capture, the return value is 0. error return -1; + */ +int HAL_PWM_ChInit(PWM_CH_ID ch_id, PWM_ChInitParam *param) +{ + int duty_ratio = -1; + if (ch_id >= PWM_CH_NUM) + return -1; + + PWM_Init(ch_id); + + switch(param->mode) { + case PWM_CYCLE_MODE: + case PWM_PLUSE_MODE: + duty_ratio = PWM_OutModeInit(ch_id, param); + break; + case PWM_CAPTURE_MODE: + duty_ratio = PWM_InputInit(ch_id, param); + break; + default: + return -1; + } + + return duty_ratio; +} + +/** + * @brief Deinit the pwm channel. + * @note This function is used to deinit the channel pin, reduce power consumption. + * @param ch_id: The pwm channel id. + * @retval HAL_Status: The status of driver. + */ +HAL_Status HAL_PWM_ChDeinit(PWM_CH_ID ch_id) +{ + if (ch_id >= PWM_CH_NUM) + return HAL_ERROR; + + PWM_DeInit(ch_id); + + return HAL_OK; +} + +/** + * @brief Init the complementary mode. + * @note This function is used to configure the groups pin(IO mode), output frequency , + * polarity. + * This mode is two channel output complementart waveform, the channel run in + * cycle mode. + * @param group_id: The pwm group id. + * @param param: + * @arg param->polarity:The channels polarity. + * @arg param->hz:The channels output frequency. + * @retval HAL_Status: The channel max duty cycle value. + */ +int HAL_PWM_ComplementaryInit(PWM_GROUP_ID group_id, PWM_CompInitParam *param) +{ + if (group_id >= PWM_GROUP_NUM) + return -1; + + int cycle_value = -1; + PWM_CH_ID ch_low = group_id * 2; + PWM_CH_ID ch_high = ch_low + 1; + PWM_ChInitParam ch_param; + + PWM_Init(ch_low); + PWM_Init(ch_high); + + ch_param.hz = param->hz; + ch_param.polarity = param->polarity; + ch_param.mode = PWM_CYCLE_MODE; + + cycle_value = PWM_OutModeInit(ch_low, &ch_param); + if (cycle_value == -1) + return -1; + + if (ch_param.polarity == PWM_LOWLEVE) + ch_param.polarity = PWM_HIGHLEVE; + else if (ch_param.polarity == PWM_HIGHLEVE) + ch_param.polarity = PWM_LOWLEVE; + else + return -1; + + PWM_OutModeInit(ch_high, &ch_param); + + return cycle_value; +} + +/** + * @brief Deinit the pwm complementary. + * @note This function is used to deinit the groupl pin, reduce power consumption. + * @param group_id: The pwm group id. + * @retval HAL_Status: The status of driver. + */ +HAL_Status HAL_PWM_ComplementaryDeInit(PWM_GROUP_ID group_id) +{ + if (group_id >= PWM_GROUP_NUM) + return HAL_ERROR; + + PWM_CH_ID ch_low = group_id * 2; + PWM_CH_ID ch_high = ch_low + 1; + + PWM_DeInit(ch_low); + PWM_DeInit(ch_high); + + return HAL_OK; +} + + +/** + * @brief Enable the pwm channel. + * @param ch_id: The pwm channel id. + * @param mode: The pwm channel run mode. + * @param en: set 1 enable , 0 disable. + * @retval HAL_Status: The status of driver. + */ +HAL_Status HAL_PWM_EnableCh(PWM_CH_ID ch_id, PWM_Mode mode, uint8_t en) +{ + if (ch_id >= PWM_CH_NUM) + return HAL_ERROR; + + PWM_EnableClock(ch_id, en); + + if (en) { + if (mode == PWM_CAPTURE_MODE) { + HAL_CLR_BIT(PWM->PER, HAL_BIT(ch_id)); + HAL_SET_BIT(PWM->CER, HAL_BIT(ch_id)); + } else { + HAL_CLR_BIT(PWM->CER, HAL_BIT(ch_id)); + HAL_SET_BIT(PWM->PER, HAL_BIT(ch_id)); + } + } else { + HAL_CLR_BIT(PWM->CER, HAL_BIT(ch_id)); + HAL_CLR_BIT(PWM->PER, HAL_BIT(ch_id)); + } + + return HAL_OK; + +} + + +/** + * @brief Output one pluse.Use this function you need already enable the channel. + * @param ch_id: The pwm channel id. + * @retval HAL_Status: The status of driver. + */ +HAL_Status HAL_PWM_OutputPluse(PWM_CH_ID ch_id) +{ + __IO uint32_t* reg = NULL; + reg = &PWM->CH_REG[ch_id].PCR; + + if ((*reg & PWM_PCR_PLUSE_START) > 0) + return HAL_BUSY; + + HAL_SET_BIT(*reg, PWM_PCR_PLUSE_START); + + return HAL_OK; +} + +/** + * @brief Enable the complementary. + * @param group_id: The pwm group id. + * @param en: set 1 enable , 0 disable. + * @retval HAL_Status: The status of driver. + */ +HAL_Status HAL_PWM_EnableComplementary(PWM_GROUP_ID group_id, uint8_t en) +{ + if (group_id >= PWM_GROUP_NUM) + return HAL_ERROR; + + PWM_CH_ID ch_id_0 = PWM_GroupToCh0(group_id); + PWM_CH_ID ch_id_1 = PWM_GroupToCh1(group_id); + PWM_EnableClock(ch_id_0, en); + PWM_EnableClock(ch_id_1, en); + if (en) { + HAL_CLR_BIT(PWM->CER, HAL_BIT(ch_id_0) | HAL_BIT(ch_id_1)); + HAL_SET_BIT(PWM->PER, HAL_BIT(ch_id_0) | HAL_BIT(ch_id_1)); + } else + HAL_CLR_BIT(PWM->PER, HAL_BIT(ch_id_0) | HAL_BIT(ch_id_1)); + + return HAL_OK; +} + +/** + * @brief Enable the pwm channel's interrupt. + * @param ch_id: The pwm channel id. + * @param param: + * @arg param->event:The interrupt event. + * @arg param->callback:The interrupt callback. + * @arg param->arg: The param for callback. + * @retval HAL_Status: The status of driver. + */ +HAL_Status HAL_PWM_EnableIRQ(PWM_CH_ID ch_id, const PWM_IrqParam *param) +{ + + if (PWM->CIER == 0 && PWM->PIER == 0) + PWM_EnableModuleIRQ(); + + switch (param->event) { + case PWM_IRQ_OUTPUT: + PWM_EnableOutputIRQ(ch_id, 1); + break; + case PWM_IRQ_RISEEDGE: + PWM_EnableInuptRiseEdgeIRQ(ch_id, 1); + break; + case PWM_IRQ_FALLEDGE: + PWM_EnableInuputFallEdgeIRQ(ch_id, 1); + break; + case PWM_IRQ_BOTHEDGE: + PWM_EnableInputBothEdgeIRQ(ch_id, 1); + break; + case PWM_IRQ_NULL: + break; + default: + HAL_WRN("PWM invalid IRQ event, event: %d\n", param->event); + return HAL_ERROR; + } + + PWM_IrqPrivate[ch_id] = *param; + + return HAL_OK; +} + + +/** + * @brief Disable the pwm channel's interrupt. + * @param ch_id: The pwm channel id. + * @retval HAL_Status: The status of driver. + */ +void HAL_PWM_DisableIRQ(PWM_CH_ID ch_id) +{ + PWM_EnableOutputIRQ(ch_id, 0); + PWM_EnableInputBothEdgeIRQ(ch_id, 0); + + if (PWM->CIER == 0 && PWM->PIER == 0) + PWM_DisableModuleIRQ(); +} + + +/** + * @brief Set channel's duty ratio. + * @param ch_id: The pwm channel id. + * @param value: The duty ratio value. + * @retval HAL_Status: The status of driver. + */ +HAL_Status HAL_PWM_ChSetDutyRatio(PWM_CH_ID ch_id, uint16_t value) +{ + + if (ch_id >= PWM_CH_NUM) { + HAL_ERR("Invalid ch_id\n"); + return HAL_ERROR; + } + + if (value > PWM_CycleValue(ch_id)) { + HAL_ERR("value is out of range , should <= %d\n",PWM_CycleValue(ch_id)); + return HAL_ERROR; + } + + while (PWM_CycleIsReady(ch_id) == 1) + OS_MSleep(10); + + PWM_SetActCycle(ch_id, value); + + return HAL_OK; +} + +/** + * @brief Set Complementary duty ratio. + * @param group_id: The pwm group id. + * @param value: The duty ratio value. + * @retval HAL_Status: The status of driver. + */ +HAL_Status HAL_PWM_ComplementarySetDutyRatio(PWM_GROUP_ID group_id, uint16_t value) +{ + PWM_CH_ID ch_0, ch_1; + + ch_0 = PWM_GroupToCh0(group_id); + ch_1 = PWM_GroupToCh1(group_id); + + if (HAL_PWM_ChSetDutyRatio(ch_0, value) != HAL_OK) + return HAL_ERROR; + if (HAL_PWM_ChSetDutyRatio(ch_1, value) != HAL_OK) + return HAL_ERROR; + + return HAL_OK; +} + +/** + * @brief Set dead zone for complementary mode. + * @param group_id: The pwm group id. + * @param value: The dead zone value. + * @retval HAL_Status: The status of driver. + */ +HAL_Status HAL_PWM_SetDeadZoneTime(PWM_GROUP_ID group_id, uint8_t dead_zone_value) +{ + __IO uint32_t *reg = NULL; + + if (group_id >= PWM_GROUP_NUM) { + HAL_ERR("Invalid group id\n"); + return HAL_ERROR; + } + + reg = &PWM->PDZCR[group_id]; + uint32_t p = *reg; + + p &= ~PWM_CH_DZ_INV; + p |= (dead_zone_value << 8); + *reg = p; + + return HAL_OK; +} + +/** + * @brief Enable the dead zone. + * @param group_id: The pwm group id. + * @param en: set 1 enable, 0 disable. + * @retval HAL_Status: The status of driver. + */ +HAL_Status HAL_PWM_EnableDeadZone(PWM_GROUP_ID group_id, uint8_t en) +{ + if (group_id >= PWM_GROUP_NUM) { + HAL_ERR("Invalid group id\n"); + return HAL_ERROR; + } + if (en) + PWM_EnableDeadZone(group_id, 1); + else + PWM_EnableDeadZone(group_id, 0); + + return HAL_OK; +} + +/** + * @brief The pwm channel capture result. + * @param mode: + * @arg PWM_CAP_PLUSE: used for capture pluse. Run in the mode, + * only the highLevlTime is valid. + * @arg PWM_CAP_CYCLE: used for cycle signal. capture the signal's period, + * high level time and low level time. + * @param ch_id: The pwm channel id. + * @retval PWM_CapResult: The result of capture. + */ +PWM_CapResult HAL_PWM_CaptureResult(PWM_CaptureMode mode, PWM_CH_ID ch_id) +{ + PWM_CapResult result = {0, 0, 0}; + uint16_t fall_time = 0, rise_time = 0; + uint32_t temp = 0; + + switch (mode) { + case PWM_CAP_PLUSE: + if (PWM_FallEdgeLock(ch_id) && PWM_RiseEdgeLock(ch_id)) { + PWM_ClearFallEdgeLock(ch_id); + PWM_ClearRiseEdgeLock(ch_id); + fall_time = PWM_CRLRValue(ch_id); + rise_time = PWM_CFLRValue(ch_id); + + result.highLevelTime = rise_time ; + result.lowLevelTime = 0 ; + result.periodTime = 0; + } + break; + case PWM_CAP_CYCLE: + if (PWM_RiseEdgeLock(ch_id)) { + temp = Cap_priv & (1 << ch_id); + if (temp) { + PWM_ClearFallEdgeLock(ch_id); + PWM_ClearRiseEdgeLock(ch_id); + fall_time = PWM_CRLRValue(ch_id); + rise_time = PWM_CFLRValue(ch_id); + + result.highLevelTime = rise_time ; + result.lowLevelTime = fall_time ; + result.periodTime = rise_time + fall_time; + Cap_priv &= ~(1 << ch_id); + } else { + PWM_ClearRiseEdgeLock(ch_id); + Cap_priv |= (1 << ch_id); + } + } + break; + default : + break; + } + + return result; +} diff --git a/platform/mcu/xr871/src/driver/chip/hal_rtc.c b/platform/mcu/xr871/src/driver/chip/hal_rtc.c new file mode 100644 index 0000000000..948b200a7c --- /dev/null +++ b/platform/mcu/xr871/src/driver/chip/hal_rtc.c @@ -0,0 +1,385 @@ +/** + * @file hal_rtc.c + * @author XRADIO IOT WLAN Team + */ + +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "driver/chip/hal_rtc.h" +#include "hal_base.h" + +typedef struct { + RTC_AlarmIRQCallback callback; + void *arg; +} RTC_AlarmPrivate; + +static RTC_AlarmPrivate gRtcSecAlarmPriv; +static RTC_AlarmPrivate gRtcWDayAlarmPriv; + +__STATIC_INLINE int RTC_WDayAlarmIsHHMMSSReadable(void) +{ + return !HAL_GET_BIT(RTC->CTRL, RTC_WDAY_ALARM_HHMMSS_ACCESS_BIT); +} + +static int RTC_IsDDHHMMSSReadable(void) +{ + return !HAL_GET_BIT(RTC->CTRL, RTC_DDHHMMSS_ACCESS_BIT); +} + +static int RTC_IsYYMMDDReadable(void) +{ + return !HAL_GET_BIT(RTC->CTRL, RTC_YYMMDD_ACCESS_BIT); +} + +static void RTC_SecAlarmSetAlarmTime(uint32_t sec) +{ + RTC->SEC_ALARM_LOAD_VAL = sec; +} + +__STATIC_INLINE uint32_t RTC_SecAlarmGetCurrentTime(void) +{ + return RTC->SEC_ALARM_CUR_VAL; +} + +static void RTC_SecAlarmEnableIRQ(void) +{ + HAL_SET_BIT(RTC->SEC_ALARM_IRQ_EN, RTC_SEC_ALARM_IRQ_EN_BIT); +} + +static void RTC_SecAlarmDisableIRQ(void) +{ + HAL_CLR_BIT(RTC->SEC_ALARM_IRQ_EN, RTC_SEC_ALARM_IRQ_EN_BIT); +} + +static int RTC_SecAlarmIsPendingIRQ(void) +{ + return HAL_GET_BIT(RTC->SEC_ALARM_IRQ_STATUS, RTC_SEC_ALARM_IRQ_PENDING_BIT); +} + +static void RTC_SecAlarmClearPendingIRQ(void) +{ + HAL_SET_BIT(RTC->SEC_ALARM_IRQ_STATUS, RTC_SEC_ALARM_IRQ_PENDING_BIT); +} + +static void RTC_SecAlarmStart(void) +{ + HAL_SET_BIT(RTC->SEC_ALARM_EN, RTC_SEC_ALARM_EN_BIT); +} + +static void RTC_SecAlarmStop(void) +{ + HAL_CLR_BIT(RTC->SEC_ALARM_EN, RTC_SEC_ALARM_EN_BIT); +} + +static void RTC_WDayAlarmSetAlarmTime(uint8_t hour, uint8_t minute, uint8_t second) +{ + HAL_ASSERT_PARAM(hour >= RTC_HOUR_MIN && hour <= RTC_HOUR_MAX); + HAL_ASSERT_PARAM(minute >= RTC_MINUTE_MIN && minute <= RTC_MINUTE_MAX); + HAL_ASSERT_PARAM(second >= RTC_SECOND_MIN && second <= RTC_SECOND_MAX); + + RTC->WDAY_ALARM_HHMMSS = + (((uint32_t)hour & RTC_WDAY_ALARM_HOUR_VMASK) << RTC_WDAY_ALARM_HOUR_SHIFT) | + (((uint32_t)minute & RTC_WDAY_ALARM_MINUTE_VMASK) << RTC_WDAY_ALARM_MINUTE_SHIFT) | + (((uint32_t)second & RTC_WDAY_ALARM_SECOND_VMASK) << RTC_WDAY_ALARM_SECOND_SHIFT); +} + +static uint32_t RTC_WDayAlarmSetAlarmDay(uint8_t wdayMask) +{ + return RTC->WDAY_ALARM_WDAY_EN = wdayMask & RTC_WDAY_ALARM_EN_MASK; +} + +static void RTC_WDayAlarmEnableIRQ(void) +{ + HAL_SET_BIT(RTC->WDAY_ALARM_IRQ_EN, RTC_WDAY_ALARM_IRQ_EN_BIT); +} + +static void RTC_WDayAlarmDisableIRQ(void) +{ + HAL_CLR_BIT(RTC->WDAY_ALARM_IRQ_EN, RTC_WDAY_ALARM_IRQ_EN_BIT); +} + +static int RTC_WDayAlarmIsPendingIRQ(void) +{ + return HAL_GET_BIT(RTC->WDAY_ALARM_IRQ_STATUS, RTC_WDAY_ALARM_IRQ_PENDING_BIT); +} + +static void RTC_WDayAlarmClearPendingIRQ(void) +{ + HAL_SET_BIT(RTC->WDAY_ALARM_IRQ_STATUS, RTC_WDAY_ALARM_IRQ_PENDING_BIT); +} + +void RTC_SecAlarm_IRQHandler() +{ + RTC_SecAlarmStop(); + if (RTC_SecAlarmIsPendingIRQ()) { + RTC_SecAlarmClearPendingIRQ(); + if (gRtcSecAlarmPriv.callback) { + gRtcSecAlarmPriv.callback(gRtcSecAlarmPriv.arg); + } + } +} + +void RTC_WDayAlarm_IRQHandler() +{ + if (RTC_WDayAlarmIsPendingIRQ()) { + RTC_WDayAlarmClearPendingIRQ(); + if (gRtcWDayAlarmPriv.callback) { + gRtcWDayAlarmPriv.callback(gRtcWDayAlarmPriv.arg); + } + } +} + +/** + * @brief Set the RTC date, including leaf year flag, year, month and month day + * @param[in] isLeapYear Leap year flag set to the RTC. + * @arg !0 The year is a leap year + * @arg 0 The year is not a leap year + * @param[in] year Year set to the RTC, [0, 255] + * @param[in] month Month set to the RTC, [1, 12] + * @param[in] mday Month day set to the RTC, [1, 31] + * @return None + * + * @note The leap year flag is always set by the caller, but never changed after + * setting. So the leap year bit of the RTC maybe wrong. + * @note The value of year is not a real year, but year's offset relative to + * the base year defined by the caller. + * @note The correction of the combination of all the parameters is guaranteed + * by the caller. + */ +void HAL_RTC_SetYYMMDD(uint8_t isLeapYear, uint8_t year, uint8_t month, uint8_t mday) +{ + HAL_ASSERT_PARAM(year <= RTC_YEAR_MAX); + HAL_ASSERT_PARAM(month >= RTC_MONTH_MIN && month <= RTC_MONTH_MAX); + HAL_ASSERT_PARAM(mday >= RTC_MDAY_MIN && month <= RTC_MDAY_MAX); + + RTC->YYMMDD = (isLeapYear ? RTC_LEAP_YEAR_BIT : 0) | + (((uint32_t)year & RTC_YEAR_VMASK) << RTC_YEAR_SHIFT) | + (((uint32_t)month & RTC_MONTH_VMASK) << RTC_MONTH_SHIFT) | + (((uint32_t)mday & RTC_MDAY_VMASK) << RTC_MDAY_SHIFT); +} + +/** + * @brief Set the RTC weekday and time including hour, minute and second + * @param[in] wday Weekday set to the RTC + * @param[in] hour Hour set to the RTC, [0, 23] + * @param[in] minute Minute set to the RTC, [0, 59] + * @param[in] second Second set to the RTC, [0, 59] + * @return None + * + * @note The correction of the weekday is guaranteed by the caller. + */ +void HAL_RTC_SetDDHHMMSS(RTC_WeekDay wday, uint8_t hour, uint8_t minute, uint8_t second) +{ + HAL_ASSERT_PARAM(hour >= RTC_HOUR_MIN && hour <= RTC_HOUR_MAX); + HAL_ASSERT_PARAM(minute >= RTC_MINUTE_MIN && minute <= RTC_MINUTE_MAX); + HAL_ASSERT_PARAM(second >= RTC_SECOND_MIN && second <= RTC_SECOND_MAX); + + RTC->DDHHMMSS = (((uint32_t)wday & RTC_WDAY_VMASK) << RTC_WDAY_SHIFT) | + (((uint32_t)hour & RTC_HOUR_VMASK) << RTC_HOUR_SHIFT) | + (((uint32_t)minute & RTC_MINUTE_VMASK) << RTC_MINUTE_SHIFT) | + (((uint32_t)second & RTC_SECOND_VMASK) << RTC_SECOND_SHIFT); +} + +/** + * @brief Get the RTC date, including leaf year flag, year, month and month day + * @param[out] isLeapYear The RTC's leap year flag. Don't use it because it + * maybe wrong. + * - 1 means the year is a leap year + * - 0 means the year is not a leap year + * @param[out] year The RTC's Year + * @param[out] month The RTC's Month + * @param[out] mday The RTC's Month day + * @return None + * + * @note Don't trust the RTC leap year flag, because it's never changed by the + * RTC hardware, and maybe wrong. + * @note The RTC's year is not a real year, but year offset relative to the + * base year defined by the caller. + */ +void HAL_RTC_GetYYMMDD(uint8_t *isLeapYear, uint8_t *year, uint8_t *month, uint8_t *mday) +{ + uint32_t yymmdd; + + while (!RTC_IsYYMMDDReadable()) { + HAL_MSleep(1); + } + + yymmdd = RTC->YYMMDD; + *isLeapYear = HAL_GET_BIT(yymmdd, RTC_LEAP_YEAR_BIT) ? 1 : 0; + *year = HAL_GET_BIT_VAL(yymmdd, RTC_YEAR_SHIFT, RTC_YEAR_VMASK); + *month = HAL_GET_BIT_VAL(yymmdd, RTC_MONTH_SHIFT, RTC_MONTH_VMASK); + *mday = HAL_GET_BIT_VAL(yymmdd, RTC_MDAY_SHIFT, RTC_MDAY_VMASK); +} + +/** + * @brief Get the RTC weekday and time including hour, minute and second + * @param[out] wday The RTC's Weekday + * @param[out] hour The RTC's hour + * @param[out] minute The RTC's minute + * @param[out] second The RTC's second + * @return None + */ +void HAL_RTC_GetDDHHMMSS(RTC_WeekDay *wday, uint8_t *hour, uint8_t *minute, uint8_t *second) +{ + uint32_t ddhhmmss; + + while (!RTC_IsDDHHMMSSReadable()) { + HAL_MSleep(1); + } + + ddhhmmss = RTC->DDHHMMSS; + *wday = (RTC_WeekDay)HAL_GET_BIT_VAL(ddhhmmss, RTC_WDAY_SHIFT, RTC_WDAY_VMASK); + *hour = HAL_GET_BIT_VAL(ddhhmmss, RTC_HOUR_SHIFT, RTC_HOUR_VMASK); + *minute = HAL_GET_BIT_VAL(ddhhmmss, RTC_MINUTE_SHIFT, RTC_MINUTE_VMASK); + *second = HAL_GET_BIT_VAL(ddhhmmss, RTC_SECOND_SHIFT, RTC_SECOND_VMASK); +} + +/** + * @brief Start the RTC second alarm once + * + * After starting, the RTC second alarm counts down from param->alarmSeconds + * to zero, and trigger interrupt when it reach zero. After alarming, the + * alarm stops automatically. + * + * @param[in] param Pointer to RTC_SecAlarmStartParam structure + * @return None + * + */ +void HAL_RTC_StartSecAlarm(const RTC_SecAlarmStartParam *param) +{ + /* reset the current value and wait it done */ + RTC_SecAlarmStop(); + while (RTC_SecAlarmGetCurrentTime() != 0) { + HAL_MSleep(10); + } + + RTC_SecAlarmSetAlarmTime(param->alarmSeconds); + gRtcSecAlarmPriv.callback = param->callback; + gRtcSecAlarmPriv.arg = param->arg; + if (RTC_SecAlarmIsPendingIRQ()) { + RTC_SecAlarmClearPendingIRQ(); + } + RTC_SecAlarmEnableIRQ(); + HAL_NVIC_SetPriority(RTC_SEC_ALARM_IRQn, NVIC_PERIPHERAL_PRIORITY_DEFAULT); + HAL_NVIC_EnableIRQ(RTC_SEC_ALARM_IRQn); + RTC_SecAlarmStart(); +} + +/** + * @brief Stop the RTC second alarm + * @return None + */ +void HAL_RTC_StopSecAlarm(void) +{ + RTC_SecAlarmStop(); + HAL_NVIC_DisableIRQ(RTC_SEC_ALARM_IRQn); + RTC_SecAlarmDisableIRQ(); + if (RTC_SecAlarmIsPendingIRQ()) { + RTC_SecAlarmClearPendingIRQ(); + } + gRtcSecAlarmPriv.callback = NULL; + gRtcSecAlarmPriv.arg = NULL; +} + +/** + * @brief Start the RTC weekday alarm + * + * After starting, the RTC weekday alarm will trigger interrupt when it reach + * the configured weekday time. After alarming, the alarm continues to running. + * + * @param[in] param Pointer to RTC_WDayAlarmStartParam structure + * @return None + */ +void HAL_RTC_StartWDayAlarm(const RTC_WDayAlarmStartParam *param) +{ + RTC_WDayAlarmSetAlarmDay(0); + RTC_WDayAlarmSetAlarmTime(param->alarmHour, param->alarmMinute, param->alarmSecond); + + gRtcWDayAlarmPriv.callback = param->callback; + gRtcWDayAlarmPriv.arg = param->arg; + if (RTC_WDayAlarmIsPendingIRQ()) { + RTC_WDayAlarmClearPendingIRQ(); + } + RTC_WDayAlarmEnableIRQ(); + HAL_NVIC_SetPriority(RTC_WDAY_ALARM_IRQn, NVIC_PERIPHERAL_PRIORITY_DEFAULT); + HAL_NVIC_EnableIRQ(RTC_WDAY_ALARM_IRQn); + RTC_WDayAlarmSetAlarmDay(param->alarmWDayMask); +} + +/** + * @brief Stop the RTC weekday alarm + * @return None + */ +void HAL_RTC_StopWDayAlarm(void) +{ + RTC_WDayAlarmSetAlarmDay(0); + HAL_NVIC_DisableIRQ(RTC_WDAY_ALARM_IRQn); + RTC_WDayAlarmDisableIRQ(); + if (RTC_WDayAlarmIsPendingIRQ()) { + RTC_WDayAlarmClearPendingIRQ(); + } + gRtcWDayAlarmPriv.callback = NULL; + gRtcWDayAlarmPriv.arg = NULL; +} + +/** + * @brief Get the time value (in microsecond) of the RTC's Free running counter + * + * Free running counter is a 48-bit counter which is driven by LFCLK and starts + * to count as soon as the system reset is released and the LFCLK is ready. + * + * The time unit of the counter is: + * (10^6 / LFCLK) = (10^6 / 32768) = (15625 / 512) us + * + * @return The time value (in microsecond) of the RTC's Free running counter. + * Its accuracy is about 32 us. + */ +uint64_t HAL_RTC_GetFreeRunTime(void) +{ +#define RTC_FREE_RUN_CNT_TO_US(cnt) (((cnt) * 15625) >> 9) + + uint64_t cnt; + uint32_t cntLow1, cntLow2, cntHigh; + + cntLow1 = RTC->FREERUN_CNT_L; + cntHigh = RTC->FREERUN_CNT_H; + cntLow2 = RTC->FREERUN_CNT_L; + if (cntLow2 < cntLow1) { + /* counter's low 32-bit overflow, get high 16-bit again */ + cntHigh = RTC->FREERUN_CNT_H; + cnt = ((uint64_t)cntHigh << 32) | cntLow2; + } else { + cnt = ((uint64_t)cntHigh << 32) | cntLow1; + } + + return RTC_FREE_RUN_CNT_TO_US(cnt); + +#undef RTC_FREE_RUN_CNT_TO_US +} diff --git a/platform/mcu/xr871/src/driver/chip/hal_spi.c b/platform/mcu/xr871/src/driver/chip/hal_spi.c new file mode 100644 index 0000000000..a0e2041ead --- /dev/null +++ b/platform/mcu/xr871/src/driver/chip/hal_spi.c @@ -0,0 +1,1356 @@ +/** + * @file hal_spi.c + * @author XRADIO IOT WLAN Team + */ + +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include "hal_base.h" +#include "driver/chip/hal_spi.h" +#include "pm/pm.h" + +/*************************************** Debug *****************************************/ +#define SPI_MODULE (DBG_ON | XR_LEVEL_ALERT) +#define SPI_TRANSFER_DEBUG +//#define SPI_REG_DEBUG + +#define SPI_ASSERT(condition) XR_ASSERT(condition, SPI_MODULE, #condition " failed\n") + +#define SPI_DEBUG(msg, arg...) XR_DEBUG(SPI_MODULE, NOEXPAND, "[SPI Debug] " msg, ##arg) + +#define SPI_ALERT(msg, arg...) XR_ALERT(SPI_MODULE, NOEXPAND, "[SPI Alert] " msg, ##arg) + +#define SPI_ENTRY() XR_ENTRY(SPI_MODULE, "[SPI Entry]") +#define SPI_EXIT(val) XR_RET(SPI_MODULE, "[SPI Exit]", val) + +#ifdef SPI_REG_DEBUG +#define SPI_REG_ALL(spi, msg) { \ + SPI_DEBUG(msg "\n"); \ + SPI_REG(spi->VER); \ + SPI_REG(spi->CTRL); \ + SPI_REG(spi->TCTRL); \ + SPI_REG(spi->IER); \ + SPI_REG(spi->STA); \ + SPI_REG(spi->FCTL); \ + SPI_REG(spi->FST); \ + SPI_REG(spi->WAIT); \ + SPI_REG(spi->CCTR); \ + SPI_REG(spi->BC); \ + SPI_REG(spi->TC); \ + SPI_REG(spi->BCC); \ + SPI_REG(spi->NDMA_MODE_CTRL); \ + } + +#define SPI_REG(reg) SPI_DEBUG("register " #reg ": 0x%x.\n", reg); + +#else +#define SPI_REG_ALL(spi, msg) + +#define SPI_REG(reg) + +#endif + +#ifdef SPI_TRANSFER_DEBUG +#define SPI_DEBUG_TRANSFER_DATA(pdata, size, dir) { \ + uint32_t temp_size = size; \ + uint8_t *temp_pdata = pdata; \ + XR_DEBUG(SPI_MODULE, NOEXPAND, "[SPI Debug Transfer] " dir " size = %d: ", size); \ + while (temp_size--) \ + XR_DEBUG(SPI_MODULE, NOEXPAND, "0x%x ", *(temp_pdata++)); \ + XR_DEBUG(SPI_MODULE, NOEXPAND, "\n"); \ + } +#else +#define SPI_DEBUG_TRANSFER_DATA(pdata, size) +#endif + + +#define __SPI_STATIC_INLINE__ __STATIC_INLINE + +/* + * @brief Hardware Layer Interface + */ +__SPI_STATIC_INLINE__ +uint32_t SPI_GetVersion(SPI_T *spi) +{ + return spi->VER; +} + +/* + * @brief + */ +__SPI_STATIC_INLINE__ +void SPI_Reset(SPI_T *spi) +{ + HAL_SET_BIT(spi->CTRL, SPI_CTRL_RST_MASK); +} + +/* + * @brief + */ +__SPI_STATIC_INLINE__ +void SPI_SetMode(SPI_T *spi, SPI_CTRL_Mode mode) +{ + HAL_MODIFY_REG(spi->CTRL, SPI_CTRL_MODE_MASK, mode); +} + +/* + * @brief + */ +__SPI_STATIC_INLINE__ +void SPI_Enable(SPI_T *spi) +{ + HAL_SET_BIT(spi->CTRL, SPI_CTRL_EN_MASK); +} + +__SPI_STATIC_INLINE__ +void SPI_Disable(SPI_T *spi) +{ + HAL_CLR_BIT(spi->CTRL, SPI_CTRL_EN_MASK); +} + +/* + * @brief + */ +__SPI_STATIC_INLINE__ +void SPI_StartTransmit(SPI_T *spi) +{ + HAL_SET_BIT(spi->TCTRL, SPI_TCTRL_XCH_MASK); +} + +/* + * @brief + */ +__SPI_STATIC_INLINE__ +void SPI_SetFirstTransmitBit(SPI_T *spi, SPI_TCTRL_Fbs bit) +{ + HAL_MODIFY_REG(spi->TCTRL, SPI_TCTRL_FBS_MASK, bit); +} + +/* + * @brief + */ +__SPI_STATIC_INLINE__ +void SPI_EnableRapidsMode(SPI_T *spi, bool delay_sample) +{ + HAL_SET_BIT(spi->TCTRL, SPI_TCTRL_RPSM_MASK); + HAL_MODIFY_REG(spi->TCTRL, SPI_TCTRL_SDC_MASK, delay_sample << SPI_TCTRL_SDC_SHIFT); +} + +/* + * @brief + */ +__SPI_STATIC_INLINE__ +void SPI_DisableRapidsMode(SPI_T *spi) +{ + HAL_CLR_BIT(spi->TCTRL, SPI_TCTRL_RPSM_MASK); +} + +/* + * @brief + */ +__SPI_STATIC_INLINE__ +void SPI_SetDuplex(SPI_T *spi, SPI_TCTRL_DHB_Duplex duplex) +{ + HAL_MODIFY_REG(spi->TCTRL, SPI_TCTRL_DHB_MASK, duplex); +} + +/* + * @brief + */ +__SPI_STATIC_INLINE__ +void SPI_SetCsLevel(SPI_T *spi, bool level) +{ + HAL_MODIFY_REG(spi->TCTRL, SPI_TCTRL_SS_LEVEL_MASK, level << SPI_TCTRL_SS_LEVEL_SHIFT); +} + +/* + * @brief + */ +__SPI_STATIC_INLINE__ +void SPI_ManualChipSelect(SPI_T *spi, SPI_TCTRL_SS_Sel cs) +{ + HAL_SET_BIT(spi->TCTRL, SPI_TCTRL_SS_OWNER_MASK); + HAL_MODIFY_REG(spi->TCTRL, SPI_TCTRL_SS_SEL_MASK, cs); +} + +/* + * @brief + */ +__SPI_STATIC_INLINE__ +void SPI_AutoChipSelect(SPI_T *spi, SPI_TCTRL_SS_Sel cs, bool cs_remain) +{ + HAL_MODIFY_REG(spi->TCTRL, SPI_TCTRL_SS_SEL_MASK, cs); + HAL_CLR_BIT(spi->TCTRL, SPI_TCTRL_SS_OWNER_MASK); + HAL_MODIFY_REG(spi->TCTRL, SPI_TCTRL_SS_CTL_MASK, (!cs_remain) << SPI_TCTRL_SS_CTL_SHIFT); +} + +/* + * @brief + */ +__SPI_STATIC_INLINE__ +void SPI_SetCsIdle(SPI_T *spi, bool idle) +{ + HAL_MODIFY_REG(spi->TCTRL, SPI_TCTRL_SPOL_MASK, (!!idle) << SPI_TCTRL_SPOL_SHIFT); +} + +/* + * @brief + */ +__SPI_STATIC_INLINE__ +void SPI_SetSclkMode(SPI_T *spi, SPI_SCLK_Mode mode) +{ + HAL_MODIFY_REG(spi->TCTRL, SPI_TCTRL_CPOL_MASK | SPI_TCTRL_CPHA_MASK, mode); +} + +typedef enum { + SPI_INT_CS_DESELECT = SPI_IER_SS_INT_EN_MASK, + SPI_INT_TRANSFER_COMPLETE = SPI_IER_TC_INT_EN_MASK, + SPI_INT_TXFIFO_UNDER_RUN = SPI_IER_TF_UDR_INT_EN_MASK, + SPI_INT_TXFIFO_OVERFLOW = SPI_IER_TF_OVF_INT_EN_MASK, + SPI_INT_RXFIFO_UNDER_RUN = SPI_IER_RF_UDR_INT_EN_MASK, + SPI_INT_RXFIFO_OVERFLOW = SPI_IER_RF_OVF_INT_EN_MASK, + SPI_INT_TXFIFO_FULL = SPI_IER_TF_FUL_INT_EN_MASK, + SPI_INT_TXFIFO_EMPTY = SPI_IER_TX_EMP_INT_EN_MASK, + SPI_INT_TXFIFO_READY = SPI_IER_TX_ERQ_INT_EN_MASK, + SPI_INT_RXFIFO_FULL = SPI_IER_RF_FUL_INT_EN_MASK, + SPI_INT_RXFIFO_EMPTY = SPI_IER_RX_EMP_INT_EN_MASK, + SPI_INT_RXFIFO_READY = SPI_IER_RF_RDY_INT_EN_MASK +} SPI_Int_Type; + +/* + * @brief + */ +__SPI_STATIC_INLINE__ +void SPI_EnableInt(SPI_T *spi, SPI_Int_Type type) +{ + HAL_SET_BIT(spi->IER, type); +} + +/* + * @brief + */ +__SPI_STATIC_INLINE__ +void SPI_DisableInt(SPI_T *spi, SPI_Int_Type type) +{ + HAL_CLR_BIT(spi->IER, type); +} + +/* + * @brief + */ +__SPI_STATIC_INLINE__ +bool SPI_IntState(SPI_T *spi, SPI_Int_Type type) +{ + return !!HAL_GET_BIT(spi->STA, type); +} + +/* + * @brief + */ +__SPI_STATIC_INLINE__ +bool SPI_ClearInt(SPI_T *spi, SPI_Int_Type type) +{ + HAL_SET_BIT(spi->STA, type); + return HAL_GET_BIT(spi->STA, type); +} + +/* + * @brief + */ +__SPI_STATIC_INLINE__ +void SPI_DebugReadTx(SPI_T *spi, uint32_t *data) +{ + // tbc... +} + +/* + * @brief + */ +__SPI_STATIC_INLINE__ +void SPI_DebugWriteRx(SPI_T *spi, uint32_t *data) +{ + // tbc... +} + +/* + * @brief + */ +__SPI_STATIC_INLINE__ +void SPI_ResetTxFifo(SPI_T *spi) +{ + HAL_SET_BIT(spi->FCTL, SPI_FCTL_TF_RST_MASK); + while(HAL_GET_BIT(spi->FCTL, SPI_FCTL_TF_RST_MASK) != 0); +} + +/* + * @brief + */ +__SPI_STATIC_INLINE__ +void SPI_ResetRxFifo(SPI_T *spi) +{ + HAL_SET_BIT(spi->FCTL, SPI_FCTL_RF_RST_MASK); + while(HAL_GET_BIT(spi->FCTL, SPI_FCTL_RF_RST_MASK) != 0); +} + +/* + * @brief + */ +__SPI_STATIC_INLINE__ +void SPI_DMA(SPI_T *spi, bool txEn, bool rxEn) +{ + HAL_MODIFY_REG(spi->FCTL, + SPI_FCTL_TF_DRQ_EN_MASK | SPI_FCTL_RF_DRQ_EN_MASK, + ((!!txEn) << SPI_FCTL_TF_DRQ_EN_SHIFT) | ((!!rxEn) << SPI_FCTL_RF_DRQ_EN_SHIFT)); +} + +/* + * @brief + */ +__SPI_STATIC_INLINE__ +void SPI_SetTxFifoThreshold(SPI_T *spi, uint8_t threshold) +{ + HAL_MODIFY_REG(spi->FCTL, SPI_FCTL_TX_TRIG_LEVEL_MASK, threshold << SPI_FCTL_TX_TRIG_LEVEL_SHIFT); +} + +/* + * @brief + */ +__SPI_STATIC_INLINE__ +void SPI_SetRxFifoThreshold(SPI_T *spi, uint8_t threshold) +{ + HAL_MODIFY_REG(spi->FCTL, SPI_FCTL_RX_TRIG_LEVEL_MASK, threshold << SPI_FCTL_RX_TRIG_LEVEL_SHIFT); +} + +/* + * @brief + */ +__SPI_STATIC_INLINE__ +uint8_t SPI_GetTxFifoCounter(SPI_T *spi) +{ + return (uint8_t)((spi->FST & SPI_FST_TF_CNT_MASK) >> SPI_FST_TF_CNT_SHIFT); +} + +/* + * @brief + */ +__SPI_STATIC_INLINE__ +uint8_t SPI_GetRxFifoCounter(SPI_T *spi) +{ + return (uint8_t)((spi->FST & SPI_FST_RF_CNT_MASK) >> SPI_FST_RF_CNT_SHIFT); +} + +/* + * @brief + */ +__SPI_STATIC_INLINE__ +void SPI_EnableDualMode(SPI_T *spi) +{ + HAL_SET_BIT(spi->BCC, SPI_BCC_DRM_MASK); +} + +/* + * @brief + */ +__SPI_STATIC_INLINE__ +void SPI_DisableDualMode(SPI_T *spi) +{ + HAL_CLR_BIT(spi->BCC, SPI_BCC_DRM_MASK); +} + +/* + * @brief + */ +__SPI_STATIC_INLINE__ +void SPI_SetInterval(SPI_T *spi, uint16_t nSCLK) +{ + HAL_MODIFY_REG(spi->WAIT, SPI_WAIT_WCC_MASK, nSCLK << SPI_WAIT_WCC_SHIFT); +} + +/* + * @brief + */ +static void SPI_SetClkDiv(SPI_T *spi, uint16_t div) +{ + uint8_t n = 0; + if (div < 1) { + SPI_DEBUG("spi div < 1\n"); + return; + } + + if (div > 2 * (0xFF + 1)) { + HAL_CLR_BIT(spi->CCTR, SPI_CCTR_DRS_MASK); + do { + div = (div == 1) ? 0 : ((div + 1) / 2); + n++; + } while (div); + + SPI_DEBUG("SPI CLK Div into 2^%d\n", n); + HAL_MODIFY_REG(spi->CCTR, SPI_CCTR_CDR1_MASK, (n & 0x0F) << SPI_CCTR_CDR1_SHIFT); + } else { + HAL_SET_BIT(spi->CCTR, SPI_CCTR_DRS_MASK); + n = ((div + 1)/ 2) - 1; + HAL_MODIFY_REG(spi->CCTR, SPI_CCTR_CDR2_MASK, (n & 0xFF) << SPI_CCTR_CDR2_SHIFT); + } +} + +/* + * @brief + */ +__SPI_STATIC_INLINE__ +void SPI_SetDataSize(SPI_T *spi, uint32_t data_size, uint32_t dummy_size) +{ + HAL_MODIFY_REG(spi->BC, SPI_BC_MBC_MASK, data_size + dummy_size); + HAL_MODIFY_REG(spi->TC, SPI_TC_MWTC_MASK, data_size); + HAL_MODIFY_REG(spi->BCC, SPI_BCC_STC_MASK, data_size); +} + +/* + * @brief + */ +__SPI_STATIC_INLINE__ +void SPI_Write(SPI_T *spi, uint8_t *data) +{ + HAL_REG_8BIT(&spi->TXD) = *data; +} + +/* + * @brief + */ +__SPI_STATIC_INLINE__ +void SPI_Read(SPI_T *spi, uint8_t *data) +{ + *data = HAL_REG_8BIT(&spi->RXD); +} + +/* + * @brief + */ +__SPI_STATIC_INLINE__ +uint8_t *SPI_TxAddress(SPI_T *spi) +{ + return (uint8_t *)&spi->TXD; +} + +/* + * @brief + */ +__SPI_STATIC_INLINE__ +uint8_t *SPI_RxAddress(SPI_T *spi) +{ + return (uint8_t *)&spi->RXD; +} + + +/************************ private **************************************/ + +typedef struct { + uint8_t *ptr; /* Pointer to SPI transfer Buffer */ + uint32_t size; /* SPI transfer size */ + uint32_t count; /* SPI Transfer Counter */ +} SPI_TransferBuffer; + +#define SPI_TRANSFERBUFFER_DEFAULT \ + { \ + .ptr = NULL, \ + .size = 0, \ + .count = 0\ + } + + +/** + * @brief HAL SPI State structure definition + */ +typedef enum +{ + SPI_STATUS_RESET = 0x00, /*!< SPI not yet initialized or disabled */ + SPI_STATUS_READY = 0x01, /*!< SPI initialized and ready for use */ + SPI_STATUS_BUSY = 0x02, /*!< SPI is occupied */ + SPI_STATUS_BUSY_TX = 0x12, /*!< Data Transmission process is ongoing */ + SPI_STATUS_BUSY_RX = 0x22, /*!< Data Reception process is ongoing */ + SPI_STATUS_BUSY_TX_RX = 0x32, /*!< Data Transmission and Reception process is ongoing */ + SPI_STATUS_ERROR = 0x03 /*!< SPI error state */ +} SPI_Status; + +typedef struct { + SPI_Status status; + HAL_Mutex lock; +} SPI_StateMachine; + +typedef struct { + SPI_T *spi; + SPI_Config config; + SPI_StateMachine sm; + bool dmaSupport; + DMA_Channel tx_dmaChannel; + DMA_Channel rx_dmaChannel; + SPI_TransferBuffer tx; // can be optimized + SPI_TransferBuffer rx; // can be optimized + uint32_t mclk; + HAL_Semaphore block; + SPI_IO_Mode ioMode; + bool cs_idle; + SPI_CS cs_using; +} SPI_Handler; + + + +static SPI_Handler spi_handler[SPI_NUM] = { + { + .spi = (SPI_T *)SPI0_BASE, + .tx_dmaChannel = DMA_CHANNEL_INVALID, + .rx_dmaChannel = DMA_CHANNEL_INVALID, + .dmaSupport = 0, + .mclk = 0, + .rx = {0}, //SPI_TRANSFERBUFFER_DEFAULT, + .tx = {0}, //SPI_TRANSFERBUFFER_DEFAULT, + .sm = {0}, + .config = SPI_CONFIG_DEFAULT, + .block = {0}, + .ioMode = SPI_IO_MODE_NORMAL, + .cs_idle = 1, + .cs_using = SPI_TCTRL_SS_SEL_SS0 + }, + { + .spi = (SPI_T *)SPI1_BASE, + .tx_dmaChannel = DMA_CHANNEL_INVALID, + .rx_dmaChannel = DMA_CHANNEL_INVALID, + .dmaSupport = 0, + .mclk = 0, + .rx = {0}, //SPI_TRANSFERBUFFER_DEFAULT, + .tx = {0}, //SPI_TRANSFERBUFFER_DEFAULT, + .sm = {0}, + .config = SPI_CONFIG_DEFAULT, + .block = {0}, + .ioMode = SPI_IO_MODE_NORMAL, + .cs_idle = 1, + .cs_using = SPI_TCTRL_SS_SEL_SS0 + } +}; + +/* + * @brief + */ +__SPI_STATIC_INLINE__ +SPI_Handler *HAL_SPI_GetInstance(SPI_Port port) +{ + return &spi_handler[port]; +} + + +/* + * @brief + */ +static void HAL_SPI_EnableCCMU(SPI_Port port) +{ + if (port == SPI0) { + HAL_CCM_BusEnablePeriphClock(CCM_BUS_PERIPH_BIT_SPI0); + HAL_CCM_SPI0_EnableMClock(); + } else if (port == SPI1) { + HAL_CCM_BusEnablePeriphClock(CCM_BUS_PERIPH_BIT_SPI1); + HAL_CCM_SPI1_EnableMClock(); + } +} + + +/* + * @brief + */ +static void HAL_SPI_DisableCCMU(SPI_Port port) +{ + if (port == SPI0) { + HAL_CCM_BusDisablePeriphClock(CCM_BUS_PERIPH_BIT_SPI0); + HAL_CCM_SPI0_DisableMClock(); + } else if (port == SPI1) { + HAL_CCM_BusDisablePeriphClock(CCM_BUS_PERIPH_BIT_SPI1); + HAL_CCM_SPI1_DisableMClock(); + } +} + +static inline void HAL_SPI_ResetCCMU(SPI_Port port) +{ + CCM_BusPeriphBit ccm_spi = CCM_BUS_PERIPH_BIT_SPI0 + port; /* spi_port translate to ccm_bit */ + HAL_CCM_BusForcePeriphReset(ccm_spi); + HAL_CCM_BusReleasePeriphReset(ccm_spi); +} + + +/* + * @brief + */ +static bool HAL_SPI_ConfigCCMU(SPI_Port port, uint32_t clk) +{ + CCM_AHBPeriphClkSrc src; + uint32_t mclk; + uint32_t div; + + if (clk > HAL_GetHFClock()) + { + mclk = HAL_PRCM_GetDevClock(); + src = CCM_AHB_PERIPH_CLK_SRC_DEVCLK; + SPI_DEBUG("CCMU src = CCM_AHB_PERIPH_CLK_SRC_DEVCLK.\n"); + } + else + { + mclk = HAL_GetHFClock(); + src = CCM_AHB_PERIPH_CLK_SRC_HFCLK; + SPI_DEBUG("CCMU src = CCM_AHB_PERIPH_CLK_SRC_HFCLK.\n"); + } + + div = (mclk + clk - 1) / clk; + div = div==0 ? 1 : div; + + SPI_DEBUG("CCMU div = %d\n", div); + if (div > (16 * 8)) + return 0; + + if (port == SPI0) + { + if (div > 64) + HAL_CCM_SPI0_SetMClock(src, CCM_PERIPH_CLK_DIV_N_8, (CCM_PeriphClkDivM)((div >> 3) - 1)); + else if (div > 32) + HAL_CCM_SPI0_SetMClock(src, CCM_PERIPH_CLK_DIV_N_4, (CCM_PeriphClkDivM)((div >> 2) - 1)); + else if (div > 16) + HAL_CCM_SPI0_SetMClock(src, CCM_PERIPH_CLK_DIV_N_2, (CCM_PeriphClkDivM)((div >> 1) - 1)); + else + HAL_CCM_SPI0_SetMClock(src, CCM_PERIPH_CLK_DIV_N_1, (CCM_PeriphClkDivM)((div >> 0) - 1)); + } + else if (port == SPI1) + { + if (div > 64) + HAL_CCM_SPI1_SetMClock(src, CCM_PERIPH_CLK_DIV_N_8, (CCM_PeriphClkDivM)((div >> 3) - 1)); + else if (div > 32) + HAL_CCM_SPI1_SetMClock(src, CCM_PERIPH_CLK_DIV_N_4, (CCM_PeriphClkDivM)((div >> 2) - 1)); + else if (div > 16) + HAL_CCM_SPI1_SetMClock(src, CCM_PERIPH_CLK_DIV_N_2, (CCM_PeriphClkDivM)((div >> 1) - 1)); + else + HAL_CCM_SPI1_SetMClock(src, CCM_PERIPH_CLK_DIV_N_1, (CCM_PeriphClkDivM)((div >> 0) - 1)); + } + + return 1; +} + + +/* + * @brief + */ +static void HAL_SPI_TxDMAIntFunc(void *arg) +{ + SPI_Handler *hdl = (SPI_Handler *)arg; + +// SPI_DEBUG("tx DMA Interrupt for SPI.\n"); + + if (hdl->sm.status != SPI_STATUS_BUSY_TX_RX) { + SPI_DMA(hdl->spi, 0, 0); + HAL_SemaphoreRelease(&hdl->block); + } +} + +static void HAL_SPI_RxDMAIntFunc(void *arg) +{ + SPI_Handler *hdl = (SPI_Handler *)arg; + +// SPI_DEBUG("rx DMA Interrupt for SPI.\n"); + SPI_DMA(hdl->spi, 0, 0); + HAL_SemaphoreRelease(&hdl->block); +} + +int pm_resume[SPI_NUM] = {0}; +#ifdef CONFIG_PM +static int spi_suspend(struct soc_device *dev, enum suspend_state_t state) +{ + SPI_Handler *hdl = HAL_SPI_GetInstance((SPI_Port)dev->platform_data); + if (hdl->sm.status != SPI_STATUS_READY) + return -1; + return 0; +} + +static int spi_resume(struct soc_device *dev, enum suspend_state_t state) +{ + SPI_Port port = (SPI_Port)dev->platform_data; + SPI_Handler *hdl = HAL_SPI_GetInstance((SPI_Port)dev->platform_data); + SPI_T *spi = hdl->spi; + + switch (state) { + case PM_MODE_SLEEP: + break; + case PM_MODE_STANDBY: + case PM_MODE_HIBERNATION: + pm_resume[port] = 1; + // CCMU config + HAL_SPI_DisableCCMU(port); + HAL_SPI_ResetCCMU(port); + HAL_SPI_ConfigCCMU(port, hdl->mclk); + HAL_SPI_EnableCCMU(port); + + SPI_Disable(spi); + SPI_Reset(spi); + SPI_ResetRxFifo(spi); + SPI_ResetTxFifo(spi); + + HAL_SPI_DisableCCMU(port); + break; + default: + break; + } + + return 0; +} + +static struct soc_device_driver spi_drv = { + .name = "spi", + .suspend_noirq = spi_suspend, + .resume_noirq = spi_resume, +}; + +static struct soc_device spi_dev[SPI_NUM] = { + {.name = "spi0", .driver = &spi_drv, .platform_data = (void *)SPI0,}, + {.name = "spi1", .driver = &spi_drv, .platform_data = (void *)SPI1,}, +}; + +#define SPI_DEV(id) (&spi_dev[id]) + +#endif + + +/************************ public **************************************/ + +/** + * @brief Initialize SPI driver. + * @note Each SPI port can master sevral devices, HAL_SPI_Init initialize the + * SPI port for its own devices. Mclk configed here must be the max freq + * of all devices under this SPI port. + * @param port: spi port + * @param gconfig: + * @arg gconfig->mclk: SPI controller frequency. It must be the the max + * freq of all devices under this SPI port. + * @arg gconfig->cs_level: chip selected level of SPI devices enabled, + * cs level should be same amound all devices. + * @retval HAL_Status: The status of driver + */ +HAL_Status HAL_SPI_Init(SPI_Port port, const SPI_Global_Config *gconfig) +{ + SPI_Handler *hdl = HAL_SPI_GetInstance(port); + SPI_T *spi = hdl->spi; + HAL_Status ret = HAL_OK; + + SPI_ENTRY(); + SPI_ASSERT(port > SPI_NUM); + + if (hdl->sm.status != SPI_STATUS_RESET) { + ret = HAL_ERROR; + SPI_ALERT("Changing State incorrectly in %s\n", __func__); + goto out; + } + + if ((ret = HAL_MutexInit(&hdl->sm.lock)) != HAL_OK) { + SPI_ALERT("Mutex init failed\n"); + goto out; + } + + if ((ret = HAL_SemaphoreInit(&hdl->block, 0, 1)) != HAL_OK) { + HAL_MutexDeinit(&hdl->sm.lock); + SPI_ALERT("Semaphore init failed\n"); + goto out; + } + + HAL_Memset(&hdl->tx, 1, sizeof(SPI_TransferBuffer)); + HAL_Memset(&hdl->rx, 1, sizeof(SPI_TransferBuffer)); +// hdl->mclk = SPI_SOURCE_CLK; + hdl->mclk = gconfig->mclk * 1; /* a larger mclk could be divided easier by + multi-device-frequency, 2 is not necessary */ + + hdl->cs_idle = !gconfig->cs_level; + SPI_SetCsIdle(spi, hdl->cs_idle); + + // CCMU config + HAL_SPI_DisableCCMU(port); + HAL_SPI_ResetCCMU(port); + if (HAL_SPI_ConfigCCMU(port, hdl->mclk) != 1) { + SPI_ALERT("mclk by CCMU divided too low, config failed.\n"); + ret = HAL_ERROR; + goto out; + } + HAL_SPI_EnableCCMU(port); + + SPI_Disable(spi); + SPI_Reset(spi); + SPI_ResetRxFifo(spi); + SPI_ResetTxFifo(spi); + + // default SPI Register Config + SPI_SetDuplex(spi, SPI_TCTRL_DHB_HALF_DUPLEX); + SPI_SetMode(spi, hdl->config.mode); + SPI_SetFirstTransmitBit(spi, hdl->config.firstBit); + SPI_SetSclkMode(spi, hdl->config.sclkMode); + SPI_SetClkDiv(spi, (hdl->mclk + hdl->config.sclk - 1) / hdl->config.sclk); + + hdl->sm.status = SPI_STATUS_READY; + + SPI_REG_ALL(hdl->spi, "inited"); + HAL_SPI_DisableCCMU(port); + +#ifdef CONFIG_PM + pm_register_ops(SPI_DEV(port)); +#endif + +out: + SPI_EXIT(ret); + return ret; +} + +/** + * @brief Deinitialize SPI driver. + * @param port: spi port + * @retval HAL_Status: The status of driver + */ +HAL_Status HAL_SPI_Deinit(SPI_Port port) +{ + SPI_Handler *hdl = HAL_SPI_GetInstance(port); + HAL_Status ret = HAL_OK; + + SPI_ENTRY(); + + if (hdl->sm.status != SPI_STATUS_READY) { + ret = HAL_ERROR; + SPI_ALERT("Changing State incorrectly in %s\n", __func__); + goto out; + } + +#ifdef CONFIG_PM + pm_unregister_ops(SPI_DEV(port)); +#endif + + HAL_MutexDeinit(&hdl->sm.lock); + HAL_SemaphoreDeinit(&hdl->block); + SPI_Disable(hdl->spi); + HAL_SPI_DisableCCMU(port); + hdl->sm.status = SPI_STATUS_RESET; + +out: + SPI_EXIT(ret); + return ret; +} + +/** + * @brief Open a SPI device before using this spi device. + * @note Other SPI devices will block when open a spi device, please make sure + * this device will be close eventually. + * @param port: spi port + * @param cs: spi cs pin. SPI_TCTRL_SS_SEL_SS0~3 are pin cs0~cs3. + * @param config: + * @arg config->mode: spi in master mode or slave mode. slave is not + * supported for now. + * @arg config->opMode: use dma to move data or CPU to move data. + * @arg config->firstBit: data on line is MSB or LSB. + * @arg config->sclk: spi device working frequency. + * @arg config->sclkMode: SPI sclk mode. + * @param msec: timeout in millisecond + * @retval HAL_Status: The status of driver + */ +HAL_Status HAL_SPI_Open(SPI_Port port, SPI_CS cs, SPI_Config *config, uint32_t msec) +{ + SPI_Handler *hdl = HAL_SPI_GetInstance(port); + SPI_T *spi = hdl->spi; + HAL_Status ret = HAL_OK; + unsigned long flags; + + SPI_ENTRY(); + flags = HAL_EnterCriticalSection(); + + if ((ret = HAL_MutexLock(&hdl->sm.lock, msec)) != HAL_OK) + goto out; + if (hdl->sm.status != SPI_STATUS_READY) { + ret = HAL_ERROR; + SPI_ALERT("Changing State incorrectly in %s, state: %d -> busy\n", __func__, hdl->sm.status); + goto failed; + } + hdl->sm.status = SPI_STATUS_BUSY; + + hdl->cs_using = cs; + HAL_BoardIoctl(HAL_BIR_PINMUX_INIT, HAL_MKDEV(HAL_DEV_MAJOR_SPI, port), cs); + + HAL_SPI_EnableCCMU(port); + + // DMA config + if (config->opMode == SPI_OPERATION_MODE_DMA) { + DMA_ChannelInitParam tx_param; + DMA_ChannelInitParam rx_param; + HAL_Memset(&tx_param, 0, sizeof(tx_param)); + HAL_Memset(&rx_param, 0, sizeof(rx_param)); + + if ((hdl->tx_dmaChannel = HAL_DMA_Request()) == DMA_CHANNEL_INVALID) { + SPI_ALERT("DMA request failed \n"); + goto init_failed; + } + if ((hdl->rx_dmaChannel = HAL_DMA_Request()) == DMA_CHANNEL_INVALID) { + SPI_ALERT("DMA request failed \n"); + HAL_DMA_Release(hdl->tx_dmaChannel); + goto init_failed; + } + + tx_param.cfg = HAL_DMA_MakeChannelInitCfg(DMA_WORK_MODE_SINGLE, + DMA_WAIT_CYCLE_2, + DMA_BYTE_CNT_MODE_REMAIN, + DMA_DATA_WIDTH_8BIT, + DMA_BURST_LEN_1, + DMA_ADDR_MODE_FIXED, + (DMA_Periph)(DMA_PERIPH_SPI0 + port), + DMA_DATA_WIDTH_8BIT, + DMA_BURST_LEN_1, + DMA_ADDR_MODE_INC, + DMA_PERIPH_SRAM); + tx_param.irqType = DMA_IRQ_TYPE_END; + tx_param.endCallback = HAL_SPI_TxDMAIntFunc; + tx_param.endArg = hdl; + + rx_param.cfg = HAL_DMA_MakeChannelInitCfg(DMA_WORK_MODE_SINGLE, + DMA_WAIT_CYCLE_2, + DMA_BYTE_CNT_MODE_REMAIN, + DMA_DATA_WIDTH_8BIT, + DMA_BURST_LEN_1, + DMA_ADDR_MODE_INC, + DMA_PERIPH_SRAM, + DMA_DATA_WIDTH_8BIT, + DMA_BURST_LEN_1, + DMA_ADDR_MODE_FIXED, + (DMA_Periph)(DMA_PERIPH_SPI0 + port)); + rx_param.irqType = DMA_IRQ_TYPE_END; + rx_param.endCallback = HAL_SPI_RxDMAIntFunc; + rx_param.endArg = hdl; + + HAL_DMA_Init(hdl->rx_dmaChannel, &rx_param); + HAL_DMA_Init(hdl->tx_dmaChannel, &tx_param); + } + + SPI_Disable(spi); + + if (HAL_Memcmp(config, &hdl->config, sizeof(*config)) || pm_resume[port]) { // ________________maybe unuseful_______________ // + HAL_Memcpy(&hdl->config, config, sizeof(*config)); + SPI_SetMode(spi, config->mode); + SPI_SetFirstTransmitBit(spi, config->firstBit); + SPI_SetSclkMode(spi, config->sclkMode); + SPI_SetClkDiv(spi, (hdl->mclk + config->sclk - 1) / config->sclk); + SPI_SetDuplex(spi, SPI_TCTRL_DHB_HALF_DUPLEX); + pm_resume[port] = 0; + } + +/* if (config->csMode == SPI_CS_MODE_AUTO) + SPI_AutoChipSelect(spi, cs, 1, hdl->cs_idle); + else { + SPI_ManualChipSelect(spi, cs, hdl->cs_idle); + SPI_SetCsLevel(spi, hdl->cs_idle); + }*/ + SPI_ManualChipSelect(spi, cs); + SPI_SetDataSize(spi, 0, 0); + SPI_Enable(spi); + SPI_REG_ALL(spi, "opened"); + + HAL_ExitCriticalSection(flags); + SPI_EXIT(ret); + return HAL_OK; + +init_failed: + HAL_BoardIoctl(HAL_BIR_PINMUX_DEINIT, HAL_MKDEV(HAL_DEV_MAJOR_SPI, port), cs); +failed: + HAL_MutexUnlock(&hdl->sm.lock); +out: + HAL_ExitCriticalSection(flags); + SPI_EXIT(ret); + return ret; +} + +/** + * @brief Close a SPI device to release SPI port. + * @param port: spi port + * @retval HAL_Status: The status of driver + */ +HAL_Status HAL_SPI_Close(SPI_Port port) +{ + SPI_Handler *hdl = HAL_SPI_GetInstance(port); + HAL_Status ret = HAL_OK; + unsigned long flags; + + SPI_ENTRY(); + flags = HAL_EnterCriticalSection(); + + + if (hdl->sm.status != SPI_STATUS_BUSY) { + ret = HAL_ERROR; + SPI_ALERT("Changing State incorrectly in %s, state: %d -> ready\n", __func__, hdl->sm.status); + goto out; + } + hdl->sm.status = SPI_STATUS_READY; + + if (hdl->config.opMode == SPI_OPERATION_MODE_DMA) { + HAL_DMA_Stop(hdl->tx_dmaChannel); + HAL_DMA_Stop(hdl->rx_dmaChannel); + HAL_DMA_DeInit(hdl->tx_dmaChannel); + HAL_DMA_DeInit(hdl->rx_dmaChannel); + HAL_DMA_Release(hdl->tx_dmaChannel); + HAL_DMA_Release(hdl->rx_dmaChannel); + } + + HAL_BoardIoctl(HAL_BIR_PINMUX_DEINIT, HAL_MKDEV(HAL_DEV_MAJOR_SPI, port), hdl->cs_using); + SPI_Disable(hdl->spi); + HAL_SPI_DisableCCMU(port); + HAL_SPI_Config(port, SPI_ATTRIBUTION_IO_MODE, SPI_IO_MODE_NORMAL); + HAL_MutexUnlock(&hdl->sm.lock); +out: + HAL_ExitCriticalSection(flags); + SPI_EXIT(ret); + return ret; + +} + +/** + * @brief Enable SPI device or not by chip select pin + * @note Example: device is select by low voltage level, HAL_SPI_CS(spi_port, 1) + * output cs pin to low voltage level. + * @param port: spi port. + * @param select: select/enable device or not. + * @retval HAL_Status: The status of driver + */ +HAL_Status HAL_SPI_CS(SPI_Port port, bool select) +{ + SPI_Handler *hdl = HAL_SPI_GetInstance(port); + SPI_ENTRY(); + + SPI_SetCsLevel(hdl->spi, select ^ hdl->cs_idle); + + SPI_EXIT(HAL_OK); + return HAL_OK; +} + +/** + * @brief Config SPI attribution, such as dual rx mode. + * @note This is a ioctl. + * attr : arg + * SPI_ATTRIBUTION_IO_MODE : SPI_IO_MODE_NORMAL + * : SPI_IO_MODE_DUAL_RX + * other is not support for now. + * @param port: spi port + * @param attr: + * @arg SPI_ATTRIBUTION_IO_MODE: config SPI to a IO mode, for example: + * flash has a dual output fast read mode may use this. And the + * arg can be SPI_IO_MODE_NORMAL or SPI_IO_MODE_DUAL_RX, others + * are not support for now. + * @param arg: an arguement according attribution. + * @retval HAL_Status: The status of driver + */ +HAL_Status HAL_SPI_Config(SPI_Port port, SPI_Attribution attr, uint32_t arg) +{ + SPI_Handler *hdl = HAL_SPI_GetInstance(port); + HAL_Status ret = HAL_OK; + SPI_ENTRY(); + + if (attr == SPI_ATTRIBUTION_IO_MODE) { + if (arg == SPI_IO_MODE_NORMAL) + hdl->ioMode = SPI_IO_MODE_NORMAL; + else if (arg == SPI_IO_MODE_DUAL_RX) + hdl->ioMode = SPI_IO_MODE_DUAL_RX; + else { + ret = HAL_ERROR; + goto out; + } + } + +out: + SPI_EXIT(ret); + return ret; +} + +/** + * @brief Receive data from SPI device. + * @param port: spi port + * @param data: the buf to store received data, created by user. + * @param size: the data size needed to receive. + * @retval HAL_Status: The status of driver + */ +HAL_Status HAL_SPI_Receive(SPI_Port port, uint8_t *data, uint32_t size) +{ + SPI_Handler *hdl = HAL_SPI_GetInstance(port); + SPI_TransferBuffer *rx = &hdl->rx; + SPI_T *spi = hdl->spi; + HAL_Status ret = HAL_OK; + unsigned long flags; + + SPI_ENTRY(); + flags = HAL_EnterCriticalSection(); + + if (hdl->sm.status != SPI_STATUS_BUSY) { + ret = HAL_INVALID; + SPI_ALERT("Changing State incorrectly in %s, state: %d -> rx\n", __func__, hdl->sm.status); + HAL_ExitCriticalSection(flags); + goto out; + } + hdl->sm.status = SPI_STATUS_BUSY_RX; + HAL_ExitCriticalSection(flags); + + if (hdl->ioMode == SPI_IO_MODE_DUAL_RX) + SPI_EnableDualMode(spi); + + // check spi enable? + + rx->ptr = data; + rx->count = size; + rx->size = size; + SPI_SetDataSize(spi, 0, rx->size); + + SPI_SetDuplex(hdl->spi, SPI_TCTRL_DHB_HALF_DUPLEX); + SPI_ResetRxFifo(spi); + + SPI_REG_ALL(spi, "prepare to receive"); + + if (hdl->config.opMode == SPI_OPERATION_MODE_DMA) + { + HAL_DMA_Start(hdl->rx_dmaChannel, (uint32_t)SPI_RxAddress(hdl->spi), (uint32_t)rx->ptr, rx->count); + + SPI_DMA(spi, 0, 1); + SPI_StartTransmit(spi); + + SPI_REG_ALL(spi, "spi receive dma start"); + HAL_SemaphoreWait(&hdl->block, SPI_MAX_WAIT_MS); //--------------------return error + HAL_DMA_Stop(hdl->rx_dmaChannel); + while (SPI_IntState(hdl->spi, SPI_INT_TRANSFER_COMPLETE) == 0); + SPI_ClearInt(spi, SPI_INT_TRANSFER_COMPLETE); + } + else if (hdl->config.opMode == SPI_OPERATION_MODE_POLL) + { + SPI_StartTransmit(spi); + SPI_REG_ALL(spi, "receive start"); + + while (rx->count > 0) + { + uint32_t len; + SPI_REG_ALL(spi, "receiving"); + +/* while (SPI_IntState(spi, SPI_INT_RXFIFO_EMPTY) != 0); + rx->count--; + SPI_Read(spi, rx->ptr); + rx->ptr++;*/ + len = SPI_GetRxFifoCounter(spi); + while (len--) { + rx->count--; + SPI_Read(spi, rx->ptr); + rx->ptr++; + } + } + + while (SPI_IntState(hdl->spi, SPI_INT_TRANSFER_COMPLETE) == 0); + SPI_ClearInt(spi, SPI_INT_TRANSFER_COMPLETE); + } + + if (hdl->ioMode == SPI_IO_MODE_DUAL_RX) + SPI_DisableDualMode(spi); + + SPI_REG_ALL(spi, "receive completed"); + SPI_DEBUG_TRANSFER_DATA(data, size, "Rx"); + +out: + hdl->sm.status = SPI_STATUS_BUSY; + SPI_EXIT(ret); + return ret; +} + + +/** + * @brief Transmit data to SPI device. + * @param port: spi port + * @param data: the data transmitted to SPI device. + * @param size: the data size needed to transmit. + * @retval HAL_Status: The status of driver + */ +HAL_Status HAL_SPI_Transmit(SPI_Port port, uint8_t *data, uint32_t size) /* timeout is not that timeout */ +{ + SPI_Handler *hdl = HAL_SPI_GetInstance(port); + SPI_TransferBuffer *tx = &hdl->tx; + SPI_T *spi = hdl->spi; + HAL_Status ret = HAL_OK; + unsigned long flags; + + flags = HAL_EnterCriticalSection(); + if (hdl->sm.status != SPI_STATUS_BUSY) { + ret = HAL_INVALID; + SPI_ALERT("Changing State incorrectly in %s, state: %d -> tx\n", __func__, hdl->sm.status); + HAL_ExitCriticalSection(flags); + goto out; + } + hdl->sm.status = SPI_STATUS_BUSY_TX; + HAL_ExitCriticalSection(flags); + + // check spi enable? + + SPI_ResetTxFifo(spi); + SPI_SetDuplex(hdl->spi, SPI_TCTRL_DHB_HALF_DUPLEX); + + tx->ptr = data; + tx->count = size; + tx->size = size; + SPI_SetDataSize(spi, tx->size, 0); + + SPI_REG_ALL(spi, "prepare transmit"); + + if (hdl->config.opMode == SPI_OPERATION_MODE_DMA) + { + HAL_DMA_Start(hdl->tx_dmaChannel, (uint32_t)tx->ptr, (uint32_t)SPI_TxAddress(hdl->spi), tx->count); + + SPI_DMA(spi, 1, 0); + SPI_StartTransmit(spi); + + HAL_SemaphoreWait(&hdl->block, SPI_MAX_WAIT_MS); + SPI_REG_ALL(spi, "writing by DMA finish"); + while (SPI_IntState(hdl->spi, SPI_INT_TRANSFER_COMPLETE) == 0); + SPI_ClearInt(spi, SPI_INT_TRANSFER_COMPLETE); + HAL_DMA_Stop(hdl->tx_dmaChannel); + //ret = HAL_DMA_GetByteCount(hdl->tx_dmaChannel); + } + else if (hdl->config.opMode == SPI_OPERATION_MODE_POLL) + { + SPI_StartTransmit(spi); + SPI_REG_ALL(spi, "transmit start, TCTRL 31 bit should be 1"); + while (tx->count > 0) + { +// while (SPI_IntState(spi, SPI_INT_TXFIFO_FULL) != 0); + while (SPI_GetTxFifoCounter(spi) == SPI_FIFO_SIZE); + tx->count--; + SPI_Write(spi, tx->ptr); + tx->ptr++; + + SPI_REG_ALL(spi, "writing"); + } + + while (SPI_IntState(hdl->spi, SPI_INT_TRANSFER_COMPLETE) == 0); + SPI_ClearInt(spi, SPI_INT_TRANSFER_COMPLETE); + } + + SPI_REG_ALL(spi, "transmit completed"); + SPI_DEBUG_TRANSFER_DATA(data, size, "Tx"); + +out: + hdl->sm.status = SPI_STATUS_BUSY; + SPI_EXIT(ret); + return ret; +} + +/** + * @brief Transmit and Receive data from SPI device in the same time. + * @note Transmit data by MOSI pin, and the exactly same time, receive the + * data by MISO pin. + * @param port: spi port + * @param tx_data: the data transmitted to SPI device. + * @param rx_data: the buf to store received data, created by user. + * @param size: the data size needed to transmit and receive. + * @retval HAL_Status: The status of driver + */ +HAL_Status HAL_SPI_TransmitReceive(SPI_Port port, uint8_t *tx_data, uint8_t *rx_data, uint32_t size) +{ + SPI_Handler *hdl = HAL_SPI_GetInstance(port); + SPI_TransferBuffer *tx = &hdl->tx; + SPI_TransferBuffer *rx = &hdl->rx; + SPI_T *spi = hdl->spi; + HAL_Status ret = HAL_OK; + unsigned long flags; + + flags = HAL_EnterCriticalSection(); + if (hdl->sm.status != SPI_STATUS_BUSY) { + ret = HAL_INVALID; + SPI_ALERT("Changing State incorrectly in %s, state: %d -> rx\n", __func__, hdl->sm.status); + HAL_ExitCriticalSection(flags); + goto out; + } + hdl->sm.status = SPI_STATUS_BUSY_TX_RX; + HAL_ExitCriticalSection(flags); + + // check spi enable? + + tx->ptr = tx_data; + tx->count = size; + tx->size = size; + rx->ptr = rx_data; + rx->count = size; + rx->size = size; + SPI_SetDataSize(spi, size, 0); + + SPI_SetDuplex(hdl->spi, SPI_TCTRL_DHB_FULL_DUPLEX); + SPI_ResetTxFifo(spi); + SPI_ResetRxFifo(spi); + + if (hdl->config.opMode == SPI_OPERATION_MODE_DMA) + { + HAL_DMA_Start(hdl->tx_dmaChannel, (uint32_t)tx->ptr, (uint32_t)SPI_TxAddress(hdl->spi), tx->count); + HAL_DMA_Start(hdl->rx_dmaChannel, (uint32_t)SPI_RxAddress(hdl->spi), (uint32_t)rx->ptr, rx->count); + + SPI_DMA(spi, 1, 1); + SPI_StartTransmit(spi); + + HAL_SemaphoreWait(&hdl->block, SPI_MAX_WAIT_MS); + HAL_DMA_Stop(hdl->tx_dmaChannel); + HAL_DMA_Stop(hdl->rx_dmaChannel); + while (SPI_IntState(hdl->spi, SPI_INT_TRANSFER_COMPLETE) == 0); + SPI_ClearInt(spi, SPI_INT_TRANSFER_COMPLETE); + } + else if (hdl->config.opMode == SPI_OPERATION_MODE_POLL) + { + SPI_StartTransmit(spi); + while ((tx->count > 0) || (rx->count > 0)) + { + while ((SPI_IntState(spi, SPI_INT_RXFIFO_FULL) == 0) && (tx->count > 0)) { + tx->count--; + SPI_Write(spi, tx->ptr); + tx->ptr++; + } + while (SPI_GetRxFifoCounter(spi) > 0) { + rx->count--; + SPI_Read(spi, rx->ptr); + rx->ptr++; + } + } + + if ((tx->count != 0) || (rx->count != 0)) { + SPI_ALERT("spi_tx_rx error with tx count = %d, rx count = %d.\n", tx->count, rx->count); + ret = HAL_ERROR; + goto out; + } + + while (SPI_IntState(hdl->spi, SPI_INT_TRANSFER_COMPLETE) == 0); + SPI_ClearInt(spi, SPI_INT_TRANSFER_COMPLETE); + + } + + SPI_DEBUG_TRANSFER_DATA(rx_data, size, "Rx"); + SPI_DEBUG_TRANSFER_DATA(tx_data, size, "Tx"); + +out: + hdl->sm.status = SPI_STATUS_BUSY; + return ret; +} + + diff --git a/platform/mcu/xr871/src/driver/chip/hal_spinlock.c b/platform/mcu/xr871/src/driver/chip/hal_spinlock.c new file mode 100644 index 0000000000..0fcfbfe14e --- /dev/null +++ b/platform/mcu/xr871/src/driver/chip/hal_spinlock.c @@ -0,0 +1,217 @@ +/** + * @file hal_spinlock.c + * @author XRADIO IOT WLAN Team + */ + +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include "hal_base.h" +#include "driver/chip/hal_spinlock.h" +#include "kernel/os/os.h" + +#define DBG_SPIN 0 +#define SPIN_DBG(fmt, arg...) \ + HAL_LOG(HAL_DBG_ON && DBG_SPIN, "[HAL SPIN] "fmt, ##arg) + +static uint32_t SPIN_Num() +{ + __IO uint32_t *reg = SPINLOCK_SYSTATUS_REG; + + uint32_t num = HAL_GET_BIT(*reg, SPINLOCK_NUM); + num = num >> 28; + + if (num > 1) + num = (num - 1) * 2; + + return num * 32; +} + +static uint32_t SPIN_Status() +{ + __IO uint32_t *reg = SPINLOCK_STATUS_REG; + return *reg; +} + +static uint32_t SPIN_ReadLock(int lock) +{ + __IO uint32_t *reg = SPINLOCKN_LOCK_REG(lock); + return HAL_GET_BIT(*reg, SPINLOCK_TAKEN); +} + +static void SPIN_ClearLock(int lock) +{ + __IO uint32_t *reg = SPINLOCKN_LOCK_REG(lock); + HAL_CLR_BIT(*reg, SPINLOCK_TAKEN); +} + +static int SPIN_AllocationLock() +{ + int i = 0; + uint32_t spin_sta = SPIN_Status(); + SPIN_DBG("%s %d, spin_sta %d\n", __func__, __LINE__, spin_sta); + uint32_t spin_num = SPIN_Num(); + SPIN_DBG("%s %d, SPIN_Num %u\n", __func__, __LINE__, spin_num); + + while (i < spin_num) { + if (! (spin_sta & HAL_BIT(i))) + return i; + i++; + } + + return -1; +} + +static int SPIN_LockCheck(SPIN_Lock_t *lock) +{ + int ret = 0; + + if (!lock) + ret = -1; + + if (lock->lock > SPIN_Num()) + ret = -1; + + return ret; +} + +static int SPIN_UnLockCheck(SPIN_Lock_t *lock) +{ + int ret = 0; + + if (!lock) + ret = -1; + + if (lock->lock > SPIN_Num()) + ret = -1; + + if (!(SPIN_Status()& HAL_BIT(lock->lock))) //it is unlock + ret = -1; + + return ret; +} + + +/** + * @brief Init the spinlock module. + * @retval HAL_Status: The status of driver + */ +HAL_Status HAL_SPIN_Init() +{ + HAL_CCM_BusDisablePeriphClock(CCM_BUS_PERIPH_BIT_SPINLOCK); + HAL_CCM_BusForcePeriphReset(CCM_BUS_PERIPH_BIT_SPINLOCK); + HAL_CCM_BusReleasePeriphReset(CCM_BUS_PERIPH_BIT_SPINLOCK); + HAL_CCM_BusEnablePeriphClock(CCM_BUS_PERIPH_BIT_SPINLOCK); + + return HAL_OK; +} + +/** + * @brief Deinit the spinlock module. + * @retval HAL_Status: The status of driver + */ +HAL_Status HAL_SPIN_Deinit() +{ + HAL_CCM_BusDisablePeriphClock(CCM_BUS_PERIPH_BIT_SPINLOCK); + + return HAL_OK; +} + +/** + * @brief Request a spinlock. + * @note Allocation a spinlock resources. + * @param lock: used to save the spinlock number; + * @retval HAL_Status: The status of driver + */ +HAL_Status HAL_SPIN_RequestLock(SPIN_Lock_t *lock) +{ + if (!lock) { + SPIN_DBG("%s %d invalid param\n", __func__, __LINE__); + return HAL_ERROR; + } + + int spin_lock = SPIN_AllocationLock(); + lock->lock = spin_lock; + + if (spin_lock == -1) + return HAL_ERROR; + + return HAL_OK; +} + +/** + * @brief Taken lock. + * @note If the lock alreadytaken, it will be block until lock is released. + * Else the lock will be taken. + * @param lock: The spinlock. + * @retval HAL_Status: The status of driver + */ +HAL_Status HAL_SPIN_Lock(SPIN_Lock_t *lock) +{ + if (SPIN_LockCheck(lock) != 0) + return HAL_INVALID; + + while (SPIN_ReadLock(lock->lock)); + + return HAL_OK; +} + +/** + * @brief Try to taken lock. + * @note If the lock lock alreadytaken, it will be return busy. + * Else the lcok will be taken. + * @param lock: The spinlock. + * @retval HAL_Status: The status of driver + */ +HAL_Status HAL_SPIN_TryLock(SPIN_Lock_t *lock) +{ + if (SPIN_LockCheck(lock) != 0) + return HAL_INVALID; + + if (SPIN_ReadLock(lock->lock)) + return HAL_BUSY; + + return HAL_OK; +} + +/** + * @brief Unlock. + * @param lock: The spinlock. + * @retval HAL_Status: The status of driver + */ +HAL_Status HAL_SPIN_Unlock(SPIN_Lock_t *lock) +{ + if (SPIN_UnLockCheck(lock) != 0) + return HAL_INVALID; + + SPIN_ClearLock(lock->lock); + + return HAL_OK; +} diff --git a/platform/mcu/xr871/src/driver/chip/hal_timer.c b/platform/mcu/xr871/src/driver/chip/hal_timer.c new file mode 100644 index 0000000000..32ddf04d3a --- /dev/null +++ b/platform/mcu/xr871/src/driver/chip/hal_timer.c @@ -0,0 +1,243 @@ +/** + * @file hal_timer.c + * @author XRADIO IOT WLAN Team + */ + +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "driver/chip/hal_timer.h" +#include "hal_base.h" + +typedef enum { + TIMER_STATE_INVALID = 0, + TIMER_STATE_READY = 1, + TIMER_STATE_RUNNING = 2 +} TIMER_State; + +typedef struct { + TIMER_State state; + TIMER_IRQCallback callback; + void *arg; +} TIMER_Private; + +static TIMER_Private gTimerPrivate[TIMER_NUM]; + +#define TIMER_ASSERT_ID(timerID) HAL_ASSERT_PARAM((timerID) < TIMER_NUM) + +static void TIMER_EnableIRQ(TIMER_ID timerID) +{ + TIMER_ASSERT_ID(timerID); + + HAL_SET_BIT(TIMER->IRQ_EN, HAL_BIT(timerID)); +} + +static void TIMER_DisableIRQ(TIMER_ID timerID) +{ + TIMER_ASSERT_ID(timerID); + + HAL_CLR_BIT(TIMER->IRQ_EN, HAL_BIT(timerID)); +} + +static int TIMER_IsPendingIRQ(TIMER_ID timerID) +{ + TIMER_ASSERT_ID(timerID); + + return HAL_GET_BIT_VAL(TIMER->IRQ_STATUS, timerID, 1); +} + +static void TIMER_ClearPendingIRQ(TIMER_ID timerID) +{ + TIMER_ASSERT_ID(timerID); + + HAL_SET_BIT(TIMER->IRQ_STATUS, HAL_BIT(timerID)); +} + +static void TIMER_IRQHandler(TIMER_ID timerID) +{ + if (TIMER_IsPendingIRQ(timerID)) { + TIMER_ClearPendingIRQ(timerID); + if (gTimerPrivate[timerID].callback) { + gTimerPrivate[timerID].callback(gTimerPrivate[timerID].arg); + } + } +} + +void TIMER0_IRQHandler(void) +{ + TIMER_IRQHandler(TIMER0_ID); +} + +void TIMER1_IRQHandler(void) +{ + TIMER_IRQHandler(TIMER1_ID); +} + +/** + * @brief Initialize the timer according to the specified parameters + * @param[in] timerID ID of the specified timer + * @param[in] param Pointer to TIMER_InitParam structure + * @retval HAL_Status, HAL_OK on success + */ +HAL_Status HAL_TIMER_Init(TIMER_ID timerID, const TIMER_InitParam *param) +{ + TIMER_Private *timerPriv; + IRQn_Type IRQn; + unsigned long flags; + + TIMER_ASSERT_ID(timerID); + + if (param->period == 0) { + return HAL_INVALID; + } + + flags = HAL_EnterCriticalSection(); + + timerPriv = &gTimerPrivate[timerID]; + if (timerPriv->state == TIMER_STATE_INVALID) + timerPriv->state = TIMER_STATE_READY; + else + timerPriv = NULL; + + HAL_ExitCriticalSection(flags); + + if (timerPriv == NULL) { + HAL_WRN("timer %d already inited\n", timerID); + return HAL_BUSY; + } + + /* enable clock */ + + /* release reset */ + + /* init parameters */ +#if 1 + /* NB: Clear other bits other than param->cfg, it seems no side effect */ + TIMER->TIMERx[timerID].CTRL = param->cfg; +#else + HAL_MODIFY_REG(TIMER->TIMERx[timerID].CTRL, + TIMER_MODE_MASK | TIMER_CLK_SRC_MASK | TIMER_CLK_PRESCALER_MASK, + param->cfg); +#endif + TIMER->TIMERx[timerID].LOAD_VAL = param->period; + + if (param->isEnableIRQ) { + IRQn = (timerID == TIMER0_ID) ? TIMER0_IRQn : TIMER1_IRQn; +#if 0 + NVIC_IRQHandler IRQHandler = (timerID == TIMER0_ID ? TIMER0_IRQHandler : TIMER1_IRQHandler); + HAL_NVIC_SetIRQHandler(IRQn, IRQHandler); +#endif + timerPriv->callback = param->callback; + timerPriv->arg = param->arg; + if (TIMER_IsPendingIRQ(timerID)) { + TIMER_ClearPendingIRQ(timerID); + } + TIMER_EnableIRQ(timerID); + HAL_NVIC_SetPriority(IRQn, NVIC_PERIPHERAL_PRIORITY_DEFAULT); + HAL_NVIC_EnableIRQ(IRQn); + } + + return HAL_OK; +} + +/** + * @brief DeInitialize the specified timer + * @param[in] timerID ID of the specified timer + * @retval HAL_Status, HAL_OK on success + */ +HAL_Status HAL_TIMER_DeInit(TIMER_ID timerID) +{ + TIMER_Private *timerPriv; + unsigned long flags; + + TIMER_ASSERT_ID(timerID); + + timerPriv = &gTimerPrivate[timerID]; + + /* stop timer */ + HAL_TIMER_Stop(timerID); + + /* disable IRQ */ + HAL_NVIC_DisableIRQ(timerID == TIMER0_ID ? TIMER0_IRQn : TIMER1_IRQn); + TIMER_DisableIRQ(timerID); + if (TIMER_IsPendingIRQ(timerID)) { + TIMER_ClearPendingIRQ(timerID); + } + timerPriv->callback = NULL; + timerPriv->arg = NULL; + + /* force reset */ + + /* disable clock */ + + flags = HAL_EnterCriticalSection(); + timerPriv->state = TIMER_STATE_INVALID; + HAL_ExitCriticalSection(flags); + + return HAL_OK; +} + +/** + * @brief Reload the timer's current value and start to count down + * @param[in] timerID ID of the specified timer + * @return None + */ +void HAL_TIMER_Start(TIMER_ID timerID) +{ + TIMER_ASSERT_ID(timerID); + + HAL_SET_BIT(TIMER->TIMERx[timerID].CTRL, TIMER_RELOAD_BIT | TIMER_START_BIT); +} + +/** + * @brief Stop the timer's counting, the current value of timer is kept + * @param[in] timerID ID of the specified timer + * @return None + */ +void HAL_TIMER_Stop(TIMER_ID timerID) +{ + TIMER_ASSERT_ID(timerID); + + HAL_CLR_BIT(TIMER->TIMERx[timerID].CTRL, TIMER_START_BIT); +} + +/** + * @brief Continue the timer's counting + * + * The timer's current value is not changed, and start to count down + * + * @param[in] timerID ID of the specified timer + * @return None + */ +void HAL_TIMER_Continue(TIMER_ID timerID) +{ + TIMER_ASSERT_ID(timerID); + + HAL_SET_BIT(TIMER->TIMERx[timerID].CTRL, TIMER_START_BIT); +} diff --git a/platform/mcu/xr871/src/driver/chip/hal_uart.c b/platform/mcu/xr871/src/driver/chip/hal_uart.c new file mode 100644 index 0000000000..48a3d21433 --- /dev/null +++ b/platform/mcu/xr871/src/driver/chip/hal_uart.c @@ -0,0 +1,1226 @@ +/** + * @file hal_uart.c + * @author XRADIO IOT WLAN Team + */ + +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "driver/chip/hal_dma.h" +#include "driver/chip/hal_uart.h" +#include "hal_base.h" +#include "pm/pm.h" + +#define UART_TRANSMIT_BY_IRQ_HANDLER 1 + +typedef enum { + UART_STATE_INVALID = 0, + UART_STATE_READY = 1, +} UART_State; + +typedef struct { + UART_State state; + + HAL_Semaphore txSem; + HAL_Semaphore rxSem; + +#if UART_TRANSMIT_BY_IRQ_HANDLER + uint8_t *txBuf; + int32_t txBufSize; +#endif /* UART_TRANSMIT_BY_IRQ_HANDLER */ + + uint8_t *rxBuf; + int32_t rxBufSize; + + UART_RxReadyCallback rxReadyCallback; + void *arg; + + DMA_Channel txDMAChan; + DMA_Channel rxDMAChan; +} UART_Private; + +static UART_Private gUartPrivate[UART_NUM]; +static UART_T * const gUartInstance[UART_NUM] = { UART0, UART1 }; + +__STATIC_INLINE IRQn_Type UART_GetIRQnType(UART_ID uartID) +{ + return (uartID == UART0_ID ? UART0_IRQn: UART1_IRQn); +} + +#ifdef CONFIG_PM + +//#define CONFIG_UART_PM_DEBUG + +#ifdef CONFIG_UART_PM_DEBUG +#define PM_UART_PRINT_BUF_LEN 512 +static volatile uint32_t pm_print_index; +static char pm_print_buf[PM_UART_PRINT_BUF_LEN]; +#endif + +static int8_t g_uart_suspending = 0; +static int8_t g_uart_irq_enable = 0; +static UART_InitParam g_uart_param[UART_NUM]; +static UART_RxReadyCallback g_uart_cb[UART_NUM]; +static void *g_uart_arg[UART_NUM]; + +static int uart_suspend(struct soc_device *dev, enum suspend_state_t state) +{ + UART_ID uartID = (UART_ID)dev->platform_data; + + g_uart_suspending |= (1 << uartID); + + switch (state) { + case PM_MODE_SLEEP: + case PM_MODE_STANDBY: + case PM_MODE_HIBERNATION: + case PM_MODE_POWEROFF: + if (g_uart_irq_enable & (1 << uartID)) + HAL_UART_DisableRxCallback(uartID); + while (!HAL_UART_IsTxEmpty(HAL_UART_GetInstance(uartID))) + ; + for (volatile int i = 0; i < 1000; i++) /* wait tx done */ + i = i; + HAL_DBG("%s id:%d okay\n", __func__, uartID); + HAL_UART_DeInit(uartID); + break; + default: + break; + } + return 0; +} + +static int uart_resume(struct soc_device *dev, enum suspend_state_t state) +{ + UART_ID uartID = (UART_ID)dev->platform_data; + + switch (state) { + case PM_MODE_SLEEP: + case PM_MODE_STANDBY: + case PM_MODE_HIBERNATION: + HAL_UART_Init(uartID, &g_uart_param[uartID]); + if (g_uart_irq_enable & (1 << uartID)) + HAL_UART_EnableRxCallback(uartID, g_uart_cb[uartID], + g_uart_arg[uartID]); + HAL_DBG("%s id:%d okay\n", __func__, uartID); + break; + default: + break; + } + + g_uart_suspending &= ~(1 << uartID); + +#ifdef CONFIG_UART_PM_DEBUG + if (pm_print_index) { + HAL_DBG("%s", pm_print_buf); + pm_print_index = 0; + } +#endif + return 0; +} + +static struct soc_device_driver uart_drv = { + .name = "uart", + .suspend_noirq = uart_suspend, + .resume_noirq = uart_resume, +}; + +static struct soc_device uart_dev[UART_NUM] = { + {.name = "uart0", .driver = &uart_drv, .platform_data = (void *)UART0_ID,}, + {.name = "uart1", .driver = &uart_drv, .platform_data = (void *)UART1_ID,}, +}; + +#define UART_DEV(id) (&uart_dev[id]) + +#endif /* CONFIG_PM */ + +#define UART_ASSERT_ID(uartID) HAL_ASSERT_PARAM((uartID) < UART_NUM) + +__STATIC_INLINE CCM_BusPeriphBit UART_GetCCMPeriphBit(UART_ID uartID) +{ + return (uartID == UART0_ID ? CCM_BUS_PERIPH_BIT_UART0 : + CCM_BUS_PERIPH_BIT_UART1); +} + +__STATIC_INLINE UART_Private *UART_GetUartPriv(UART_ID uartID) +{ + return &gUartPrivate[uartID]; +} + +static void UART_EnableIRQ(UART_T *uart, uint32_t mask) +{ + HAL_SET_BIT(uart->DLH_IER.IRQ_EN, mask); +} + +static void UART_DisableIRQ(UART_T *uart, uint32_t mask) +{ + HAL_CLR_BIT(uart->DLH_IER.IRQ_EN, mask); +} + +__STATIC_INLINE void UART_DisableAllIRQ(UART_T *uart) +{ + uart->DLH_IER.IRQ_EN = 0U; +} + +__STATIC_INLINE void UART_EnableTxReadyIRQ(UART_T *uart) +{ + UART_EnableIRQ(uart, UART_TX_FIFO_TRIG_MODE_EN_BIT | UART_TX_READY_IRQ_EN_BIT); +} + +__STATIC_INLINE void UART_DisableTxReadyIRQ(UART_T *uart) +{ + UART_DisableIRQ(uart, UART_TX_FIFO_TRIG_MODE_EN_BIT | UART_TX_READY_IRQ_EN_BIT); +} + +__STATIC_INLINE void UART_EnableRxReadyIRQ(UART_T *uart) +{ + UART_EnableIRQ(uart, UART_RX_READY_IRQ_EN_BIT); +} + +__STATIC_INLINE void UART_DisableRxReadyIRQ(UART_T *uart) +{ + UART_DisableIRQ(uart, UART_RX_READY_IRQ_EN_BIT); +} + +__STATIC_INLINE void UART_EnableFIFO(UART_T *uart) +{ + HAL_SET_BIT(uart->IIR_FCR.FIFO_CTRL, UART_FIFO_EN_BIT); +} + +__STATIC_INLINE void UART_DisableFIFO(UART_T *uart) +{ + HAL_CLR_BIT(uart->IIR_FCR.FIFO_CTRL, UART_FIFO_EN_BIT); +} + +__STATIC_INLINE void UART_ResetTxFIFO(UART_T *uart) +{ + HAL_SET_BIT(uart->IIR_FCR.FIFO_CTRL, UART_TX_FIFO_RESET_BIT); +} + +__STATIC_INLINE void UART_ResetRxFIFO(UART_T *uart) +{ + HAL_SET_BIT(uart->IIR_FCR.FIFO_CTRL, UART_RX_FIFO_RESET_BIT); +} + +/* baudRate = apb_freq / (16 * div) */ +__STATIC_INLINE uint32_t UART_CalcClkDiv(uint32_t baudRate) +{ + uint32_t div; + + div = baudRate << 4; + div = (HAL_GetAPBClock() + (div >> 1)) / div; + if (div == 0) + div = 1; + return div; +} + +__STATIC_INLINE void UART_SetClkDiv(UART_T *uart, uint16_t div) +{ + HAL_SET_BIT(uart->LINE_CTRL, UART_DIV_ACCESS_BIT); + + uart->RBR_THR_DLL.DIV_LOW = div & UART_DIV_LOW_MASK; + uart->DLH_IER.DIV_HIGH = (div >> 8) & UART_DIV_HIGH_MASK; + + HAL_CLR_BIT(uart->LINE_CTRL, UART_DIV_ACCESS_BIT); +} + +__STATIC_INLINE int UART_IsBusy(UART_T *uart) +{ + return HAL_GET_BIT(uart->STATUS, UART_BUSY_BIT); +} + +__STATIC_INLINE void UART_EnableTx(UART_T *uart) +{ + HAL_CLR_BIT(uart->HALT, UART_HALT_TX_EN_BIT); +} + +__STATIC_INLINE void UART_DisableTx(UART_T *uart) +{ + HAL_SET_BIT(uart->HALT, UART_HALT_TX_EN_BIT); +} + +/** + * @brief Get UART hardware instance by the specified UART ID + * @param[in] uartID ID of the specified UART + * @return UART hardware instance + */ +UART_T *HAL_UART_GetInstance(UART_ID uartID) +{ + return gUartInstance[uartID]; +} + +/** + * @brief Check whether the specified UART can transmit data or not + * @param[in] uart UART hardware instance + * @return 1 The UART can transmit data + * @return 0 The UART cannot transmit data + */ +int HAL_UART_IsTxReady(UART_T *uart) +{ +// return HAL_GET_BIT(uart->LINE_STATUS, UART_TX_HOLD_EMPTY_BIT); + return HAL_GET_BIT(uart->STATUS, UART_TX_FIFO_NOT_FULL_BIT); +} + +/** + * @brief Check whether the transmit FIFO of the specified UART is empty or not + * @param[in] uart UART hardware instance + * @return 1 The UART transmit FIFO is empty + * @return 0 The UART transmit FIFO is not empty + */ +int HAL_UART_IsTxEmpty(UART_T *uart) +{ + return HAL_GET_BIT(uart->STATUS, UART_TX_FIFO_EMPTY_BIT); +} + +/** + * @brief Check whether the specified UART has receive data or not + * @param[in] uart UART hardware instance + * @return 1 The UART has receive data + * @return 0 The UART has no receive data + */ +int HAL_UART_IsRxReady(UART_T *uart) +{ +#ifdef CONFIG_PM + UART_ID uartID = (uart == gUartInstance[UART0_ID]) ? UART0_ID : UART1_ID; + + if (g_uart_suspending & (1 << uartID)) + return 0; +#endif + + return HAL_GET_BIT(uart->LINE_STATUS, UART_RX_READY_BIT); +// return HAL_GET_BIT(uart->STATUS, UART_RX_FIFO_NOT_EMPTY_BIT); +} + +/** + * @brief Get one byte received data of the specified UART + * @param[in] uart UART hardware instance + * @return One byte received data + * + * @note Before calling this function, make sure the specified UART has + * receive data by calling HAL_UART_IsRxReady() + */ +uint8_t HAL_UART_GetRxData(UART_T *uart) +{ + return (uint8_t)(uart->RBR_THR_DLL.RX_BUF); +} + +uint8_t HAL_UART_GetRxFIFOLevel(UART_T *uart) +{ + return (uint8_t)(uart->RX_FIFO_LEVEL); +} + +/** + * @brief Transmit one byte data for the specified UART + * @param[in] uart UART hardware instance + * @param[in] data one byte data + * @return None + * + * @note Before calling this function, make sure the specified UART can + * transmit data by calling HAL_UART_IsTxReady() + */ +void HAL_UART_PutTxData(UART_T *uart, uint8_t data) +{ + uart->RBR_THR_DLL.TX_HOLD = data; +} + +__STATIC_INLINE uint32_t UART_GetInterruptID(UART_T *uart) +{ + return HAL_GET_BIT(uart->IIR_FCR.IRQ_ID, UART_IID_MASK); +} + +__STATIC_INLINE uint32_t UART_GetLineStatus(UART_T *uart) +{ + return HAL_GET_BIT(uart->LINE_STATUS, UART_LINE_STATUS_MASK); +} + +__STATIC_INLINE uint32_t UART_GetModemStatus(UART_T *uart) +{ + return HAL_GET_BIT(uart->MODEM_STATUS, UART_MODEM_STATUS_MASK); +} + +__STATIC_INLINE uint32_t UART_GetUartStatus(UART_T *uart) +{ + return HAL_GET_BIT(uart->STATUS, UART_STATUS_MASK); +} + +static void UART_IRQHandler(UART_T *uart, UART_Private *priv) +{ + uint32_t iid = UART_GetInterruptID(uart); + + switch (iid) { + case UART_IID_MODEM_STATUS: + UART_GetModemStatus(uart); + break; + case UART_IID_TX_READY: +#if UART_TRANSMIT_BY_IRQ_HANDLER + if (priv->txBuf) { + while (priv->txBufSize > 0) { + if (HAL_UART_IsTxReady(uart)) { + HAL_UART_PutTxData(uart, *priv->txBuf); + ++priv->txBuf; + --priv->txBufSize; + } else { + break; + } + } + if (priv->txBufSize == 0) { + UART_DisableTxReadyIRQ(uart); + HAL_SemaphoreRelease(&priv->txSem); /* end transmitting */ + } + } +#else /* UART_TRANSMIT_BY_IRQ_HANDLER */ + UART_DisableTxReadyIRQ(uart); + HAL_SemaphoreRelease(&priv->txSem); +#endif /* UART_TRANSMIT_BY_IRQ_HANDLER */ + break; + case UART_IID_RX_READY: + case UART_IID_CHAR_TIMEOUT: + if (priv->rxReadyCallback) { + priv->rxReadyCallback(priv->arg); + } else if (priv->rxBuf) { + while (priv->rxBufSize > 0) { + if (HAL_UART_IsRxReady(uart)) { + *priv->rxBuf = HAL_UART_GetRxData(uart); + ++priv->rxBuf; + --priv->rxBufSize; + } else { + break; + } + } + if (priv->rxBufSize == 0 || iid == UART_IID_CHAR_TIMEOUT) { + UART_DisableRxReadyIRQ(uart); + HAL_SemaphoreRelease(&priv->rxSem); /* end receiving */ + } + } else { + HAL_WRN("no one receive data, but uart irq is enable\n"); + /* discard received data */ + while (HAL_UART_IsRxReady(uart)) { + HAL_UART_GetRxData(uart); + } + } + break; + case UART_IID_LINE_STATUS: + UART_GetLineStatus(uart); + break; + case UART_IID_BUSY_DETECT: + UART_GetUartStatus(uart); + break; + case UART_IID_RS485: + default: + break; + } +} + +void UART0_IRQHandler(void) +{ + UART_IRQHandler(UART0, &gUartPrivate[UART0_ID]); +} + +void UART1_IRQHandler(void) +{ + UART_IRQHandler(UART1, &gUartPrivate[UART1_ID]); +} + +void N_UART_IRQHandler(void) +{ + HAL_NVIC_DisableIRQ(N_UART_IRQn); + HAL_NVIC_ClearPendingIRQ(N_UART_IRQn); +} + +/** + * @brief Initialize the UART according to the specified parameters + * @param[in] uartID ID of the specified UART + * @param[in] param Pointer to UART_InitParam structure + * @retval HAL_Status, HAL_OK on success + */ +HAL_Status HAL_UART_Init(UART_ID uartID, const UART_InitParam *param) +{ + UART_T *uart; + UART_Private *priv; + CCM_BusPeriphBit ccmPeriphBit; + IRQn_Type IRQn; + uint32_t tmp; + unsigned long flags; + + UART_ASSERT_ID(uartID); + + flags = HAL_EnterCriticalSection(); + + priv = UART_GetUartPriv(uartID); + if (priv->state == UART_STATE_INVALID) + priv->state = UART_STATE_READY; + else + priv = NULL; + + HAL_ExitCriticalSection(flags); + + if (priv == NULL) { + HAL_WRN("uart %d already inited\n", uartID); + return HAL_BUSY; + } + + uart = HAL_UART_GetInstance(uartID); + + HAL_SemaphoreInitBinary(&priv->txSem); + HAL_SemaphoreInitBinary(&priv->rxSem); +#if UART_TRANSMIT_BY_IRQ_HANDLER + priv->txBuf = NULL; + priv->txBufSize = 0; +#endif + priv->rxBuf = NULL; + priv->rxBufSize = 0; + priv->rxReadyCallback = NULL; + priv->arg = NULL; + priv->txDMAChan = DMA_CHANNEL_INVALID; + priv->rxDMAChan = DMA_CHANNEL_INVALID; + + /* config pinmux */ + HAL_BoardIoctl(HAL_BIR_PINMUX_INIT, HAL_MKDEV(HAL_DEV_MAJOR_UART, uartID), 0); + + /* enable uart clock and release reset */ + ccmPeriphBit = UART_GetCCMPeriphBit(uartID); + HAL_CCM_BusEnablePeriphClock(ccmPeriphBit); + HAL_CCM_BusReleasePeriphReset(ccmPeriphBit); + + UART_DisableAllIRQ(uart); /* disable all IRQ */ + + UART_DisableTx(uart); + + /* wait uart become not busy if necessary */ + while (UART_IsBusy(uart)) { + ; + } + + /* set baud rate, parity, stop bits, data bits */ + tmp = UART_CalcClkDiv(param->baudRate); + UART_SetClkDiv(uart, (uint16_t)tmp); + + HAL_MODIFY_REG(uart->LINE_CTRL, + UART_PARITY_MASK | UART_STOP_BITS_MASK | + UART_DATA_BITS_MASK | UART_BREAK_CTRL_BIT, + param->parity | param->stopBits | param->dataBits); + + /* set uart->MODEM_CTRL */ + if (uartID != UART0_ID && param->isAutoHwFlowCtrl) { + tmp = UART_WORK_MODE_UART | UART_AUTO_FLOW_CTRL_EN_BIT | UART_RTS_ASSERT_BIT; + } else { + tmp = UART_WORK_MODE_UART; + } + HAL_MODIFY_REG(uart->MODEM_CTRL, + UART_WORK_MODE_MASK | UART_LOOP_BACK_EN_BIT | + UART_AUTO_FLOW_CTRL_EN_BIT | UART_RTS_ASSERT_BIT, + tmp); + + /* set FIFO level, DMA mode, reset FIFO, enable FIFO */ + uart->IIR_FCR.FIFO_CTRL = UART_RX_FIFO_TRIG_LEVEL_HALF_FULL | + UART_TX_FIFO_TRIG_LEVEL_EMPTY | + UART_DMA_MODE_1 | + UART_TX_FIFO_RESET_BIT | + UART_RX_FIFO_RESET_BIT | + UART_FIFO_EN_BIT; + UART_EnableTx(uart); + + /* enable NVIC IRQ */ + IRQn = UART_GetIRQnType(uartID); + HAL_NVIC_SetPriority(IRQn, NVIC_PERIPHERAL_PRIORITY_DEFAULT); + HAL_NVIC_EnableIRQ(IRQn); + +#ifdef CONFIG_PM + if (!(g_uart_suspending & (1 << uartID))) { + memcpy(&g_uart_param[uartID], param, sizeof(UART_InitParam)); + pm_register_ops(UART_DEV(uartID)); + } +#endif + + return HAL_OK; +} + +/** + * @brief DeInitialize the specified UART + * @param[in] uartID ID of the specified UART + * @retval HAL_Status, HAL_OK on success + */ +HAL_Status HAL_UART_DeInit(UART_ID uartID) +{ + UART_T *uart; + UART_Private *priv; + CCM_BusPeriphBit ccmPeriphBit; + unsigned long flags; + + UART_ASSERT_ID(uartID); + + priv = UART_GetUartPriv(uartID); + if (priv->state == UART_STATE_INVALID) { + return HAL_OK; + } + uart = HAL_UART_GetInstance(uartID); + +#ifdef CONFIG_PM + if (!(g_uart_suspending & (1 << uartID))) { + pm_unregister_ops(UART_DEV(uartID)); + } +#endif + + HAL_UART_DisableTxDMA(uartID); + HAL_UART_DisableRxDMA(uartID); + + if (priv->rxReadyCallback != NULL) { + HAL_WRN("RX callback should be disabled first\n"); + } + HAL_NVIC_DisableIRQ(UART_GetIRQnType(uartID)); + UART_DisableAllIRQ(uart); + UART_DisableTx(uart); + UART_DisableFIFO(uart); + + /* disable uart clock and force reset */ + ccmPeriphBit = UART_GetCCMPeriphBit(uartID); + HAL_CCM_BusForcePeriphReset(ccmPeriphBit); + HAL_CCM_BusDisablePeriphClock(ccmPeriphBit); + + /* De-config pinmux */ + HAL_BoardIoctl(HAL_BIR_PINMUX_DEINIT, HAL_MKDEV(HAL_DEV_MAJOR_UART, uartID), 0); + + HAL_SemaphoreDeinit(&priv->txSem); + HAL_SemaphoreDeinit(&priv->rxSem); + + flags = HAL_EnterCriticalSection(); + priv->state = UART_STATE_INVALID; + HAL_ExitCriticalSection(flags); + + return HAL_OK; +} + +/** + * @brief Transmit an amount of data in interrupt mode + * @param[in] uartID ID of the specified UART + * @param[in] buf Pointer to the data buffer + * @param[in] size Number of bytes to be transmitted + * @return Number of bytes transmitted, -1 on error + * + * @note This function is not thread safe. If using the UART transmit series + * functions in multi-thread, make sure they are executed exclusively. + */ +int32_t HAL_UART_Transmit_IT(UART_ID uartID, uint8_t *buf, int32_t size) +{ + UART_T *uart; + UART_Private *priv; + + UART_ASSERT_ID(uartID); + + if (buf == NULL || size <= 0) { + return -1; + } + + /* TODO: add protection */ + + uart = HAL_UART_GetInstance(uartID); + priv = UART_GetUartPriv(uartID); + +#if UART_TRANSMIT_BY_IRQ_HANDLER + priv->txBuf = buf; + priv->txBufSize = size; + + UART_EnableTxReadyIRQ(uart); + HAL_SemaphoreWait(&priv->txSem, HAL_WAIT_FOREVER); + UART_DisableTxReadyIRQ(uart); + priv->txBuf = NULL; + size -= priv->txBufSize; + priv->txBufSize = 0; +#else /* UART_TRANSMIT_BY_IRQ_HANDLER */ + uint8_t *ptr = buf; + int32_t left = size; + while (left > 0) { + if (HAL_UART_IsTxReady(uart)) { + HAL_UART_PutTxData(uart, *ptr); + ++ptr; + --left; + } else { + UART_EnableTxReadyIRQ(uart); + HAL_SemaphoreWait(&priv->txSem, HAL_WAIT_FOREVER); + } + } + UART_DisableTxReadyIRQ(uart); /* just in case */ +// size -= left; +#endif /* UART_TRANSMIT_BY_IRQ_HANDLER */ + + /* TODO: add protection */ + + return size; +} + +/** + * @brief Receive an amount of data in interrupt mode + * @param[in] uartID ID of the specified UART + * @param[out] buf Pointer to the data buffer + * @param[in] size The maximum number of bytes to be received. + * The actual received bytes can be less than this. + * @param[in] msec Timeout value in millisecond to receive data. + * HAL_WAIT_FOREVER for no timeout. + * @return Number of bytes received, -1 on error + * + * @note This function is not thread safe. If using the UART receive series + * functions in multi-thread, make sure they are executed exclusively. + */ +int32_t HAL_UART_Receive_IT(UART_ID uartID, uint8_t *buf, int32_t size, uint32_t msec) +{ + UART_T *uart; + UART_Private *priv; + + UART_ASSERT_ID(uartID); + + if (buf == NULL || size <= 0) { + return -1; + } + + /* TODO: add protection */ + + uart = HAL_UART_GetInstance(uartID); + priv = UART_GetUartPriv(uartID); + + if (priv->rxReadyCallback != NULL) { + HAL_WRN("rx callback is enabled\n"); + return -1; + } + + priv->rxBuf = buf; + priv->rxBufSize = size; + + UART_EnableRxReadyIRQ(uart); + HAL_SemaphoreWait(&priv->rxSem, msec); + UART_DisableRxReadyIRQ(uart); + + priv->rxBuf = NULL; + size -= priv->rxBufSize; + priv->rxBufSize = 0; + + /* TODO: add protection */ + + return size; +} + +/** + * @brief Enable receive ready callback function for the specified UART + * @param[in] uartID ID of the specified UART + * @param[in] cb The UART receive ready callback function + * @param[in] arg Argument of the UART receive ready callback function + * @retval HAL_Status, HAL_OK on success + * + * @note To handle receive data externally, use this function to enable the + * receive ready callback function, then receive and process the data in + * the callback function. + * @note If the receive ready callback function is enabled, all other receive + * series functions cannot be used to receive data. + * @note This function is not thread safe. If using the UART receive series + * functions in multi-thread, make sure they are executed exclusively. + */ +HAL_Status HAL_UART_EnableRxCallback(UART_ID uartID, UART_RxReadyCallback cb, void *arg) +{ + UART_T *uart; + UART_Private *priv; + + UART_ASSERT_ID(uartID); + + /* TODO: add protection */ + + uart = HAL_UART_GetInstance(uartID); + priv = UART_GetUartPriv(uartID); + + priv->rxReadyCallback = cb; + priv->arg = arg; + + UART_EnableRxReadyIRQ(uart); + + /* TODO: add protection */ + +#ifdef CONFIG_PM + if (!(g_uart_suspending & (1 << uartID))) { + g_uart_cb[uartID] = cb; + g_uart_arg[uartID] = arg; + g_uart_irq_enable |= (1 << uartID); + } +#endif + + return HAL_OK; +} + +/** + * @brief Disable receive ready callback function for the specified UART + * @param[in] uartID ID of the specified UART + * @retval HAL_Status, HAL_OK on success + */ +HAL_Status HAL_UART_DisableRxCallback(UART_ID uartID) +{ + UART_T *uart; + UART_Private *priv; + + UART_ASSERT_ID(uartID); + + /* TODO: add protection */ + + uart = HAL_UART_GetInstance(uartID); + priv = UART_GetUartPriv(uartID); + + UART_DisableRxReadyIRQ(uart); + + priv->rxReadyCallback = NULL; + priv->arg = NULL; + + /* TODO: add protection */ + +#ifdef CONFIG_PM + if (!(g_uart_suspending & (1 << uartID))) { + g_uart_cb[uartID] = NULL; + g_uart_arg[uartID] = NULL; + g_uart_irq_enable &= ~(1 << uartID); + } +#endif + + return HAL_OK; +} + +/** + * @internal + * @brief UART DMA transfer complete callback fucntion to release waiting + * semaphore + * @param[in] arg Pointer to waiting semaphore for DMA transfer + * @return None + */ +static void UART_DMAEndCallback(void *arg) +{ + HAL_SemaphoreRelease((HAL_Semaphore *)arg); +} + +/** + * @brief Enable UART transmitting data in DMA mode + * @param[in] uartID ID of the specified UART + * @retval HAL_Status, HAL_OK on success, HAL_ERROR on no valid DMA channel + * + * @note To transmit data in DMA mode, a DMA channel for the specified + * UART to transmit data MUST be configured first by this function. + */ +HAL_Status HAL_UART_EnableTxDMA(UART_ID uartID) +{ + UART_T *uart; + UART_Private *priv; + DMA_ChannelInitParam dmaParam; + + UART_ASSERT_ID(uartID); + + /* TODO: add protection */ + + uart = HAL_UART_GetInstance(uartID); + priv = UART_GetUartPriv(uartID); + + if (priv->txDMAChan != DMA_CHANNEL_INVALID) { + return HAL_OK; + } + + priv->txDMAChan = HAL_DMA_Request(); + if (priv->txDMAChan == DMA_CHANNEL_INVALID) { + return HAL_ERROR; + } + + HAL_Memset(&dmaParam, 0, sizeof(dmaParam)); + dmaParam.cfg = HAL_DMA_MakeChannelInitCfg(DMA_WORK_MODE_SINGLE, + DMA_WAIT_CYCLE_2, + DMA_BYTE_CNT_MODE_REMAIN, + DMA_DATA_WIDTH_8BIT, + DMA_BURST_LEN_1, + DMA_ADDR_MODE_FIXED, + uartID == UART0_ID ? + DMA_PERIPH_UART0 : + DMA_PERIPH_UART1, + DMA_DATA_WIDTH_8BIT, + DMA_BURST_LEN_1, + DMA_ADDR_MODE_INC, + DMA_PERIPH_SRAM); + dmaParam.irqType = DMA_IRQ_TYPE_END; + dmaParam.endCallback = UART_DMAEndCallback; + dmaParam.endArg = &priv->txSem; + + HAL_DMA_Init(priv->txDMAChan, &dmaParam); + + /* set DMA mode of UART */ +// HAL_MODIFY_REG(uart->IIR_FCR.FIFO_CTRL, UART_DMA_MODE_MASK, UART_DMA_MODE_1); + HAL_SET_BIT(uart->HALT, UART_DMA_PTE_TX_BIT); + + return HAL_OK; +} + +/** + * @brief Enable UART receiving data in DMA mode + * @param[in] uartID ID of the specified UART + * @retval HAL_Status, HAL_OK on success, HAL_ERROR on no valid DMA channel + * + * @note To reveive data in DMA mode, a DMA channel for the specified + * UART to receive data MUST be configured first by this function. + */ +HAL_Status HAL_UART_EnableRxDMA(UART_ID uartID) +{ + UART_T *uart; + UART_Private *priv; + DMA_ChannelInitParam dmaParam; + + UART_ASSERT_ID(uartID); + + /* TODO: add protection */ + + uart = HAL_UART_GetInstance(uartID); + priv = UART_GetUartPriv(uartID); + + if (priv->rxDMAChan != DMA_CHANNEL_INVALID) { + return HAL_OK; + } + + priv->rxDMAChan = HAL_DMA_Request(); + if (priv->rxDMAChan == DMA_CHANNEL_INVALID) { + return HAL_ERROR; + } + + HAL_Memset(&dmaParam, 0, sizeof(dmaParam)); + dmaParam.cfg = HAL_DMA_MakeChannelInitCfg(DMA_WORK_MODE_SINGLE, + DMA_WAIT_CYCLE_2, + DMA_BYTE_CNT_MODE_REMAIN, + DMA_DATA_WIDTH_8BIT, + DMA_BURST_LEN_1, + DMA_ADDR_MODE_INC, + DMA_PERIPH_SRAM, + DMA_DATA_WIDTH_8BIT, + DMA_BURST_LEN_1, + DMA_ADDR_MODE_FIXED, + uartID == UART0_ID ? + DMA_PERIPH_UART0 : + DMA_PERIPH_UART1); + dmaParam.irqType = DMA_IRQ_TYPE_END; + dmaParam.endCallback = UART_DMAEndCallback; + dmaParam.endArg = &priv->rxSem; + + HAL_DMA_Init(priv->rxDMAChan, &dmaParam); + + /* set DMA mode of UART */ +// HAL_MODIFY_REG(uart->IIR_FCR.FIFO_CTRL, UART_DMA_MODE_MASK, UART_DMA_MODE_1); + HAL_SET_BIT(uart->HALT, UART_DMA_PTE_RX_BIT); + + return HAL_OK; +} + +/** + * @brief Disable UART transmitting data in DMA mode + * @param[in] uartID ID of the specified UART + * @retval HAL_Status, HAL_OK on success + */ +HAL_Status HAL_UART_DisableTxDMA(UART_ID uartID) +{ + UART_Private *priv; + + UART_ASSERT_ID(uartID); + + /* TODO: add protection */ + + priv = UART_GetUartPriv(uartID); + + if (priv->txDMAChan != DMA_CHANNEL_INVALID) { + HAL_DMA_Stop(priv->txDMAChan); + HAL_DMA_DeInit(priv->txDMAChan); + HAL_DMA_Release(priv->txDMAChan); + priv->txDMAChan = DMA_CHANNEL_INVALID; + } + + return HAL_OK; +} + +/** + * @brief Disable UART receiving data in DMA mode + * @param[in] uartID ID of the specified UART + * @retval HAL_Status, HAL_OK on success + */ +HAL_Status HAL_UART_DisableRxDMA(UART_ID uartID) +{ + UART_Private *priv; + + UART_ASSERT_ID(uartID); + + /* TODO: add protection */ + + priv = UART_GetUartPriv(uartID); + + if (priv->rxDMAChan != DMA_CHANNEL_INVALID) { + HAL_DMA_Stop(priv->rxDMAChan); + HAL_DMA_DeInit(priv->rxDMAChan); + HAL_DMA_Release(priv->rxDMAChan); + priv->rxDMAChan = DMA_CHANNEL_INVALID; + } + + return HAL_OK; +} + +/** + * @brief Transmit an amount of data in DMA mode + * + * Steps to transmit data in DMA mode: + * - use HAL_UART_EnableTxDMA() to enable UART transmit DMA mode + * - use HAL_UART_Transmit_DMA() to transmit data, it can be called + * repeatedly after HAL_UART_EnableTxDMA() + * - use HAL_UART_DisableTxDMA() to disable UART transmit DMA mode if needed + * + * @param[in] uartID ID of the specified UART + * @param[in] buf Pointer to the data buffer + * @param[in] size Number of bytes to be transmitted + * @return Number of bytes transmitted, -1 on error + * + * @note This function is not thread safe. If using the UART transmit series + * functions in multi-thread, make sure they are executed exclusively. + * @note To transmit data in DMA mode, HAL_UART_EnableTxDMA() MUST be executed + * before calling this function. + */ +int32_t HAL_UART_Transmit_DMA(UART_ID uartID, uint8_t *buf, int32_t size) +{ + UART_T *uart; + UART_Private *priv; + int32_t left; + + UART_ASSERT_ID(uartID); + + if (buf == NULL || size <= 0) { + return -1; + } + + /* TODO: add protection */ + + uart = HAL_UART_GetInstance(uartID); + priv = UART_GetUartPriv(uartID); + + if (priv->txDMAChan == DMA_CHANNEL_INVALID) { + HAL_WRN("tx dma is disabled\n"); + return -1; + } + + HAL_DMA_Start(priv->txDMAChan, (uint32_t)buf, (uint32_t)&uart->RBR_THR_DLL.TX_HOLD, size); + HAL_SemaphoreWait(&priv->txSem, HAL_WAIT_FOREVER); + HAL_DMA_Stop(priv->txDMAChan); + left = HAL_DMA_GetByteCount(priv->txDMAChan); + + /* TODO: add protection */ + + return (size - left); +} + +/** + * @brief Receive an amount of data in DMA mode + * + * Steps to receive data in DMA mode: + * - use HAL_UART_EnableRxDMA() to enable UART receive DMA mode + * - use HAL_UART_Receive_DMA() to receive data, it can be called + * repeatedly after HAL_UART_EnableRxDMA() + * - use HAL_UART_DisableRxDMA() to disable UART receive DMA mode if needed + * + * @param[in] uartID ID of the specified UART + * @param[out] buf Pointer to the data buffer + * @param[in] size The maximum number of bytes to be received. + * The actual received bytes can be less than this. + * @param[in] msec Timeout value in millisecond to receive data. + * HAL_WAIT_FOREVER for no timeout. + * @return Number of bytes received, -1 on error + * + * @note This function is not thread safe. If using the UART receive series + * functions in multi-thread, make sure they are executed exclusively. + * @note To receive data in DMA mode, HAL_UART_EnableRxDMA() MUST be executed + * before calling this function. + */ +int32_t HAL_UART_Receive_DMA(UART_ID uartID, uint8_t *buf, int32_t size, uint32_t msec) +{ + UART_T *uart; + UART_Private *priv; + int32_t left; + + UART_ASSERT_ID(uartID); + + if (buf == NULL || size <= 0) { + return -1; + } + + /* TODO: add protection */ + + uart = HAL_UART_GetInstance(uartID); + priv = UART_GetUartPriv(uartID); + + if (priv->rxReadyCallback != NULL) { + HAL_WRN("rx callback is enabled\n"); + return -1; + } + + if (priv->rxDMAChan == DMA_CHANNEL_INVALID) { + HAL_WRN("rx dma is disabled\n"); + return -1; + } + + HAL_DMA_Start(priv->rxDMAChan, (uint32_t)&uart->RBR_THR_DLL.RX_BUF, (uint32_t)buf, size); + HAL_SemaphoreWait(&priv->rxSem, msec); + HAL_DMA_Stop(priv->rxDMAChan); + left = HAL_DMA_GetByteCount(priv->rxDMAChan); + + /* TODO: add protection */ + + return (size - left); +} + +/** + * @brief Transmit an amount of data in polling mode + * @param[in] uartID ID of the specified UART + * @param[in] buf Pointer to the data buffer + * @param[in] size Number of bytes to be transmitted + * @return Number of bytes transmitted, -1 on error + * + * @note This function is not thread safe. If using the UART transmit series + * functions in multi-thread, make sure they are executed exclusively. + */ +int32_t HAL_UART_Transmit_Poll(UART_ID uartID, uint8_t *buf, int32_t size) +{ + UART_T *uart; + uint8_t *ptr; + int32_t left; + + UART_ASSERT_ID(uartID); + + if (buf == NULL || size <= 0) { + return -1; + } + +#ifdef CONFIG_PM + if (g_uart_suspending & (1 << uartID)) { +#ifdef CONFIG_UART_PM_DEBUG + if (pm_print_index + size < (PM_UART_PRINT_BUF_LEN - 1)) { + memcpy(pm_print_buf + pm_print_index, buf, size); + pm_print_index += size; + } +#endif + return size; + } +#endif + + /* TODO: add protection */ + + uart = HAL_UART_GetInstance(uartID); + ptr = buf; + left = size; + while (left > 0) { + while (!HAL_UART_IsTxReady(uart)) { + ; /* wait FIFO become not full */ + } + + HAL_UART_PutTxData(uart, *ptr); + ++ptr; + --left; + } + + /* TODO: add protection */ + + return size; +} + +/** + * @brief Receive an amount of data in polling mode + * @param[in] uartID ID of the specified UART + * @param[out] buf Pointer to the data buffer + * @param[in] size The maximum number of bytes to be received. + * The actual received bytes can be less than this. + * @param[in] msec Timeout value in millisecond to receive data. + * HAL_WAIT_FOREVER for no timeout. + * @return Number of bytes received, -1 on error + * + * @note This function is not thread safe. If using the UART receive series + * functions in multi-thread, make sure they are executed exclusively. + */ +int32_t HAL_UART_Receive_Poll(UART_ID uartID, uint8_t *buf, int32_t size, uint32_t msec) +{ + UART_T *uart; + uint8_t *ptr; + int32_t left; + uint32_t endTick; + UART_Private *priv; + + UART_ASSERT_ID(uartID); + + if (buf == NULL || size <= 0) { + return -1; + } + + priv = UART_GetUartPriv(uartID); + if (priv->rxReadyCallback != NULL) { + HAL_WRN("rx callback is enabled\n"); + return -1; + } + + /* TODO: add protection */ + + uart = HAL_UART_GetInstance(uartID); + ptr = buf; + left = size; + + if (msec != HAL_WAIT_FOREVER) { + endTick = HAL_Ticks() + HAL_MSecsToTicks(msec); + } + while (left > 0) { + if (HAL_UART_IsRxReady(uart)) { + *ptr = HAL_UART_GetRxData(uart); + ++ptr; + --left; + } else if (msec != HAL_WAIT_FOREVER) { + if (HAL_TimeAfter(HAL_Ticks(), endTick)) { + break; + } + } + } + + /* TODO: add protection */ + + return (size - left); +} + +/** + * @brief Start or stop to transmit break characters + * @param[in] uartID ID of the specified UART + * @param[in] isSet + * @arg !0 Start to transmit break characters + * @arg 0 Stop to transmit break characters + * @return None + */ +void HAL_UART_SetBreakCmd(UART_ID uartID, int8_t isSet) +{ + UART_T *uart; + + UART_ASSERT_ID(uartID); + + uart = HAL_UART_GetInstance(uartID); + + if (isSet) { + HAL_SET_BIT(uart->LINE_CTRL, UART_BREAK_CTRL_BIT); + } else { + HAL_CLR_BIT(uart->LINE_CTRL, UART_BREAK_CTRL_BIT); + } +} diff --git a/platform/mcu/xr871/src/driver/chip/hal_util.c b/platform/mcu/xr871/src/driver/chip/hal_util.c new file mode 100644 index 0000000000..37c8434adc --- /dev/null +++ b/platform/mcu/xr871/src/driver/chip/hal_util.c @@ -0,0 +1,62 @@ +/** + * @file hal_util.c + * @author XRADIO IOT WLAN Team + */ + +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include "driver/chip/hal_rtc.h" + +/** + * @brief Provide accurate delay (in microsecond), and its accuracy is about + * 32 microseconds. + * @param[in] us Time (in microsecond) to delay + * @return None + * + * @note Avoid using this function to delay for a long time (longer than 10ms), + * because its execution will occupy a lot of CPU resource. + */ +void HAL_UDelay(uint32_t us) +{ +#if defined(__CONFIG_CHIP_XR871) + uint64_t expire; + + expire = us + HAL_RTC_GetFreeRunTime(); + while (expire > HAL_RTC_GetFreeRunTime()) + ; +#else /* __CONFIG_CHIP_XR871 */ + unsigned int cpu_clk, i; + + cpu_clk = HAL_PRCM_GetCPUAClk() / 2000; + for (i = 0; i < cpu_clk; i++) + i = i; +#endif /* __CONFIG_CHIP_XR871 */ +} diff --git a/platform/mcu/xr871/src/driver/chip/hal_wakeup.c b/platform/mcu/xr871/src/driver/chip/hal_wakeup.c new file mode 100644 index 0000000000..fd4ab8eb57 --- /dev/null +++ b/platform/mcu/xr871/src/driver/chip/hal_wakeup.c @@ -0,0 +1,364 @@ +/** + * @file hal_wakeup.c + * @author XRADIO IOT WLAN Team + */ + +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "sys/io.h" +#include "driver/chip/hal_wakeup.h" +#include "hal_base.h" + +#define HAL_DBG_WAKEUP 1 + +#if (HAL_DBG_WAKEUP == 1) +#define WK_INF(fmt, arg...) HAL_LOG(HAL_DBG_ON && 0, "[WK] "fmt, ##arg) +#define WK_WAR(fmt, arg...) HAL_LOG(HAL_DBG_ON && HAL_DBG_WAKEUP, "[WK] "fmt, ##arg) +#define WK_ERR(fmt, arg...) HAL_LOG(HAL_DBG_ON && HAL_DBG_WAKEUP, "[WK] "fmt, ##arg) +#else +#define WK_INF(fmt, arg...) +#define WK_WAR(fmt, arg...) +#define WK_ERR(fmt, arg...) +#endif + +#define WAKEUP_IRQn A_WAKEUP_IRQn +#define WAKEUP_GetTimerPending() HAL_PRCM_GetWakeupTimerPending() +#define WAKEUP_ClearTimerPending() HAL_PRCM_ClearWakeupTimerPending() +#define WAKEUP_GetTimerEnable() HAL_PRCM_GetWakeupTimerEnable() + +static uint32_t wakeup_event; + +#ifdef __CONFIG_ARCH_APP_CORE +static void Wakeup_ClrIO() +{ + HAL_PRCM_WakeupIODisableCfgHold((1< counter to wakeup system based on 32k counter. from + * WAKEUP_TIMER_MIN_TIME*32(WAKEUP_TIMER_MIN_TIME mS) to 134217727(4194.303S). + * retval 0 if success or other if failed. + */ +int32_t HAL_Wakeup_SetTimer(uint32_t count_32k) +{ +#ifdef WAKEUP_TIMER_CHECK_TIME + uint32_t current_count; + unsigned long flags; +#endif + + if ((count_32k < (32*WAKEUP_TIMER_MIN_TIME)) || (count_32k & PRCM_CPUx_WAKE_TIMER_EN_BIT)) + return -1; + +#ifdef WAKEUP_TIMER_CHECK_TIME + flags = arch_irq_save(); + current_count = HAL_PRCM_WakeupTimerGetCurrentValue(); + if (wakeup_time_back > current_count) + wakeup_time_back -= current_count; + else + WK_WAR("WAR:%s,%d\n", __func__, __LINE__); + + if (wakeup_time_back <= count_32k) { + arch_irq_restore(flags); + WK_WAR("ignor time set, bk:%d cu:%d\n", wakeup_time_back, count_32k); + return -1; + } + + wakeup_time_back = count_32k; + arch_irq_restore(flags); +#endif + Wakeup_DisTimer(); + HAL_PRCM_WakeupTimerSetCompareValue(count_32k); + HAL_PRCM_WakeupTimerEnable(); + + return 0; +} + +#ifdef __CONFIG_ARCH_APP_CORE +static uint32_t wakeup_io_en; +static uint32_t wakeup_io_mode; + +/** + * @brief Set wakeup IO enable and mode. + * @note This won't change IO config immediately, the enabled IO will be setted + * to input and wakeup mode before system enter lowpower mode. And the IO + * will be disabled after wakeup. So reinit IO if you want this IO used + * as other function. The IO will used as wakeup IO until be cleaned. + * @param pn: + * @arg pn-> 0~9. + * @param mode: + * @arg mode-> 0:negative edge, 1:positive edge. + * retval None. + */ +void HAL_Wakeup_SetIO(uint32_t pn, uint32_t mode) +{ + if (pn >= WAKEUP_IO_MAX || mode > 1) { + WK_ERR("%s,%d err\n", __func__, __LINE__); + return; + } + + /* mode */ + if (mode) + wakeup_io_mode |= BIT(pn); + else + wakeup_io_mode &= ~BIT(pn); + + /* enable */ + wakeup_io_en |= BIT(pn); +} + +/** + * @brief Clear wakeup IO enable. + * @param pn: + * @arg pn-> 0~9. + * retval None. + */ +void HAL_Wakeup_ClrIO(uint32_t pn) +{ + wakeup_io_en &= ~BIT(pn); +} + +static GPIO_Pin WakeIo_To_Gpio(uint32_t wkup_io) +{ + switch (wkup_io) { + case 0: return WAKEUP_IO0; + case 1: return WAKEUP_IO1; + case 2: return WAKEUP_IO2; + case 3: return WAKEUP_IO3; + case 4: return WAKEUP_IO4; + case 5: return WAKEUP_IO5; + case 6: return WAKEUP_IO6; + case 7: return WAKEUP_IO7; + case 8: return WAKEUP_IO8; + case 9: return WAKEUP_IO9; + } + + WK_ERR("%s,%d err!\n", __func__, __LINE__); + + return 0; +} + +/** + * @brief Set IO hold. + * @note Set all IO hold before poweroff to prevent IO output low level voltage. + * @param hold_io: + * @arg hold_io-> IO hold mask. + * retval 0 if success or other if failed. + */ +int32_t HAL_Wakeup_SetIOHold(uint32_t hold_io) +{ + /* clear */ + HAL_PRCM_WakeupIOClearEventDetected(HAL_PRCM_WakeupIOGetEventStatus()); + /* set hold */ + HAL_PRCM_WakeupIOEnableCfgHold(hold_io); + + return 0; +} +#endif + +/** + * @brief Config and enable wakeup io. + * retval 0 if success or other if failed. + */ +int32_t HAL_Wakeup_SetSrc(void) +{ +#ifdef __CONFIG_ARCH_APP_CORE + uint32_t i, wkio_input; +#endif + + /* check wakeup time */ + if (WAKEUP_GetTimerEnable() && + (HAL_PRCM_WakeupTimerGetCompareValue() <= (32*WAKEUP_TIMER_MIN_TIME))) { + WK_ERR("wakeup timer err:%x\n", HAL_PRCM_WakeupTimerGetCurrentValue()); + return -1; + } + +#ifdef __CONFIG_ARCH_APP_CORE + /* enable wakeup gpio if configed wakeup io */ + if (wakeup_io_en) { + wkio_input = wakeup_io_en; + for (i = 0; (i < WAKEUP_IO_MAX) && wkio_input; wkio_input >>= 1, i++) { + if (wkio_input & 0x01) { + GPIO_InitParam param; + + param.mode = GPIOx_Pn_F0_INPUT; + param.driving = GPIO_DRIVING_LEVEL_1; + param.pull = GPIO_PULL_UP; + WK_INF("init io:%d\n", WakeIo_To_Gpio(i)); + HAL_GPIO_Init(GPIO_PORT_A, WakeIo_To_Gpio(i), ¶m); /* set input */ + } + } + /* clear */ + HAL_PRCM_WakeupIOClearEventDetected(HAL_PRCM_WakeupIOGetEventStatus()); + /* mode */ + HAL_PRCM_WakeupIOSetRisingEvent(wakeup_io_mode); + /* enable */ + HAL_PRCM_WakeupIOEnable(wakeup_io_en); + /* enable global wakeup io */ + HAL_PRCM_WakeupIOEnableGlobal(); + /* set hold if this io enable */ + HAL_PRCM_WakeupIOEnableCfgHold(wakeup_io_en); + } +#endif + + NVIC_EnableIRQ(WAKEUP_IRQn); /* enable when sleep */ + + return 0; +} + +/** @brief Disable wakeup io. */ +void HAL_Wakeup_ClrSrc(void) +{ +#ifdef __CONFIG_ARCH_APP_CORE + uint32_t i, wkio_input; +#endif +#ifdef WAKEUP_TIMER_CHECK_TIME + unsigned long flags; +#endif + +#ifdef __CONFIG_ARCH_APP_CORE + /* wakeup io event */ + wakeup_event = HAL_PRCM_WakeupIOGetEventStatus(); +#else + wakeup_event = 0; +#endif + + /* wakeup timer event */ + if (WAKEUP_GetTimerPending()) { + WAKEUP_ClearTimerPending(); + while (WAKEUP_GetTimerPending()) + ; + wakeup_event |= PM_WAKEUP_SRC_WKTIMER; +#ifdef WAKEUP_TIMER_CHECK_TIME + flags = arch_irq_save(); + wakeup_time_back = 0xffffffff; + arch_irq_restore(flags); +#endif + } + + /* no events is net cpu sev */ + if (!wakeup_event) + wakeup_event = PM_WAKEUP_SRC_WKSEV; + +#ifdef __CONFIG_ARCH_APP_CORE + if (wakeup_io_en) { + wkio_input = wakeup_io_en; + for (i = 0; (i < WAKEUP_IO_MAX) && wkio_input; wkio_input >>= 1, i++) { + if (wkio_input & 0x01) { + HAL_GPIO_DeInit(GPIO_PORT_A, WakeIo_To_Gpio(i)); + WK_INF("deinit io:%d\n", i); + } + } + } +#endif + + NVIC_EnableIRQ(WAKEUP_IRQn); +} + + +/** + * @brief Get last wakeup event. + * retval Events defined in PM_WAKEUP_SRC_XX. + */ +uint32_t HAL_Wakeup_GetEvent(void) +{ + return wakeup_event; +} + +/** @brief Init wakeup IO and Timer as disable mode. */ +void HAL_Wakeup_Init(void) +{ + Wakeup_ClrTimer(); + Wakeup_DisTimer(); +#ifdef __CONFIG_ARCH_APP_CORE + Wakeup_ClrIO(); +#endif + + HAL_NVIC_SetIRQHandler(WAKEUP_IRQn, Wakeup_Source_Handler); + NVIC_ClearPendingIRQ(WAKEUP_IRQn); + NVIC_EnableIRQ(WAKEUP_IRQn); +} + +/** @brief Deinit wakeup IO and Timer. */ +void HAL_Wakeup_DeInit(void) +{ + NVIC_DisableIRQ(WAKEUP_IRQn); + +#ifdef __CONFIG_ARCH_APP_CORE + Wakeup_ClrIO(); +#endif + Wakeup_DisTimer(); +} diff --git a/platform/mcu/xr871/src/driver/chip/hal_wdg.c b/platform/mcu/xr871/src/driver/chip/hal_wdg.c new file mode 100644 index 0000000000..fcbcb46fcd --- /dev/null +++ b/platform/mcu/xr871/src/driver/chip/hal_wdg.c @@ -0,0 +1,257 @@ +/** + * @file hal_wdg.c + * @author XRADIO IOT WLAN Team + */ + +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "pm/pm.h" + +#include "driver/chip/hal_wdg.h" +#include "hal_base.h" + +/* Note: Has no 32000HZ clock source, use WDG_CLK_32768HZ by default */ +#define WDG_CLK_SRC_SHIFT 8 +#define WDG_CLK_SRC_MASK (0x1U << WDG_CLK_SRC_SHIFT) +typedef enum { + WDG_CLK_32000HZ = (0U << WDG_CLK_SRC_SHIFT), + WDG_CLK_32768HZ = (1U << WDG_CLK_SRC_SHIFT) +} WDG_ClkSrc; + +#define WDG_RELOAD_VAL ((0xA57 << 1) | 1) + +typedef struct { + WDG_IRQCallback callback; + void *arg; +} WDG_Private; + +static WDG_Private gWdgPrivate; + +static void WDG_EnableIRQ(void) +{ + HAL_SET_BIT(WDG->IRQ_EN, WDG_IRQ_EN_BIT); +} + +static void WDG_DisableIRQ(void) +{ + HAL_CLR_BIT(WDG->IRQ_EN, WDG_IRQ_EN_BIT); +} + +static int WDG_IsPendingIRQ(void) +{ + return HAL_GET_BIT(WDG->IRQ_STATUS, WDG_IRQ_PENDING_BIT); +} + +static void WDG_ClearPendingIRQ(void) +{ + HAL_SET_BIT(WDG->IRQ_STATUS, WDG_IRQ_PENDING_BIT); +} + +void WDG_IRQHandler(void) +{ + if (WDG_IsPendingIRQ()) { + WDG_ClearPendingIRQ(); + if (gWdgPrivate.callback) { + gWdgPrivate.callback(gWdgPrivate.arg); + } + } +} + +#ifdef CONFIG_PM +static WDG_InitParam hal_wdg_param; +static uint32_t hal_wdg_suspending = 0; +static uint32_t hal_wdg_runing = 0; + +static int wdg_suspend(struct soc_device *dev, enum suspend_state_t state) +{ + hal_wdg_suspending = 1; + + HAL_WDG_DeInit(); + //HAL_DBG("%s okay\n", __func__); + + return 0; +} + +static int wdg_resume(struct soc_device *dev, enum suspend_state_t state) +{ + HAL_WDG_Init(&hal_wdg_param); + //HAL_DBG("%s okay\n", __func__); + + if (hal_wdg_runing) + HAL_WDG_Start(); + else + HAL_WDG_Stop(); + + hal_wdg_suspending = 0; + + return 0; +} + +static struct soc_device_driver wdg_drv = { + .name = "wdg", + .suspend = wdg_suspend, + .resume = wdg_resume, +}; + +static struct soc_device wdg_dev = { + .name = "wdg", + .driver = &wdg_drv, +}; + +#define WDG_DEV (&wdg_dev) +#endif + +/** + * @brief Initialize the watchdog according to the specified parameters + * @param[in] param Pointer to WDG_InitParam structure + * @retval HAL_Status, HAL_OK on success + */ +HAL_Status HAL_WDG_Init(const WDG_InitParam *param) +{ + /* enable clock */ + + /* release reset */ + + /* init parameters */ + WDG->MODE = param->timeout; /* NB: it will clear WDG_IRQ_EN_BIT (stop WDG) */ + WDG->CFG = param->event | WDG_CLK_32768HZ; + + if (param->event == WDG_EVT_RESET) { + WDG->RESET_CTRL = ((param->resetCycle << WDG_RESET_CYCLE_SHIFT) & + WDG_RESET_CYCLE_MASK); + } else if (param->event == WDG_EVT_INTERRUPT) { + gWdgPrivate.callback = param->callback; + gWdgPrivate.arg = param->arg; + if (WDG_IsPendingIRQ()) { + WDG_ClearPendingIRQ(); + } + WDG_EnableIRQ(); + HAL_NVIC_SetPriority(WDG_IRQn, NVIC_PERIPHERAL_PRIORITY_DEFAULT); + HAL_NVIC_EnableIRQ(WDG_IRQn); + } else { + HAL_ERR("Invalid event type 0x%x\n", param->event); + return HAL_ERROR; + } + +#ifdef CONFIG_PM + if (!hal_wdg_suspending) { + memcpy(&hal_wdg_param, param, sizeof(WDG_InitParam)); + pm_register_ops(WDG_DEV); + } +#endif + + return HAL_OK; +} + +/** + * @brief DeInitialize the watchdog + * @retval HAL_Status, HAL_OK on success + */ +HAL_Status HAL_WDG_DeInit(void) +{ + HAL_WDG_Stop(); + +#ifdef CONFIG_PM + if (!hal_wdg_suspending) { + pm_unregister_ops(WDG_DEV); + } +#endif + + /* disable IRQ */ + HAL_NVIC_DisableIRQ(WDG_IRQn); + WDG_DisableIRQ(); + if (WDG_IsPendingIRQ()) { + WDG_ClearPendingIRQ(); + } + + gWdgPrivate.callback = NULL; + gWdgPrivate.arg = NULL; + + /* force reset */ + + /* disable clock */ + + return HAL_OK; +} + +/** + * @brief Feed the watchdog + * @note When watchdog running, reset system or IRQ event will be triggered if + * no feeding executed in the interval configured by HAL_WDG_Init(). + * @return None + */ +void HAL_WDG_Feed(void) +{ + WDG->CTRL = WDG_RELOAD_VAL; +} + +/** + * @brief Start the watchdog + * @return None + */ +void HAL_WDG_Start(void) +{ + HAL_WDG_Feed(); +#ifdef CONFIG_PM + if (!hal_wdg_suspending) + hal_wdg_runing = 1; +#endif + HAL_SET_BIT(WDG->MODE, WDG_EN_BIT); +} + +/** + * @brief Stop the watchdog + * @return None + */ +void HAL_WDG_Stop(void) +{ + HAL_CLR_BIT(WDG->MODE, WDG_EN_BIT); +#ifdef CONFIG_PM + if (!hal_wdg_suspending) + hal_wdg_runing = 0; +#endif +} + +/** + * @brief Reboot system using the watchdog + * @return None + */ +void HAL_WDG_Reboot(void) +{ + HAL_DisableIRQ(); + HAL_PRCM_DisableSys2(); + HAL_PRCM_DisableSys2Power(); + WDG->MODE = WDG_TIMEOUT_500MS; /* NB: it will clear WDG_IRQ_EN_BIT (stop WDG) */ + WDG->CFG = WDG_EVT_RESET | WDG_CLK_32768HZ; + WDG->RESET_CTRL = ((WDG_DEFAULT_RESET_CYCLE << WDG_RESET_CYCLE_SHIFT) & + WDG_RESET_CYCLE_MASK); + HAL_WDG_Start(); + while (1) ; +} diff --git a/platform/mcu/xr871/src/driver/chip/hal_xip.c b/platform/mcu/xr871/src/driver/chip/hal_xip.c new file mode 100644 index 0000000000..1266740aff --- /dev/null +++ b/platform/mcu/xr871/src/driver/chip/hal_xip.c @@ -0,0 +1,245 @@ +/** + * @file hal_xip.c + * @author XRADIO IOT WLAN Team + */ + +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include "hal_base.h" +#include "driver/chip/hal_flashctrl.h" +#include "driver/chip/hal_flash.h" +#include "driver/chip/hal_xip.h" +#include "hal_os.h" +#include "sys/param.h" + +#include "pm/pm.h" + + +#define XIP_DEBUG(msg, arg...) XR_DEBUG((DBG_OFF | XR_LEVEL_ERROR), NOEXPAND, "[XIP iface debug] <%s : %d> " msg "\n", __func__, __LINE__, ##arg) +#define XIP_ERROR(msg, arg...) XR_ERROR((DBG_ON | XR_LEVEL_ALL), NOEXPAND, "[XIP iface error] <%s : %d> " msg "\n", __func__, __LINE__, ##arg) + +FlashChipBase *getFlashChip(FlashDev *dev); +FlashReadMode getFlashMode(FlashDev *dev); +FlashDev *getFlashDev(uint32_t flash); +void insToFcIns(InstructionField *ins, FC_InstructionField *fcins); +static int setCmd(XipDriverBase *base, InstructionField *cmd, InstructionField *addr, InstructionField *dummy, InstructionField *data); +static int setContinue(XipDriverBase *base, uint32_t continueMode, void *arg); +static int setDelay(XipDriverBase *base, Flashc_Delay *delay); +static int setAddr(XipDriverBase *base, uint32_t addr); +/*void HAL_Flashc_Xip_RawEnable(); +void HAL_Flashc_Xip_Enable(); +void HAL_Flashc_Xip_RawDisable(); +void HAL_Flashc_Xip_Disable();*/ + +static XipDriverBase xipDrv = {0}; +#ifdef CONFIG_PM +static struct soc_device Xip_dev; +#endif + + +/** + * @brief Initializes XIP module. + * @note XIP is a module that cpu can run the code in flash but not ram. + * @param flash: flash number, this flash must have been initialized, and must + * be connected to the flash controller pin. + * @param xaddr: XIP code start address. + * @retval HAL_Status: The status of driver + */ +HAL_Status HAL_Xip_Init(uint32_t flash, uint32_t xaddr) +{ + HAL_Status ret = HAL_OK; + FlashDev *dev; + FlashChipBase *chip; + FlashBoardCfg *cfg; + int devNum = HAL_MKDEV(HAL_DEV_MAJOR_FLASH, flash); + + HAL_BoardIoctl(HAL_BIR_GET_CFG, devNum, (uint32_t)&cfg); + if (cfg->type != FLASH_DRV_FLASHC) + return HAL_INVALID; + + dev = getFlashDev(flash); + if (dev == NULL) + return HAL_INVALID; + + xipDrv.flash = flash; + xipDrv.dev = dev; + xipDrv.setAddr = setAddr; + xipDrv.setCmd = setCmd; + xipDrv.setContinue = setContinue; + xipDrv.setDelay = setDelay; + HAL_Memset(&xipDrv.mCfg, 0, sizeof(xipDrv.mCfg)); + + chip = getFlashChip(dev); + + chip->mXip = &xipDrv; + + chip->enableXIP(chip); + chip->xipDriverCfg(chip, getFlashMode(dev)); + xipDrv.mCfg.freq = cfg->flashc.clk; + xipDrv.mCfg.addr = xaddr; + + HAL_Flashc_Xip_Init(&xipDrv.mCfg); + +#ifdef CONFIG_PM + pm_register_ops(&Xip_dev); +#endif + + return ret; +} + +/** + * @brief Deinitializes XIP module. + * @retval HAL_Status: The status of driver + */ +HAL_Status HAL_Xip_Deinit(void) +{ + HAL_Status ret = HAL_OK; + FlashChipBase *chip = getFlashChip(xipDrv.dev); + +#ifdef CONFIG_PM + pm_unregister_ops(&Xip_dev); +#endif + + HAL_Flashc_Xip_Deinit(); + chip->disableXIP(chip); + + return ret; +} +/* +static HAL_Status HAL_XIP_Enable() +{ + +} + +static HAL_Status HAL_XIP_Disable() +{ + +}*/ + +static int setCmd(struct XipDriverBase *base, InstructionField *cmd, InstructionField *addr, InstructionField *dummy, InstructionField *data) +{ + FC_InstructionField tmp; + XIP_Instruction *ins = &base->mCfg.ins; + + insToFcIns(cmd, &tmp); + ins->cmd = *tmp.pdata; + ins->cmd_line = tmp.line; + + insToFcIns(addr, &tmp); + ins->addr_line = tmp.line; + + insToFcIns(dummy, &tmp); + ins->dummy_line = tmp.line; + ins->dum_btyes = tmp.len; + + insToFcIns(data, &tmp); + ins->data_line = tmp.line; + + return 0; +} + +static int setContinue(struct XipDriverBase *base, uint32_t continueMode, void *arg) +{ + base->mCfg.cont_mode = continueMode; + + return 0; +} + +static int setDelay(struct XipDriverBase *base, Flashc_Delay *delay) +{ + /*TODO: how and where should be the delay calculation*/ + + return 0; +} + +static int setAddr(struct XipDriverBase *base, uint32_t addr) +{ + base->mCfg.addr = addr; + + return 0; +} + + +#ifdef CONFIG_PM +//#define FLASH_POWERDOWN (PM_MODE_POWEROFF) + +static int PM_XipSuspend(struct soc_device *dev, enum suspend_state_t state) +{ + FlashChipBase *chip = getFlashChip(xipDrv.dev); + + switch (state) { + case PM_MODE_SLEEP: + break; + case PM_MODE_STANDBY: + case PM_MODE_HIBERNATION: + case PM_MODE_POWEROFF: + chip->disableXIP(chip); + break; + default: + break; + } + + return 0; +} + +static int PM_XipResume(struct soc_device *dev, enum suspend_state_t state) +{ + FlashChipBase *chip = getFlashChip(xipDrv.dev); + + switch (state) { + case PM_MODE_SLEEP: + break; + case PM_MODE_STANDBY: + case PM_MODE_HIBERNATION: + case PM_MODE_POWEROFF: + chip->enableXIP(chip); + break; + default: + break; + } + + return 0; +} + +static struct soc_device_driver Xip_drv = { + .name = "Xip", + .suspend = PM_XipSuspend, + .resume = PM_XipResume, +}; + +static struct soc_device Xip_dev = { + .name = "Xip", + .driver = &Xip_drv, +}; + +#endif + diff --git a/platform/mcu/xr871/src/driver/chip/ir_nec.c b/platform/mcu/xr871/src/driver/chip/ir_nec.c new file mode 100644 index 0000000000..f376b06dbb --- /dev/null +++ b/platform/mcu/xr871/src/driver/chip/ir_nec.c @@ -0,0 +1,284 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "sys/xr_debug.h" +#include "driver/chip/ir_nec.h" +#include "hal_base.h" + +#define HAL_DBG_IRNEC 0 + +#if (HAL_DBG_IRNEC == 1) +#define IRNEC_DBG(fmt, arg...) HAL_LOG(HAL_DBG_ON && HAL_DBG_IRNEC, "[IRNEC] "fmt, ##arg) +#define irnec_hex_dump_bytes(...) print_hex_dump_bytes(__VA_ARGS__) +#define IRNEC_INF IRNEC_DBG +#define IRNEC_WRN HAL_WRN +#define IRNEC_ERR HAL_ERR +#else +#define irnec_hex_dump_bytes(...) +#define IRNEC_INF(...) +#define IRNEC_WRN(...) +#define IRNEC_ERR(...) +#endif + +/** + * @brief DePacket code by NEC protocal. + * @param buf: + * @arg buf->[in]Raw code buffer. + * @param dcnt: + * @arg dcnt->[in] Num of Raw code. + * @retval DePacket code if success or 0xffffffff if depacket failed. + */ +uint32_t IRRX_NECPacket_DeCode(uint8_t *buf, uint32_t dcnt) +{ + uint32_t len; + uint8_t val = 0, last; + uint32_t code = 0; + int32_t bitCnt = 0; + uint32_t i = 0; + uint32_t active_delay = 0; + uint32_t irrx_l1_min = 0, irrx_l0_min = 0, irrx_pmax = 0, irrx_dmid = 0, irrx_dmax = 0; +#if !defined (IR_CLK_32K_USED) + uint32_t clk = HAL_GetHFClock(); +#endif + + irnec_hex_dump_bytes(buf, dcnt); + + IRNEC_INF("dcnt = %d\n", dcnt); + + /* Find Lead '1' */ +#if defined (IR_CLK_32K_USED) + active_delay = (IRRX_32K_ACTIVE_T + 1) * (IRRX_32K_ACTIVE_T_C ? 128 : 1); + irrx_l1_min = IRRX_32K_L1_MIN; + irrx_l0_min = IRRX_32K_L0_MIN; + irrx_pmax = IRRX_32K_PMAX; + irrx_dmid = IRRX_32K_DMID; + irrx_dmax = IRRX_32K_DMAX; +#else + if (clk == HOSC_CLOCK_26M) { + active_delay = (IRRX_26M_ACTIVE_T + 1) * (IRRX_26M_ACTIVE_T_C ? 128 : 1); + irrx_l1_min = IRRX_26M_L1_MIN; + irrx_l0_min = IRRX_26M_L0_MIN; + irrx_pmax = IRRX_26M_PMAX; + irrx_dmid = IRRX_26M_DMID; + irrx_dmax = IRRX_26M_DMAX; + } else if (clk == HOSC_CLOCK_24M) { + active_delay = (IRRX_24M_ACTIVE_T + 1) * (IRRX_24M_ACTIVE_T_C ? 128 : 1); + irrx_l1_min = IRRX_24M_L1_MIN; + irrx_l0_min = IRRX_24M_L0_MIN; + irrx_pmax = IRRX_24M_PMAX; + irrx_dmid = IRRX_24M_DMID; + irrx_dmax = IRRX_24M_DMAX; + } +#endif + //IRNEC_INF("active_delay = %d\n", active_delay); + len = 0; + len += (active_delay >> 1); + for (i = 0; i < dcnt; i++) { + val = buf[i]; + if (val & 0x80) { + len += val & 0x7f; + } else { + if (len > irrx_l1_min) + break; + len = 0; + } + } + + //IRNEC_INF("'1' len = %d\n", len); + + if ((val & 0x80) || (len <= irrx_l1_min)){ + IRNEC_INF("start 1 error code %d\n", len); + goto error_code; + } + + /* Find Lead '0' */ + len = 0; + for (; i < dcnt; i++) { + val = buf[i]; + if (val & 0x80) { + if (len > irrx_l0_min) + break; + len = 0; + } else { + len += val & 0x7f; + } + } + + //IRNEC_INF("'0' len = %d\n", len); + + if ((!(val & 0x80)) || (len <= irrx_l0_min)){ + IRNEC_INF("start 0 error code %d\n", len); + goto error_code; + } + + /* go decoding */ + code = 0; /* 0 for Repeat Code */ + bitCnt = 0; + last = 1; + len = 0; + for (; i < dcnt; i++) { + val = buf[i]; + if (last) { + if (val & 0x80) { + len += val & 0x7f; + } else { + if (len > irrx_pmax) { /* Error Pulse */ + IRNEC_INF("len:%d > IRRX_PMAX @%d\n", len, i); + goto error_code; + } + last = 0; + len = val & 0x7f; + } + } else { + if (val & 0x80) { + if (len > irrx_dmax){ /* Error Distant */ + IRNEC_INF("len:%d > IRRX_DMAX @%d\n", len, i); + goto error_code; + } else { + if (len > irrx_dmid) { + /* data '1'*/ + code |= 1 << bitCnt; + } + bitCnt++; + if (bitCnt == 32) + break; /* decode over */ + } + last = 1; + len = val & 0x7f; + } else { + len += val & 0x7f; + } + } + } + return code; + +error_code: + irnec_hex_dump_bytes(buf, dcnt); + + return (uint32_t)-1; +} + +int32_t IRRX_NECCode_Valid(uint32_t code) +{ + uint32_t tmp1, tmp2; + +#ifdef IRRX_CHECK_ADDR_CODE + /* Check Address Value */ + if ((code & 0xffff) != (IRRX_ADDR_CODE & 0xffff)) + return 0; /* Address Error */ + + tmp1 = code & 0x00ff0000; + tmp2 = (code & 0xff000000) >> 8; + + return ((tmp1 ^ tmp2) == 0x00ff0000); /* Check User Code */ +#else + /* Do Not Check Address Value */ + tmp1 = code & 0x00ff00ff; + tmp2 = (code & 0xff00ff00) >> 8; + + return (((tmp1 ^ tmp2) & 0x00ff0000) == 0x00ff0000); +#endif +} + +/** + * @brief Packet code by NEC protocal. + * @param txBuff: + * @arg txBuff->[out] Raw code will put in. + * @param ir_tx_code: + * @arg ir_tx_code->[in] The code will be Packeted. + * @retval Raw code num. + */ +uint32_t IRTX_NECPacket_Code(uint8_t *txBuff, uint32_t ir_tx_code) +{ + uint32_t i, j; + uint32_t txCnt = 0; + uint8_t tx_code[4]; +#if !defined (IR_CLK_32K_USED) + uint32_t clk = HAL_GetHFClock(); +#endif + uint32_t irtx_560us_num = 0, irtx_1680us_num = 0; + + tx_code[0] = (uint8_t)(ir_tx_code >> 24); + tx_code[1] = (uint8_t)(ir_tx_code >> 16); + tx_code[2] = (uint8_t)(ir_tx_code >> 8); + tx_code[3] = (uint8_t)(ir_tx_code & 0xff); + + /* go encoding */ +#if defined (IR_CLK_32K_USED) + txBuff[txCnt++] = 0x80|IRTX_32K_9MS_NUM1; /* S1:9ms */ + txBuff[txCnt++] = 0x80|IRTX_32K_9MS_NUM2; + txBuff[txCnt++] = IRTX_32K_4P5MS_NUM1; /* S0:4.5ms */ + irtx_560us_num = IRTX_32K_560US_NUM; + irtx_1680us_num = IRTX_32K_1680US_NUM; +#else + if (clk == HOSC_CLOCK_26M) { + txBuff[txCnt++] = 0x80|IRTX_26M_9MS_NUM1; /* S1:9ms */ + txBuff[txCnt++] = 0x80|IRTX_26M_9MS_NUM2; + txBuff[txCnt++] = IRTX_26M_4P5MS_NUM1; /* S0:4.5ms */ + irtx_560us_num = IRTX_26M_560US_NUM; + irtx_1680us_num = IRTX_26M_1680US_NUM; + } else if (clk == HOSC_CLOCK_24M) { + txBuff[txCnt++] = 0x80|IRTX_24M_9MS_NUM1; /* S1:9ms */ + txBuff[txCnt++] = 0x80|IRTX_24M_9MS_NUM2; + txBuff[txCnt++] = IRTX_24M_4P5MS_NUM1; /* S0:4.5ms */ + irtx_560us_num = IRTX_24M_560US_NUM; + irtx_1680us_num = IRTX_24M_1680US_NUM; + } +#endif + + for (j = 0; j < 4; j++) { + for (i = 0; i < 8; i++ ) { + if (tx_code[j] & 0x01) { + /* L1:560uS, 1680uS */ + txBuff[txCnt++] = 0x80|irtx_560us_num; + txBuff[txCnt++] = irtx_1680us_num; + } else { + /* L0:560uS, 560uS */ + txBuff[txCnt++] = 0x80|irtx_560us_num; + txBuff[txCnt++] = irtx_560us_num; + } + tx_code[j] = tx_code[j] >> 1; + } + } + txBuff[txCnt++] = 0x80|irtx_560us_num; + txBuff[txCnt++] = irtx_560us_num; /* add for set gpio to 0 */ + txBuff[txCnt++] = 0x7f; + txBuff[txCnt++] = 0x7f; + txBuff[txCnt++] = 0x7f; + txBuff[txCnt++] = 0x7f; + + IRNEC_INF("%s: tx_dcnt = %d\n", __func__, txCnt); + irnec_hex_dump_bytes(txBuff, txCnt); + + return txCnt; +} + +#undef IRNEC_INF +#undef IRNEC_WRN +#undef IRNEC_ERR diff --git a/platform/mcu/xr871/src/driver/chip/sdmmc/_sd.h b/platform/mcu/xr871/src/driver/chip/sdmmc/_sd.h new file mode 100644 index 0000000000..68af307f0e --- /dev/null +++ b/platform/mcu/xr871/src/driver/chip/sdmmc/_sd.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _DRIVER_CHIP_SDMMC_SD_H_ +#define _DRIVER_CHIP_SDMMC_SD_H_ + +#include "core.h" + +#ifdef __cplusplus +extern "C" { +#endif + +extern int32_t mmc_wait_for_app_cmd(struct mmc_host *host, struct mmc_card *card, struct mmc_command *cmd); +extern int32_t mmc_app_cmd(struct mmc_host *host, struct mmc_card *card); +extern int32_t mmc_send_relative_addr(struct mmc_host *host, uint32_t *rca); +extern int32_t mmc_send_if_cond(struct mmc_host *host, uint32_t ocr); +extern int32_t mmc_app_sd_status(struct mmc_card *card, uint8_t *ssr); +extern uint32_t mmc_sd_get_max_clock(struct mmc_card *card); + +#ifdef __cplusplus +} +#endif + +#endif /* _DRIVER_CHIP_SDMMC_SD_H_ */ diff --git a/platform/mcu/xr871/src/driver/chip/sdmmc/core.c b/platform/mcu/xr871/src/driver/chip/sdmmc/core.c new file mode 100644 index 0000000000..78d01bde82 --- /dev/null +++ b/platform/mcu/xr871/src/driver/chip/sdmmc/core.c @@ -0,0 +1,970 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "../hal_base.h" + +#include "driver/chip/sdmmc/hal_sdhost.h" +#include "driver/chip/sdmmc/sdmmc.h" +#ifdef CONFIG_USE_SDIO +#include "driver/chip/sdmmc/sdio.h" +#endif +#include "sdhost.h" +#include "core.h" +#ifdef CONFIG_USE_SDIO +#include "_sdio.h" +#endif +#ifdef CONFIG_USE_SD +#include "_sd.h" +#endif +#ifdef CONFIG_USE_MMC +#include "_mmc.h" +#endif + +/** + * mmc_wait_for_req - start a request and wait for completion + * @host: MMC host to start command + * @mrq: MMC request to start + * + * Start a new MMC custom command request for a host, and wait + * for the command to complete. Does not attempt to parse the + * response. + */ +int32_t mmc_wait_for_req(struct mmc_host *host, struct mmc_request *mrq) +{ + mrq->cmd->data = mrq->data; + return HAL_SDC_Request(host, mrq); +} + +/** + * mmc_wait_for_cmd - start a command and wait for completion + * @host: MMC host to start command + * @cmd: MMC command to start + * @retries: maximum number of retries + * + * Start a new MMC command for a host, and wait for the command + * to complete. Return any error that occurred while the command + * was executing. Do not attempt to parse the response. + */ +int32_t mmc_wait_for_cmd(struct mmc_host *host, struct mmc_command *cmd) +{ + struct mmc_request mrq = {0}; + + memset(cmd->resp, 0, sizeof(cmd->resp)); + + mrq.cmd = cmd; + + return mmc_wait_for_req(host, &mrq); +} + +/** + * mmc_align_data_size - pads a transfer size to a more optimal value + * @card: the MMC card associated with the data transfer + * @sz: original transfer size + * + * Pads the original data size with a number of extra bytes in + * order to avoid controller bugs and/or performance hits + * (e.g. some controllers revert to PIO for certain sizes). + * + * Returns the improved size, which might be unmodified. + * + * Note that this function is only relevant when issuing a + * single scatter gather entry. + */ +int32_t mmc_align_data_size(struct mmc_card *card, uint32_t sz) +{ + /* + * FIXME: We don't have a system for the controller to tell + * the core about its problems yet, so for now we just 32-bit + * align the size. + */ + sz = ((sz + 3) / 4) * 4; + + return sz; +} + +static inline void mmc_host_clk_hold(struct mmc_host *host) +{ + HAL_SDC_Clk_PWR_Opt(host, 1, 0); +} + +static inline void mmc_host_clk_release(struct mmc_host *host) +{ + HAL_SDC_Clk_PWR_Opt(host, 0, 0); +} + +/* + * Apply power to the MMC stack. This is a two-stage process. + * First, we enable power to the card without the clock running. + * We then wait a bit for the power to stabilise. Finally, + * enable the bus drivers and clock to the card. + * + * We must _NOT_ enable the clock prior to power stablising. + * + * If a host does all the power sequencing itself, ignore the + * initial MMC_POWER_UP stage. + */ +static void mmc_power_up(struct mmc_host *host) +{ + //int bit; + + mmc_host_clk_hold(host); + + /* If ocr is set, we use it */ + //if (host->ocr) + // bit = ffs(host->ocr) - 1; + //else + // bit = fls(host->ocr_avail) - 1; + + HAL_SDC_PowerOn(host); + + /* + * This delay must be at least 74 clock sizes, or 1 ms, or the + * time required to reach a stable voltage. + */ + mmc_mdelay(10); + + mmc_host_clk_release(host); +} + +static void mmc_power_off(struct mmc_host *host) +{ + mmc_host_clk_hold(host); + + /* + * For eMMC 4.5 device send AWAKE command before + * POWER_OFF_NOTIFY command, because in sleep state + * eMMC 4.5 devices respond to only RESET and AWAKE cmd + */ + //if (host->card && mmc_card_is_sleep(host->card)) { + // mmc_poweroff_notify(host); + //} + + /* + * Reset ocr mask to be the highest possible voltage supported for + * this mmc host. This value will be used at next power up. + */ + //host->ocr = 1 << (fls(host->ocr_avail) - 1); + + HAL_SDC_PowerOff(host); + /* + * Some configurations, such as the 802.11 SDIO card in the OLPC + * XO-1.5, require a short delay after poweroff before the card + * can be successfully turned on again. + */ + mmc_mdelay(1); + + mmc_host_clk_release(host); +} + +static int32_t mmc_go_idle(struct mmc_host *host) +{ + int32_t err; + struct mmc_command cmd = {0}; + + cmd.opcode = MMC_GO_IDLE_STATE; + cmd.arg = 0; + cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_NONE | MMC_CMD_BC; + + err = mmc_wait_for_cmd(host, &cmd); + + mmc_mdelay(1); + + return err; +} + +int32_t mmc_send_status(struct mmc_card *card, uint32_t *status) +{ + int32_t err; + struct mmc_command cmd = {0}; + + cmd.opcode = MMC_SEND_STATUS; + cmd.arg = card->rca << 16; + cmd.flags = MMC_RSP_SPI_R2 | MMC_RSP_R1 | MMC_CMD_AC; + + err = mmc_wait_for_cmd(card->host, &cmd); + if (err) + return err; + + /* NOTE: callers are required to understand the difference + * between "native" and SPI format status words! + */ + if (status) + *status = cmd.resp[0]; + + return 0; +} + +#if ((defined CONFIG_USE_SD) || (defined CONFIG_USE_MMC)) +int32_t mmc_sd_switch(struct mmc_card *card, uint8_t mode, uint8_t group, + uint16_t value, uint8_t *resp) +{ + struct mmc_request mrq; + struct mmc_command cmd = {0}; + struct mmc_data data = {0}; + struct scatterlist sg; + + /* NOTE: caller guarantees resp is heap-allocated */ + + mode = !!mode; + value &= 0xF; + + mrq.cmd = &cmd; + mrq.data = &data; + + cmd.opcode = SD_SWITCH; + cmd.arg = mode << 31 | 0x00FFFFFF; + cmd.arg &= ~(0xF << (group * 4)); + cmd.arg |= value << (group * 4); + cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC; + + data.blksz = 64; + data.blocks = 1; + data.flags = MMC_DATA_READ; + data.sg = &sg; + data.sg_len = 1; + + sg.len = 64; + sg.buffer = resp; + + if (mmc_wait_for_req(card->host, &mrq)) { + return -1; + } + + return 0; +} + +static int32_t mmc_switch(struct mmc_card *card, uint8_t set, uint8_t index, uint8_t value) +{ + struct mmc_command cmd = {0}; + int32_t ret; + uint32_t status = 0; + + cmd.opcode = MMC_SWITCH; + cmd.arg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) | (index << 16) | (value << 8) | set; + cmd.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC; + + if (mmc_wait_for_cmd(card->host, &cmd)) { + return -1; + } + + /* Must check status to be sure of no errors */ + do { + ret = mmc_send_status(card, &status); + if (ret) + return ret; + } while (R1_CURRENT_STATE(status) == R1_STATE_PRG); + + if (status & 0xFDFFA000) + SD_LOGW("unexpected status %x after switch", status); + if (status & R1_SWITCH_ERROR) + return -1; + + return 0; +} + +int32_t mmc_switch_part(struct mmc_card *card, uint32_t part_num) +{ + return mmc_switch(card, MMC_EXT_CSD_CMD_SET_NORMAL, MMC_EXT_CSD_PART_CONF, + (card->extcsd.part_config & ~MMC_SWITCH_PART_ACCESS_MASK) + | (part_num & MMC_SWITCH_PART_ACCESS_MASK)); +} + +int32_t mmc_switch_boot_part(struct mmc_card *card, uint32_t boot_ack, uint32_t boot_part) +{ + return mmc_switch(card, MMC_EXT_CSD_CMD_SET_NORMAL, MMC_EXT_CSD_PART_CONF, + (card->extcsd.part_config & (~MMC_SWITCH_PART_BOOT_PART_MASK) & (~MMC_SWITCH_PART_BOOT_ACK_MASK)) + | ((boot_part << 3) & MMC_SWITCH_PART_BOOT_PART_MASK) | (boot_ack << 6)); +} + +int32_t mmc_switch_boot_bus_cond(struct mmc_card *card, uint32_t boot_mode, uint32_t rst_bus_cond, uint32_t bus_width) +{ + return mmc_switch(card, MMC_EXT_CSD_CMD_SET_NORMAL, MMC_EXT_CSD_BOOT_BUS_COND, + (card->extcsd.boot_bus_cond & + (~MMC_SWITCH_BOOT_MODE_MASK) & + (~MMC_SWITCH_BOOT_RST_BUS_COND_MASK) & + (~MMC_SWITCH_BOOT_BUS_WIDTH_MASK)) + | ((boot_mode << 3) & MMC_SWITCH_BOOT_MODE_MASK) + | ((rst_bus_cond << 2) & MMC_SWITCH_BOOT_RST_BUS_COND_MASK) + | ((bus_width) & MMC_SWITCH_BOOT_BUS_WIDTH_MASK) ); +} + +int32_t mmc_app_cmd(struct mmc_host *host, struct mmc_card *card) +{ + int32_t err; + struct mmc_command cmd = {0}; + + SD_BUG_ON(!host); + SD_BUG_ON(card && (card->host != host)); + + cmd.opcode = MMC_APP_CMD; + + if (card) { + cmd.arg = card->rca << 16; + cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_AC; + } else { + cmd.arg = 0; + cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_BCR; + } + + err = mmc_wait_for_cmd(host, &cmd); + if (err) + return err; + + /* Check that card supported application commands */ + if (!(cmd.resp[0] & R1_APP_CMD)) + return -1; + + return 0; +} + +/** + * mmc_wait_for_app_cmd - start an application command and wait for + * completion + * @host: MMC host to start command + * @card: Card to send MMC_APP_CMD to + * @cmd: MMC command to start + * + * Sends a MMC_APP_CMD, checks the card response, sends the command + * in the parameter and waits for it to complete. Return any error + * that occurred while the command was executing. Do not attempt to + * parse the response. + */ +int32_t mmc_wait_for_app_cmd(struct mmc_host *host, struct mmc_card *card, + struct mmc_command *cmd) +{ + struct mmc_request mrq = {NULL}; + + int32_t i, err; + + SD_BUG_ON(!cmd); + + err = -1; + + /* + * We have to resend MMC_APP_CMD for each attempt so + * we cannot use the retries field in mmc_command. + */ + for (i = 0; i <= MMC_CMD_RETRIES; i++) { + err = mmc_app_cmd(host, card); + if (err) { + continue; + } + + memset(&mrq, 0, sizeof(struct mmc_request)); + + memset(cmd->resp, 0, sizeof(cmd->resp)); + + mrq.cmd = cmd; + cmd->data = NULL; + + err = mmc_wait_for_req(host, &mrq); + if (!err) + break; + } + + return err; +} + +int32_t mmc_app_set_bus_width(struct mmc_card *card, uint32_t width) +{ + struct mmc_command cmd = {0}; + + cmd.opcode = SET_BUS_WIDTH; + cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; + + switch (width) { + case MMC_BUS_WIDTH_1: + cmd.arg = SD_BUS_WIDTH_1; + break; + case MMC_BUS_WIDTH_4: + cmd.arg = SD_BUS_WIDTH_4; + break; + default: + cmd.arg = SD_BUS_WIDTH_1; + } + + if (mmc_wait_for_app_cmd(card->host, card, &cmd)) { + return -1; + } + card->bus_width = width; + + return 0; +} + +int32_t mmc_switch_to_high_speed(struct mmc_card *card) +{ + int32_t err; + +#ifdef CONFIG_USE_MMC + if (card->csd.mmc_spec_ver < MMC_CSD_SPEC_VER_4) { + SD_LOGD("MMC card doesn't support to switch to high speed mode !!\n"); + return -1; + } +#endif + + err = mmc_switch(card, MMC_EXT_CSD_CMD_SET_NORMAL, MMC_EXT_CSD_HS_TIMING, 1); + if (err) { + SD_LOGD("MMC card failed to switch to high speed mode !!\n"); + return err; + } + + SD_LOGD("MMC card is switched to high speed!!\n"); + + return 0; +} + +int32_t smc_model_set_blkcnt(struct mmc_host *host, uint32_t blkcnt) +{ + struct mmc_command cmd = {0}; + + cmd.opcode = MMC_SET_BLOCK_COUNT; + cmd.arg = blkcnt & 0xffff; + cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_AC; + + if (mmc_wait_for_cmd(host, &cmd)) { + return -1; + } + + host->blkcnt = blkcnt; + + return 0; +} + +int32_t __sdmmc_block_rw(struct mmc_card *card, uint32_t blk_num, uint32_t blk_cnt, + uint32_t sg_len, struct scatterlist *sg, int write) +{ + struct mmc_command cmd = {0}; + struct mmc_data data = {0}; + struct mmc_request mrq; + uint32_t status = 0; + + SD_LOGD("%s %s blk_num:%d, blk_cnt:%d, sg_len:%d sg->len:%d\n", __func__, + write?"wirte":"read", blk_num, blk_cnt, sg_len, sg->len); + + if (blk_cnt > 1) { + cmd.opcode = write ? MMC_WRITE_MULTIPLE_BLOCK : MMC_READ_MULTIPLE_BLOCK; + } else { + cmd.opcode = write ? MMC_WRITE_SINGLE_BLOCK : MMC_READ_SINGLE_BLOCK; + } + cmd.arg = blk_num; + if (!mmc_card_blockaddr(card)) + cmd.arg <<= 9; + cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC; + cmd.stop = (blk_cnt == 1) ? 0 : 1; + + data.blksz = 512; + data.flags = write ? MMC_DATA_WRITE : MMC_DATA_READ; + + data.sg = sg; + data.sg_len = sg_len; + + mrq.cmd = &cmd; + mrq.data = &data; + + SD_LOGD("starting CMD%d arg 0x%08x flags %x\n", cmd.opcode, cmd.arg, cmd.flags); + SD_LOGD(" blksz %d blocks %d flags %x\n", data.blksz, data.blocks, data.flags); + if (mmc_wait_for_req(card->host, &mrq)) { + SD_LOGE("%s,%d %s sector:%x BSZ:%d Err!!\n", __func__, __LINE__, + write?"W":"R", blk_num, blk_cnt); + + return -1; + } + if (write) { + do { + if (HAL_SDC_Is_Busy(card->host)) + continue; + mmc_send_status(card, &status); + mmc_mdelay(1); + } while (!(status & 0x100)); + } + return 0; +} + +int32_t sdmmc_stream_write(struct mmc_card *card, uint32_t blk_num, uint32_t blk_size, uint32_t sg_len, struct scatterlist *sg) +{ + struct mmc_command cmd = {0}; + struct mmc_data data = {0}; + struct mmc_request mrq; + uint32_t status = 0; + + cmd.opcode = MMC_WRITE_SINGLE_BLOCK; + cmd.arg = blk_num; + if (!mmc_card_blockaddr(card)) + cmd.arg <<= 9; + cmd.stop = 0; + cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 |MMC_CMD_ADTC; + cmd.data = &data; + data.flags |= MMC_DATA_WRITE | MMC_DATA_STREAM; + + data.blksz = blk_size; + data.sg_len = sg_len; + data.sg = sg; + mrq.cmd = &cmd; + mrq.data = &data; + if (mmc_wait_for_req(card->host, &mrq)) { + return -1; + } + + /* check busy */ + do { + if (HAL_SDC_Is_Busy(card->host)) + continue; + mmc_send_status(card, &status); + } while (!(status & 0x100)); + return 0; +} +#endif + +void mmc_add_card(struct mmc_card *card) +{ + uint8_t spec_ver = 0; + uint32_t speed_hz = 0; +#if SD_DEBUG + const char *speed_mode = ""; + const char *type; +#endif + + switch (card->type) { +#ifdef CONFIG_USE_MMC + case MMC_TYPE_MMC: + type = "MMC"; + if (card->csd.mmc_spec_ver == 4) + spec_ver = 0x40; + else + spec_ver = 0x31; + speed_hz = mmc_mmc_get_max_clock(card); + break; +#endif +#ifdef CONFIG_USE_SD + case MMC_TYPE_SD: + type = "SD"; + if (mmc_card_blockaddr(card)) { + if (mmc_card_ext_capacity(card)) + type = "SDXC"; + else + type = "SDHC"; + } + if (card->scr.sda_vsn == 0) + spec_ver = 0x10; + else if (card->scr.sda_vsn == 1) + spec_ver = 0x11; + else if (card->scr.sda_vsn == 2 && card->scr.sda_spec3) + spec_ver = 0x30; + else + spec_ver = 0x20; + if (card->scr.sda_vsn == 2 && card->scr.sda_spec3 && card->scr.sda_spec4) + spec_ver = 0x40; + if (card->scr.sda_vsn == 2 && card->scr.sda_spec3 && card->scr.sda_spec5) + spec_ver = 0x50; + speed_hz = mmc_sd_get_max_clock(card); + break; +#endif +#ifdef CONFIG_USE_SDIO + case MMC_TYPE_SDIO: + type = "SDIO"; + spec_ver = 0x10; + speed_hz = mmc_sdio_get_max_clock(card); + break; +#endif +#ifdef CONFIG_USE_SD_COMBO + case MMC_TYPE_SD_COMBO: + type = "SD-combo"; + if (mmc_card_blockaddr(card)) + type = "SDHC-combo"; + break; +#endif + default: + type = "?"; + break; + } + + if (card->sd_bus_speed == HIGH_SPEED_BUS_SPEED) + speed_mode = "HS: 50 MHz"; + else + speed_mode = "DS: 25 MHz"; + + SD_LOGN("\n============= card information ==============\n"); + SD_LOGN("Card Type : %s\n", type); + SD_LOGN("Card Spec Ver : %x.%x\n", spec_ver>>4, spec_ver&0xf); + SD_LOGN("Card RCA : 0x%04x \n", card->rca); + SD_LOGN("Card OCR : 0x%x\n", card->ocr.ocr); + SD_LOGN(" vol_window : 0x%08x\n", card->ocr.vol_window); + SD_LOGN(" to_1v8_acpt : %x\n", card->ocr.to_1v8_acpt); + SD_LOGN(" high_capac : %x\n", card->ocr.high_capacity); + SD_LOGN("Card CSD :\n"); + SD_LOGN(" speed : %d KHz\n", speed_hz/1000); +#if ((defined CONFIG_USE_SD) || (defined CONFIG_USE_MMC)) + SD_LOGN(" cmd class : 0x%x\n", card->csd.cmdclass); + SD_LOGN(" capacity : %dMB\n", card->csd.capacity/1024); + SD_LOGN("Card CUR_STA :\n"); + SD_LOGN(" speed_mode : %s\n", speed_mode); + SD_LOGN(" bus_width : %d\n", card->bus_width); + SD_LOGN(" speed_class : %d\n", card->speed_class); +#else + (void)speed_mode; +#endif + SD_LOGN("=============================================\n"); + (void)spec_ver; + + mmc_card_set_present(card); +} + +#if ((defined CONFIG_USE_SD) || (defined CONFIG_USE_MMC)) +int mmc_set_blocklen(struct mmc_card *card, unsigned int blocklen) +{ + struct mmc_command cmd = {0}; + + if (mmc_card_blockaddr(card) || mmc_card_ddr_mode(card)) + return 0; + + cmd.opcode = MMC_SET_BLOCKLEN; + cmd.arg = blocklen; + cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_AC; + return mmc_wait_for_cmd(card->host, &cmd); +} + +/** + * @brief read SD card. + * @param card: + * @arg card->card handler. + * @param buf: + * @arg buf->for store readed data. + * @param sblk: + * @arg sblk->start block num. + * @param nblk: + * @arg nblk->number of blocks. + * @retval 0 if success or other if failed. + */ +int32_t mmc_block_read(struct mmc_card *card, uint8_t *buf, uint64_t sblk, uint32_t nblk) +{ + int32_t err; + struct scatterlist sg = {0}; + + SD_BUG_ON(!card->host); + + if (nblk > SDXC_MAX_TRANS_LEN/512) { + SD_LOGW("%s only support len < %d\n", __func__, SDXC_MAX_TRANS_LEN/512); + return -1; + } + + mmc_claim_host(card->host); + + err = mmc_set_blocklen(card, 512); + if (err) + goto out; + + sg.len = 512 * nblk; + sg.buffer = buf; + //if ((unsigned int)buf & 0x03) { + // SD_LOGW("%s buf not align 4!!!\n", __func__); + // return -1; + //} + + err = __sdmmc_block_rw(card, sblk, nblk, 1, &sg, 0); + +out: + mmc_release_host(card->host); + return err; +} + +/** + * @brief write SD card. + * @param card: + * @arg card->card handler. + * @param buf: + * @arg buf->data will be write. + * @param sblk: + * @arg sblk->start block num. + * @param nblk: + * @arg nblk->number of blocks. + * @retval 0 if success or other if failed. + */ +int32_t mmc_block_write(struct mmc_card *card, const uint8_t *buf, uint64_t sblk, uint32_t nblk) +{ + int32_t err; + struct scatterlist sg = {0}; + + SD_BUG_ON(!card->host); + + if (nblk > SDXC_MAX_TRANS_LEN/512) { + SD_LOGW("%s only support len < %d\n", __func__, SDXC_MAX_TRANS_LEN/512); + return -1; + } + + mmc_claim_host(card->host); + + err = mmc_set_blocklen(card, 512); + if (err) + goto out; + + sg.len = 512 * nblk; + sg.buffer = (uint8_t *)buf; + //if ((unsigned int)buf & 0x03) { + // SD_LOGW("%s buf not align 4!!!\n", __func__); + // return -1; + //} + + err = __sdmmc_block_rw(card, sblk, nblk, 1, &sg, 1); + +out: + mmc_release_host(card->host); + return err; +} +#endif + +int32_t mmc_send_relative_addr(struct mmc_host *host, uint32_t *rca) +{ + struct mmc_command cmd = {0}; + + cmd.opcode = SD_SEND_RELATIVE_ADDR; + cmd.arg = 0; + cmd.flags = MMC_RSP_R6 | MMC_CMD_BCR; + + do { + if (mmc_wait_for_cmd(host, &cmd)) { + return -1; + } + *rca = cmd.resp[0] >> 16; + } while (!*rca); + + return 0; +} + +int32_t mmc_send_if_cond(struct mmc_host *host, uint32_t ocr) +{ + struct mmc_command cmd = {0}; + int32_t err; + static const uint8_t test_pattern = 0xAA; + uint8_t result_pattern; + + /* + * To support SD 2.0 cards, we must always invoke SD_SEND_IF_COND + * before SD_APP_OP_COND. This command will harmlessly fail for + * SD 1.0 cards. + */ + cmd.opcode = SD_SEND_IF_COND; + cmd.arg = ((ocr & 0xFF8000) != 0) << 8 | test_pattern; + cmd.flags = MMC_RSP_SPI_R7 | MMC_RSP_R7 | MMC_CMD_BCR; + + err = mmc_wait_for_cmd(host, &cmd); + if (err) + return err; + + result_pattern = cmd.resp[0] & 0xFF; + + if (result_pattern != test_pattern) + return -1; + + return 0; +} + +int32_t mmc_select_card(struct mmc_card *card, uint32_t select) +{ + struct mmc_command cmd = {0}; + + cmd.opcode = MMC_SELECT_CARD; + if (select) { + cmd.arg = card->rca << 16; + cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; + } else { + cmd.arg = 0; + cmd.flags = MMC_RSP_NONE | MMC_CMD_AC; + } + + if (mmc_wait_for_cmd(card->host, &cmd)) { + return -1; + } + + return 0; +} + +int32_t mmc_all_send_cid(struct mmc_host *host, uint32_t *cid) +{ + int32_t err; + struct mmc_command cmd = {0}; + + SD_BUG_ON(!host); + SD_BUG_ON(!cid); + + cmd.opcode = MMC_ALL_SEND_CID; + cmd.arg = 0; + cmd.flags = MMC_RSP_R2 | MMC_CMD_BCR; + + err = mmc_wait_for_cmd(host, &cmd); + if (err) + return err; + + memcpy(cid, cmd.resp, sizeof(uint32_t) * 4); + + return 0; +} + +#ifdef CONFIG_SD_PM +/* + * Assign a mmc bus handler to a host. Only one bus handler may control a + * host at any given time. + */ +void mmc_attach_bus(struct mmc_host *host, const struct mmc_bus_ops *ops) +{ + unsigned long flags; + + SD_BUG_ON(!host); + SD_BUG_ON(!ops); + + flags = arch_irq_save(); + + SD_BUG_ON(host->bus_ops); + + host->bus_ops = ops; + + arch_irq_restore(flags); +} + +/* + * Remove the current bus handler from a host. + */ +void mmc_detach_bus(struct mmc_host *host) +{ + unsigned long flags; + + SD_BUG_ON(!host); + + SD_WARN_ON(!host->bus_ops); + + flags = arch_irq_save(); + + host->bus_ops = NULL; + + arch_irq_restore(flags); +} + +#endif + +/** + * @brief scan or rescan SD card. + * @param card: + * @arg card->card handler. + * @param sdc_id: + * @arg sdc_id->SDC ID which card on. + * @retval 0 if success or other if failed. + */ +int32_t mmc_rescan(struct mmc_card *card, uint32_t sdc_id) +{ + int32_t err = -1; + struct mmc_host *host = _mci_host; + + if (!host) { + SD_LOGE("%s init sdc host first!!\n", __func__); + return -1; + } + + card->host = host; + + mmc_claim_host(host); + + mmc_power_up(host); + + /* set identification clock 400KHz */ + HAL_SDC_Update_Clk(card->host, 400000); + + /* Initialization should be done at 3.3 V I/O voltage. */ + //mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_330, 0); + + /* + * sdio_reset sends CMD52 to reset card. Since we do not know + * if the card is being re-initialized, just send it. CMD52 + * should be ignored by SD/eMMC cards. + */ +#ifdef CONFIG_USE_SDIO + sdio_reset(host); +#endif + mmc_go_idle(host); + + /* cmd8 for SD2.0 */ + if (mmc_send_if_cond(host, host->ocr_avail)) { + SD_LOGN("sd1.0 or mmc\n"); + } + + /* Order's important: probe SDIO, then SD, then MMC */ +#ifdef CONFIG_USE_SDIO + SD_LOGN("***** Try sdio *****\n"); + if (!mmc_attach_sdio(card, host)){ + SD_LOGN("***** sdio init ok *****\n"); + err = 0; + goto out; + } +#endif +#ifdef CONFIG_USE_SD + SD_LOGN("***** Try sd *****\n"); + if (!mmc_attach_sd(card, host)){ + SD_LOGN("***** sd init ok *****\n"); + err = 0; + goto out; + } +#endif +#ifdef CONFIG_USE_MMC + SD_LOGN("***** Try mmc *****\n"); + if (!mmc_attach_mmc(card, host)){ + SD_LOGN("***** mmc init ok *****\n"); + err = 0; + goto out; + } +#endif + + SD_LOGD("Undown Card Detected!!\n"); + + mmc_power_off(host); + +out: + mmc_release_host(host); + return err; +} + +/** + * @brief deinit SD card. + * @param card: + * @arg card->card handler. + * @retval 0 if success or other if failed. + */ +int32_t mmc_card_deinit(struct mmc_card *card) +{ + SD_BUG_ON(!card->host); + +#ifdef CONFIG_USE_SDIO + mmc_deattach_sdio(card, card->host); +#endif +#ifdef CONFIG_USE_SD + mmc_deattach_sd(card, card->host); +#endif + + mmc_power_off(card->host); + memset(card, 0, sizeof(struct mmc_card)); + + return 0; +} diff --git a/platform/mcu/xr871/src/driver/chip/sdmmc/core.h b/platform/mcu/xr871/src/driver/chip/sdmmc/core.h new file mode 100644 index 0000000000..73f2c6b900 --- /dev/null +++ b/platform/mcu/xr871/src/driver/chip/sdmmc/core.h @@ -0,0 +1,219 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _DRIVER_CHIP_SDMMC_CORE_H_ +#define _DRIVER_CHIP_SDMMC_CORE_H_ + +#include "../hal_base.h" + +#include "driver/chip/sdmmc/card.h" + +#ifdef CONFIG_USE_SDIO +#include "driver/chip/sdmmc/sdio.h" +#endif +#include "driver/chip/sdmmc/sdmmc.h" + +#include "sdhost.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define SD_DEBUG 1 + +#if SD_DEBUG +#define SD_DEBUG_DETAIL 0 +#define SD_LOGD(format, args...) HAL_LOG(0, format, ##args) +#define SD_LOGN(format, args...) HAL_LOG(SD_DEBUG_DETAIL, format, ##args) +#define SD_LOGW(format, args...) HAL_LOG(SD_DEBUG, format, ##args) +#define SD_LOGE(format, args...) HAL_LOG(SD_DEBUG, format, ##args) +#define SD_LOGA(format, args...) HAL_LOG(SD_DEBUG, format, ##args) +#if SD_DEBUG_DETAIL +extern void print_hex_dump_bytes(const void *addr, unsigned int len); +#define sd_hex_dump_bytes(a, l) print_hex_dump_bytes(a, l) +#else +#define sd_hex_dump_bytes(a, l) +#endif +#else +#define SD_LOGD(x...) +#define SD_LOGN(x...) +#define SD_LOGW(x...) +#define SD_LOGE(x...) +#define SD_LOGA(x...) +#define sd_hex_dump_bytes(a, l) +#endif +#define SD_BUG_ON(v) do {if(v) {printf("BUG at %s:%d!\n", __func__, __LINE__); while (1);}} while (0) +#define SD_WARN_ON(v) do {if(v) {printf("WARN at %s:%d!\n", __func__, __LINE__);}} while (0) + +#define MMC_CMD_RETRIES 3 + +struct mmc_data { + uint32_t blksz; /* data block size */ + uint32_t blocks; /* number of blocks */ + uint32_t flags; + +#define MMC_DATA_WRITE (1 << 8) +#define MMC_DATA_READ (1 << 9) +#define MMC_DATA_STREAM (1 << 10) + + uint32_t bytes_xfered; + uint32_t sg_len; /* size of scatter list */ + struct scatterlist *sg; /* I/O scatter list */ +}; + +struct mmc_command { + uint32_t opcode; + uint32_t arg; + uint32_t resp[4]; + uint32_t flags; /* expected response type */ + /* data transfer */ + volatile uint32_t stop :1, + boot :1, + vol_switch :1; + +#define MMC_RSP_MASK (0x1f << 0) +#define MMC_RSP_PRESENT (1 << 0) +#define MMC_RSP_136 (1 << 1) /* 136 bit response */ +#define MMC_RSP_CRC (1 << 2) /* expect valid crc */ +#define MMC_RSP_BUSY (1 << 3) /* card may send busy */ +#define MMC_RSP_OPCODE (1 << 4) /* response contains opcode */ + +#define MMC_CMD_MASK (3 << 5) /* non-SPI command type */ +#define MMC_CMD_AC (0 << 5) /* addressed comamnd without data transfer */ +#define MMC_CMD_ADTC (1 << 5) /* addressed command with data transfer */ +#define MMC_CMD_BC (2 << 5) /* broadcast command without response */ +#define MMC_CMD_BCR (3 << 5) /* broadcast command with response */ + +#define MMC_RSP_SPI_S1 (1 << 7) /* one status byte */ +#define MMC_RSP_SPI_S2 (1 << 8) /* second byte */ +#define MMC_RSP_SPI_B4 (1 << 9) /* four data bytes */ +#define MMC_RSP_SPI_BUSY (1 << 10) /* card may send busy */ + +/* These are the native response types, and correspond to valid bit + * patterns of the above flags. One additional valid pattern + * is all zeros, which means we don't expect a response. + */ +#define MMC_RSP_NONE (0) +#define MMC_RSP_R1 (MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE) +#define MMC_RSP_R1B (MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE|MMC_RSP_BUSY) +#define MMC_RSP_R2 (MMC_RSP_PRESENT|MMC_RSP_136|MMC_RSP_CRC) +#define MMC_RSP_R3 (MMC_RSP_PRESENT) +#define MMC_RSP_R4 (MMC_RSP_PRESENT) +#define MMC_RSP_R5 (MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE) +#define MMC_RSP_R6 (MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE) +#define MMC_RSP_R7 (MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE) + +#define mmc_resp_type(cmd) ((cmd)->flags & MMC_RSP_MASK) + +/* + * These are the SPI response types for MMC, SD, and SDIO cards. + * Commands return R1, with maybe more info. Zero is an error type; + * callers must always provide the appropriate MMC_RSP_SPI_Rx flags. + */ +#define MMC_RSP_SPI_R1 (MMC_RSP_SPI_S1) +#define MMC_RSP_SPI_R1B (MMC_RSP_SPI_S1|MMC_RSP_SPI_BUSY) +#define MMC_RSP_SPI_R2 (MMC_RSP_SPI_S1|MMC_RSP_SPI_S2) +#define MMC_RSP_SPI_R3 (MMC_RSP_SPI_S1|MMC_RSP_SPI_B4) +#define MMC_RSP_SPI_R4 (MMC_RSP_SPI_S1|MMC_RSP_SPI_B4) +#define MMC_RSP_SPI_R5 (MMC_RSP_SPI_S1|MMC_RSP_SPI_S2) +#define MMC_RSP_SPI_R7 (MMC_RSP_SPI_S1|MMC_RSP_SPI_B4) + +/* These are the command types. */ +#define mmc_cmd_type(cmd) ((cmd)->flags & MMC_CMD_MASK) + + struct mmc_data *data; /* data segment associated with cmd */ +}; + +struct mmc_request { + struct mmc_command *cmd; + struct mmc_data *data; +}; + +#define UNSTUFF_BITS(resp,start,size) \ + ({ \ + const int32_t __size = size; \ + const uint32_t __mask = (__size < 32 ? 1 << __size : 0) - 1; \ + const int32_t __off = 3 - ((start) / 32); \ + const int32_t __shft = (start) & 31; \ + uint32_t __res; \ + \ + __res = resp[__off] >> __shft; \ + if (__size + __shft > 32) \ + __res |= resp[__off-1] << ((32 - __shft) % 32); \ + __res & __mask; \ + }) + +void mmc_attach_bus(struct mmc_host *host, const struct mmc_bus_ops *ops); +void mmc_detach_bus(struct mmc_host *host); + +extern int32_t mmc_align_data_size(struct mmc_card *card, uint32_t sz); +extern int32_t mmc_sd_switch(struct mmc_card *card, uint8_t mode, uint8_t group, + uint16_t value, uint8_t *resp); +extern void mmc_enumerate_card_info(struct mmc_card *card); +extern int32_t mmc_switch_to_high_speed(struct mmc_card *card); +extern int32_t mmc_sd_switch_hs(struct mmc_card *card); +extern int32_t mmc_app_set_bus_width(struct mmc_card *card, uint32_t width); +extern int32_t mmc_wait_for_cmd(struct mmc_host *host, struct mmc_command *cmd); +extern int32_t mmc_wait_for_req(struct mmc_host *host, struct mmc_request *mrq); +extern int32_t mmc_attach_sd(struct mmc_card *card, struct mmc_host *host); +extern void mmc_deattach_sd(struct mmc_card *card, struct mmc_host *host); +extern int32_t mmc_select_card(struct mmc_card *card, uint32_t select); +extern int32_t mmc_all_send_cid(struct mmc_host *host, uint32_t *cid); +extern int32_t mmc_send_relative_addr(struct mmc_host *host, uint32_t *rca); +extern void mmc_add_card(struct mmc_card *card); + +/** + * @brief Exclusively claim a host. + * @note Claim a host for a set of operations. + * @param host: + * @host->mmc host to claim. + * @retval None. + */ +static inline void mmc_claim_host(struct mmc_host *host) +{ + HAL_SDC_Claim_Host(host); +} + +/** + * @brief Release a host. + * @note Release a MMC host, allowing others to claim the host for their operations. + * @param host: + * @host->mmc host to release. + * @retval None. + */ +static inline void mmc_release_host(struct mmc_host *host) +{ + HAL_SDC_Release_Host(host); +} + +#ifdef __cplusplus +} +#endif + +#endif /* _DRIVER_CHIP_SDMMC_CORE_H_ */ diff --git a/platform/mcu/xr871/src/driver/chip/sdmmc/hal_sdhost.c b/platform/mcu/xr871/src/driver/chip/sdmmc/hal_sdhost.c new file mode 100644 index 0000000000..fe5e25f6d2 --- /dev/null +++ b/platform/mcu/xr871/src/driver/chip/sdmmc/hal_sdhost.c @@ -0,0 +1,1462 @@ +/** + * @file hal_sdhost.c + * @author XRADIO IOT WLAN Team + */ + +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "../hal_base.h" + +#include "sys/io.h" +#include "driver/chip/hal_gpio.h" + +#include "driver/chip/sdmmc/hal_sdhost.h" +#ifdef CONFIG_USE_SDIO +#include "driver/chip/sdmmc/sdio.h" +#endif +#include "driver/chip/sdmmc/sdmmc.h" +#include "sdhost.h" +#include "core.h" + +#include "pm/pm.h" + +#if ((defined CONFIG_USE_SD) || (defined CONFIG_USE_MMC)) +#define SDC_DMA_TIMEOUT 2000 /* not much data to write on this platform */ +#else +#define SDC_DMA_TIMEOUT 300 +#endif +#define SDC_THREAD_TIMEOUT (SDC_DMA_TIMEOUT + 50) + +#define SDC_SemCreate(l, n) HAL_SemaphoreInit(l, n, OS_SEMAPHORE_MAX_COUNT) +#define SDC_SemDel(l) HAL_SemaphoreDeinit(l) +#define SDC_SemPend(l, t) HAL_SemaphoreWait(l, t) +#define SDC_SemPost(l) HAL_SemaphoreRelease(l) + +#define SDC_MutexCreate(m) OS_MutexCreate(m); +#define SDC_MutexDelete(m) OS_MutexDelete(m); +#define SDC_MutexLock(m, t) OS_MutexLock(m, t) +#define SDC_MutexUnlock(m) OS_MutexUnlock(m); + +#define SDC_InitTimer(t, cb, arg, pms) OS_TimerCreate(t, OS_TIMER_ONCE, cb, arg, pms) +#define SDC_DelTimer(t) OS_TimerDelete(t) +#define SDC_ModTimer(t, ms) do {if (!ms) SDC_BUG_ON(1); \ + OS_TimerChangePeriod(t, ms);} while (0) + +#define SDC_REQUEST_IRQ(n, hdl) HAL_NVIC_SetIRQHandler(n, hdl) +#define SDC_SetPriority(n, l) HAL_NVIC_SetPriority(n, l) +#define SDC_ENABLE_IRQ(n) HAL_NVIC_EnableIRQ(n) +#define SDC_DISABLE_IRQ(n) HAL_NVIC_DisableIRQ(n) +#define SDC_CLEAR_IRQPINGD(n) HAL_NVIC_ClearPendingIRQ(n) +#define SDC_IRQHandler NVIC_IRQHandler + +//#define NUSE_STANDARD_INTERFACE 1 +#ifdef NUSE_STANDARD_INTERFACE +#define SDC_CCM_BASE (0x40040400) +#define SDC_CCM_SDC0_SCLK_CTRL (SDC_CCM_BASE + 0x028) +#else +#define SDC_CCM_BusForceReset() HAL_CCM_BusForcePeriphReset(CCM_BUS_PERIPH_BIT_SDC0) +#define SDC_CCM_BusReleaseRest() HAL_CCM_BusReleasePeriphReset(CCM_BUS_PERIPH_BIT_SDC0) +#define SDC_CCM_BusEnableClock() HAL_CCM_BusEnablePeriphClock(CCM_BUS_PERIPH_BIT_SDC0) +#define SDC_CCM_BusDisableClock() HAL_CCM_BusDisablePeriphClock(CCM_BUS_PERIPH_BIT_SDC0) +#define SDC_CCM_EnableMClock() HAL_CCM_SDC_EnableMClock() +#endif + +#define MEMS_VA2PA(x) (x) + +struct mmc_host *_mci_host; + +static int32_t __mci_exit_host(struct mmc_host *host) +{ + uint32_t rval; + +#ifdef CONFIG_SDC_SUPPORT_1V8 + host->voltage = SDC_WOLTAGE_OFF; +#endif + + rval = readl(SDXC_REG_GCTRL) | SDXC_HWReset; + writel(rval, SDXC_REG_GCTRL); + + return 0; +} + +static __inline void __mci_sel_access_mode(uint32_t access_mode) +{ + writel((readl(SDXC_REG_GCTRL) & (~SDXC_ACCESS_BY_AHB)) | access_mode, SDXC_REG_GCTRL); +} + +static int32_t __mci_reset(void) +{ + uint32_t value; + int32_t time = 0x0ffff; + + value = readl(SDXC_REG_GCTRL)|SDXC_HWReset; + writel(value, SDXC_REG_GCTRL); + while (time-- && (readl(SDXC_REG_GCTRL) & SDXC_SoftReset)) + ; + if (time <= 0) { + SDC_LOGE("SDC reset time out\n"); + return -1; + } + SDC_LOGD("%s,%d SDC reset finish \n", __func__, __LINE__); + + return 0; +} + +static int32_t __mci_program_clk(void) +{ + uint32_t value; + int32_t time = 0xf000; + int32_t ret = 0; + + /* disable command done interrupt */ + writel(readl(SDXC_REG_IMASK) & (~SDXC_CmdDone), SDXC_REG_IMASK); + + value = SDXC_Start | SDXC_UPCLKOnly | SDXC_WaitPreOver; + writel(value, SDXC_REG_CMDR); + + do { + value = readl(SDXC_REG_CMDR); + } while (time-- && (value & SDXC_Start)); + + if ((time <= 0) || (value & SDXC_Start)) { + SDC_LOGE("%s,%d SDC change clock time out\n", __func__, __LINE__); + ret = -1; + } + + /* clear command done flag */ + value = readl(SDXC_REG_RINTR); + writel(value, SDXC_REG_RINTR); + + /* enable command done interrupt */ + writel(readl(SDXC_REG_IMASK) | SDXC_CmdDone, SDXC_REG_IMASK); + + return ret; +} + +static void __mci_trans_by_ahb(struct mmc_host *host, struct mmc_data *data) +{ + uint32_t i, j; + uint32_t *buf_temp; /* Cortex-M3/4 can access data with unaligned address */ + volatile uint32_t time; + + for (i = 0; i < data->sg_len; i++) { + buf_temp = data->sg[i].buffer; + if (data->flags & MMC_DATA_READ) { + for (j = 0; j < (data->sg[i].len >> 2); j++) { /* sg[i].len should be multiply of 4 */ + time = 0xfffff00; + while ((readl(SDXC_REG_STAS) & SDXC_FIFOEmpty) && time--) + ; + buf_temp[j] = readl(SDXC_REG_FIFO); + } + } else if (data->flags & MMC_DATA_WRITE) { + for (j = 0; j < (data->sg[i].len >> 2); j++) { /* sg[i].len should be multiply of 4 */ + time = 0xfffff00; + while ((readl(SDXC_REG_STAS) & SDXC_FIFOFull) && time--) + ; + if (time <= 5) + SDC_BUG_ON(1); + writel(buf_temp[j], SDXC_REG_FIFO); + } + } else { + SDC_LOGW("illigle data request\n"); + SDC_BUG_ON(1); + } + } +} + +static smc_idma_des _mci_idma_des[SDXC_MAX_DES_NUM] __attribute__ ((aligned (8))); + +static smc_idma_des *__mci_alloc_idma_des(struct mmc_data *data) +{ + smc_idma_des *pdes = _mci_idma_des; + uint32_t des_idx = 0; + uint32_t buff_frag_num = 0; + uint32_t remain; + uint32_t i, j; + uint32_t config; + + /* init IDMA Descriptor, two mode: 1-fixed skip length, 2-chain mode */ + for (i = 0; i < data->sg_len; i++) { + buff_frag_num = data->sg[i].len >> SDXC_DES_NUM_SHIFT; /* num = len/8192 = len>>13 */ + remain = data->sg[i].len & (SDXC_DES_BUFFER_MAX_LEN - 1); + if (remain) { + buff_frag_num++; + } else { + remain = SDXC_DES_BUFFER_MAX_LEN; + } + + for (j = 0; j < buff_frag_num; j++, des_idx++) { + HAL_Memset((void *)&pdes[des_idx], 0, sizeof(smc_idma_des)); + config = SDXC_IDMAC_DES0_CH | SDXC_IDMAC_DES0_OWN | SDXC_IDMAC_DES0_DIC; + if (buff_frag_num > 1 && j != buff_frag_num - 1) { + pdes[des_idx].data_buf1_sz = SDXC_DES_BUFFER_MAX_LEN; + } else { + pdes[des_idx].data_buf1_sz = remain; + } + + pdes[des_idx].buf_addr_ptr1 = (uint32_t)MEMS_VA2PA(data->sg[i].buffer) + j * SDXC_DES_BUFFER_MAX_LEN; + if (i == 0 && j == 0) { + config |= SDXC_IDMAC_DES0_FD; + } + + if ((i == data->sg_len - 1) && (j == buff_frag_num - 1)) { + config &= ~SDXC_IDMAC_DES0_DIC; + config |= SDXC_IDMAC_DES0_LD | SDXC_IDMAC_DES0_ER; + pdes[des_idx].buf_addr_ptr2 = 0; + } else { + pdes[des_idx].buf_addr_ptr2 = (uint32_t)MEMS_VA2PA(&pdes[des_idx + 1]); + } + pdes[des_idx].config = config; + SDC_LOGD("sg %d, frag %d, remain %d, des[%d](%p): [0]:%x, [1]:%x, [2]:%x, [3]:%x\n", + i, j, remain, des_idx, &pdes[des_idx], + ((uint32_t *)&pdes[des_idx])[0], ((uint32_t *)&pdes[des_idx])[1], + ((uint32_t *)&pdes[des_idx])[2], ((uint32_t *)&pdes[des_idx])[3]); + } + } + + return pdes; +} + +static smc_idma_des *__mci_prepare_dma(struct mmc_host *host, struct mmc_data *data) +{ + uint32_t temp; + smc_idma_des *pdes = NULL; + + /* creat descriptor list, two mode: 1-fixed skip length, 2-chain mode */ + pdes = __mci_alloc_idma_des(data); + if (NULL == pdes) { + SDC_LOGW("alloc IDMA descriptor failed\n"); + return NULL; + } + + temp = readl(SDXC_REG_GCTRL); + temp |= SDXC_DMAEnb; + writel(temp, SDXC_REG_GCTRL); + temp |= SDXC_DMAReset; + writel(temp, SDXC_REG_GCTRL); + writel(SDXC_IDMACSoftRST, SDXC_REG_DMAC); /* reset IDMAC */ + temp = SDXC_IDMACFixBurst | SDXC_IDMACIDMAOn; + writel(temp, SDXC_REG_DMAC); + /* enable IDMA interrupt, here not use */ + temp = readl(SDXC_REG_IDIE); + temp &= ~(SDXC_IDMACReceiveInt | SDXC_IDMACTransmitInt); + if (data->flags & MMC_DATA_WRITE) { + temp |= SDXC_IDMACTransmitInt; + } else { + temp |= SDXC_IDMACReceiveInt; + } + writel(temp, SDXC_REG_IDIE); + + /* write descriptor address to register */ + writel((uint32_t)MEMS_VA2PA(pdes), SDXC_REG_DLBA); + /* write water level */ + writel((BURST_SIZE << 28) | (SMC_RX_WLEVEL << 16) | SMC_TX_WLEVEL, SDXC_REG_FTRGL); + + return pdes; +} + +static void __mci_free_idma_des(smc_idma_des *pdes) +{ + pdes->config &= ~SDXC_IDMAC_DES0_OWN; +} + +int32_t __mci_wait_access_done(void) +{ + int32_t own_set = 0; + int32_t timeout = 0x0f0000; + + while (!(readl(SDXC_REG_GCTRL) & SDXC_MemAccessDone) && timeout--) + ; + if (!(readl(SDXC_REG_GCTRL) & SDXC_MemAccessDone)) { + SDC_LOGW("wait memory access done timeout !!\n"); + } + + return own_set; +} + +static int32_t __mci_request_done(struct mmc_host *host) +{ + struct mmc_request *mrq = host->mrq; + unsigned long iflags; + uint32_t temp; + int32_t ret = 0; + + iflags = arch_irq_save(); + if (host->wait != SDC_WAIT_FINALIZE) { + arch_irq_restore(iflags); + SDC_LOGW("%s nothing finalize, wt %x\n", __func__, host->wait); + return -1; + } + host->wait = SDC_WAIT_NONE; + host->trans_done = 0; + host->dma_done = 0; + arch_irq_restore(iflags); + + if (host->int_sum & SDXC_IntErrBit) { + SDC_LOGE("SDC err, cmd %d, %s%s%s%s%s%s%s%s%s%s\n", + (host->smc_cmd & SDXC_CMD_OPCODE), + host->int_sum & SDXC_RespErr ? " RE" : "", + host->int_sum & SDXC_RespCRCErr ? " RCE" : "", + host->int_sum & SDXC_DataCRCErr ? " DCE" : "", + host->int_sum & SDXC_RespTimeout ? " RTO" : "", + host->int_sum & SDXC_DataTimeout ? " DTO" : "", + host->int_sum & SDXC_DataStarve ? " DS" : "", + host->int_sum & SDXC_FIFORunErr ? " FRE" : "", + host->int_sum & SDXC_HardWLocked ? " HL" : "", + host->int_sum & SDXC_StartBitErr ? " SBE" : "", + host->int_sum & SDXC_EndBitErr ? " EBE" : ""); + ret = -1; + goto out; + } + + if (mrq->cmd->flags & MMC_RSP_136) { + mrq->cmd->resp[0] = readl(SDXC_REG_RESP3); + mrq->cmd->resp[1] = readl(SDXC_REG_RESP2); + mrq->cmd->resp[2] = readl(SDXC_REG_RESP1); + mrq->cmd->resp[3] = readl(SDXC_REG_RESP0); + } else + mrq->cmd->resp[0] = readl(SDXC_REG_RESP0); + +out: + if (mrq->data) { + if (host->dma_hdle) { + __mci_wait_access_done(); + writel(0x337, SDXC_REG_IDST); /* clear interrupt flags */ + writel(0, SDXC_REG_IDIE); /* disable idma interrupt */ + writel(0, SDXC_REG_DMAC); /* idma off */ + temp = readl(SDXC_REG_GCTRL); + writel(temp | SDXC_DMAReset, SDXC_REG_GCTRL); + temp &= ~SDXC_DMAEnb; + writel(temp, SDXC_REG_GCTRL); /* disable IDMA */ + temp |= SDXC_FIFOReset; + writel(temp, SDXC_REG_GCTRL); + __mci_free_idma_des((void *)host->dma_hdle); + host->dma_hdle = NULL; + } + writel(readl(SDXC_REG_GCTRL) | SDXC_FIFOReset, SDXC_REG_GCTRL); + } + +#ifdef CONFIG_DETECT_CARD + if (host->param.cd_mode == CARD_DETECT_BY_D3){ + writel(SDXC_CardInsert|SDXC_CardRemove, SDXC_REG_IMASK); + } else +#endif + { + writel(0, SDXC_REG_IMASK); + } + if (host->int_sum & (SDXC_RespErr | SDXC_HardWLocked | SDXC_RespTimeout)) { + SDC_LOGE("sdc %d abnormal status: %s\n", __LINE__, + host->int_sum & SDXC_HardWLocked ? "HardWLocked" : "RespErr"); + } + + writel(0xffff, SDXC_REG_RINTR); + + SDC_LOGD("SDC done, resp %x %x %x %x\n", mrq->cmd->resp[0], + mrq->cmd->resp[1], mrq->cmd->resp[2], mrq->cmd->resp[3]); + + if (mrq->data && (host->int_sum & SDXC_IntErrBit)) { + SDC_LOGW("found data error, need to send stop command !!\n"); + __mci_reset(); + __mci_program_clk(); + } + + return ret; +} + +#ifdef CONFIG_SDIO_IRQ_SUPPORT +static void __mci_enable_sdio_irq(struct mmc_host *host, int enable) +{ + uint32_t imask; + + imask = readl(SDXC_REG_IMASK); + if (enable) + imask |= SDXC_SDIOInt; + else + imask &= ~SDXC_SDIOInt; + writel(imask, SDXC_REG_IMASK); +} + +static inline void __mci_signal_sdio_irq(struct mmc_host *host) +{ + SDC_BUG_ON(!host->card || !host->card->irq_handler); + + __mmc_enable_sdio_irq(host, 0); + host->card->irq_handler(host->card); + __mmc_enable_sdio_irq(host, 1); +} +#endif + +static void __mci_clk_prepare_enable(void) +{ + SDC_CCM_BusEnableClock(); /* clock enable */ + SDC_CCM_BusReleaseRest(); /* reset and gating */ + SDC_CCM_EnableMClock(); +} + +static void __mci_clk_disable_unprepare(void) +{ + SDC_CCM_BusDisableClock(); +} + +static void __mci_hold_io(struct mmc_host* host) +{ +#ifdef __CONFIG_ARCH_APP_CORE + /* disable gpio to avoid power leakage */ + HAL_BoardIoctl(HAL_BIR_PINMUX_DEINIT, HAL_MKDEV(HAL_DEV_MAJOR_SDC, host->sdc_id), SDCGPIO_BAS); +#endif +} + +static void __mci_restore_io(struct mmc_host* host) +{ +#ifdef __CONFIG_ARCH_APP_CORE + HAL_BoardIoctl(HAL_BIR_PINMUX_INIT, HAL_MKDEV(HAL_DEV_MAJOR_SDC, host->sdc_id), SDCGPIO_BAS); +#endif +} + +static void __mci_irq_handler(void) +{ + struct mmc_host *host = _mci_host; + uint32_t sdio_int = 0; + uint32_t raw_int; + uint32_t msk_int; + uint32_t idma_inte; + uint32_t idma_int; + + if (!host->present) { + SDC_CCM_BusEnableClock(); + } + + idma_int = readl(SDXC_REG_IDST); + idma_inte = readl(SDXC_REG_IDIE); + raw_int = readl(SDXC_REG_RINTR); + msk_int = readl(SDXC_REG_MISTA); + + if (!msk_int && !idma_int) { + SDC_LOGE("sdc nop irq: ri:%08x mi:%08x ie:%08x idi:%08x\n", + raw_int, msk_int, idma_inte, idma_int); + return ; + } + + host->int_sum = raw_int; + SDC_LOGD("smc %d ri:%02x(%02x), mi:%x, ie:%x, idi:%x\n", __LINE__, + (int)raw_int, (int)host->int_sum, + (int)msk_int, (int)idma_inte, (int)idma_int); + + (void)idma_inte; + +#ifdef CONFIG_SDIO_IRQ_SUPPORT + if (msk_int & SDXC_SDIOInt) { + sdio_int = 1; + SDC_LOGE("%s,%d unsupport sdio int now!\n", __func__, __LINE__); + writel(SDXC_SDIOInt, SDXC_REG_RINTR); + goto sdio_out; + } +#endif + +#ifdef CONFIG_DETECT_CARD + if (host->param.cd_mode == CARD_DETECT_BY_D3) { + if (msk_int & SDXC_CardInsert) { + SDC_LOGN("Card Insert !!\n"); + host->present = 1; + SDC_ModTimer(&host->cd_timer, 10); + } else if (msk_int & SDXC_CardRemove) { + SDC_LOGN("Card Remove !!\n"); + host->present = 0; + SDC_ModTimer(&host->cd_timer, 10); + } + } +#endif + + if (host->wait == SDC_WAIT_NONE && !sdio_int) { + SDC_LOGE("%s nothing to complete, ri:%08x, mi:%08x\n", + __func__, raw_int, msk_int); + goto irq_out; + } + + if ((raw_int & SDXC_IntErrBit) || (idma_int & SDXC_IDMA_ERR)) { + host->int_err = raw_int & SDXC_IntErrBit; + host->wait = SDC_WAIT_FINALIZE; + SDC_LOGE("%s,%d raw_int:%x err!\n", __func__, __LINE__, raw_int); + goto irq_out; + } + if (raw_int & SDXC_HardWLocked) { + SDC_LOGE("command hardware lock\n"); + } + + if (idma_int & (SDXC_IDMACTransmitInt|SDXC_IDMACReceiveInt)) { + host->dma_done = 1; + writel(idma_int, SDXC_REG_IDST); + } + if (msk_int & (SDXC_AutoCMDDone|SDXC_DataOver|SDXC_CmdDone|SDXC_VolChgDone)) + host->trans_done = 1; + if ((host->trans_done && \ + (host->wait == SDC_WAIT_AUTOCMD_DONE || host->wait == SDC_WAIT_DATA_OVER + || host->wait == SDC_WAIT_CMD_DONE || host->wait == SDC_WAIT_SWITCH1V8)) + || (host->trans_done && host->dma_done && (host->wait & SDC_WAIT_IDMA_DONE))) { + host->wait = SDC_WAIT_FINALIZE; + } + +irq_out: + writel(msk_int & (~SDXC_SDIOInt), SDXC_REG_RINTR); + + if (host->wait == SDC_WAIT_FINALIZE) { +#ifdef CONFIG_DETECT_CARD + if (host->param.cd_mode == CARD_DETECT_BY_D3){ + writel(SDXC_CardInsert|SDXC_CardRemove, SDXC_REG_IMASK); + } else +#endif + { + writel(0, SDXC_REG_IMASK); + } + SDC_SemPost(&host->lock); + SDC_LOGD("SDC irq post, trans:%d, dma:%d\n", (int)host->trans_done, (int)host->dma_done); + } + +#ifdef CONFIG_SDIO_IRQ_SUPPORT +sdio_out: + if (sdio_int) + __mci_signal_sdio_irq(host); +#endif +} + +/* clock config: + * SYS_CRYSTAL: 26M + * 400K, SCLK-26M, 26M/(2^3)/(7+1)/2 = 361K + * SYS_CRYSTAL: 24M + * 400K, SCLK-24M, 24M/(2^2)/(7+1)/2 = 375K + * + * 25M, SCLK-192M, 192M/(2^0)/(3+1)/2 = 24M + * 50M, SCLK-192M, 192M/(2^0)/(1+1)/2 = 48M + * 64M, SCLK-192M, 192M/(2^0)/(1+1)/2 = 48M + */ +#define DEFINE_SYS_CRYSTAL HAL_GetHFClock() +#define DEFINE_SYS_DEVCLK HAL_GetDevClock() + +static int32_t __mci_update_clock(uint32_t cclk) +{ + uint32_t sclk; + uint32_t div; + uint32_t rval; + uint32_t src = 0; + uint32_t m, n; + + if (cclk > DEFINE_SYS_CRYSTAL/2) { +#ifdef NUSE_STANDARD_INTERFACE + src = 1; +#else + src = CCM_AHB_PERIPH_CLK_SRC_DEVCLK; +#endif + sclk = DEFINE_SYS_DEVCLK; + } else { +#ifdef NUSE_STANDARD_INTERFACE + src = 0; +#else + src = CCM_AHB_PERIPH_CLK_SRC_HFCLK; +#endif + sclk = DEFINE_SYS_CRYSTAL; + } + cclk = cclk * 2; /* 2x MODE clock configure */ + div = (2 * sclk + cclk) / (2 * cclk); + div = div == 0 ? 1 : div; + if (div > 128) { + n = 3; + m = 16; + SDC_LOGE("source clk is too high!\n"); + } else if (div > 64) { + n = 3; + m = div >> 3; + } else if (div > 32) { + n = 2; + m = div >> 2; + } else if (div > 16) { + n = 1; + m = div >> 1; + } else { + n = 0; + m = div; + } + m = m - 1; +#ifdef NUSE_STANDARD_INTERFACE + rval = (1U << 31) | (src << 24) | (n << 16) | (m); + if (src) { + rval |= (2U << 20) | (1 << 8); + SDC_LOGN("%s,%d check spec. bit20, 8!!\n", __func__, __LINE__); + } + writel(rval, SDC_CCM_SDC0_SCLK_CTRL); + SDC_LOGN("SDC clock=%d kHz,src:%x, n:%d, m:%d\n", + (src?DEFINE_SYS_DEVCLK:DEFINE_SYS_CRYSTAL)/(1<clk = clk; + + return 0; +} + +static int32_t __mci_update_clk(struct mmc_host *host) +{ + uint32_t rval; + int32_t timeout = 0xf000; + int32_t ret = 0; + + rval = SDXC_Start|SDXC_UPCLKOnly|SDXC_WaitPreOver; +#ifdef CONFIG_SDC_SUPPORT_1V8 + if (host->voltage_switching) + rval |= SDXC_VolSwitch; +#endif + writel(rval, SDXC_REG_CMDR); + + do { + rval = readl(SDXC_REG_CMDR); + } while (timeout-- && (rval & SDXC_Start)); + + if (rval & SDXC_Start) { + SDC_LOGE("sdc update clock timeout, fatal error!!!\n"); + ret = -1; + } + + if (!ret) + SDC_LOGD("sdc update clock ok\n"); + + return ret; +} + +int32_t HAL_SDC_Clk_PWR_Opt(struct mmc_host *host, uint32_t oclk_en, uint32_t pwr_save) +{ + uint32_t rval; + + if (!host->power_on) + return 0; + + rval = readl(SDXC_REG_CLKCR); + + rval &= ~(SDXC_CardClkOn | SDXC_LowPowerOn); + if (oclk_en) + rval |= SDXC_CardClkOn; + if (pwr_save) + rval |= SDXC_LowPowerOn; + writel(rval, SDXC_REG_CLKCR); + + __mci_update_clk(host); + + return 0; +} + +static void __mci_debounce_onoff(uint32_t onoff) +{ + uint32_t rval = readl(SDXC_REG_GCTRL); + + rval &= ~SDXC_DebounceEnb; + if (onoff) + rval |= SDXC_DebounceEnb; + writel(rval, SDXC_REG_GCTRL); +} + +uint32_t HAL_SDC_Is_Busy(struct mmc_host *host) +{ + return readl(SDXC_REG_STAS) & SDXC_CardBusy; +} + +void HAL_SDC_Set_BusWidth(struct mmc_host *host, uint32_t width) +{ + SDC_BUG_ON(!host); + + switch (width) { + case MMC_BUS_WIDTH_1: + writel(SDXC_WIDTH1, SDXC_REG_WIDTH); + break; + case MMC_BUS_WIDTH_4: + writel(SDXC_WIDTH4, SDXC_REG_WIDTH); + break; +#ifdef CONFIG_USE_MMC + case MMC_BUS_WIDTH_8: + writel(SDXC_WIDTH8, SDXC_REG_WIDTH); + break; +#endif + default: + SDC_BUG_ON(1); + } + + host->buswidth = width; +} + +static void __mci_send_cmd(struct mmc_host *host, struct mmc_command *cmd) +{ + uint32_t imask = SDXC_IntErrBit; + uint32_t cmd_val = SDXC_Start | (cmd->opcode & 0x3f); + unsigned long iflags; + uint32_t wait = SDC_WAIT_CMD_DONE; + struct mmc_data *data = cmd->data; + + if (cmd->opcode == MMC_GO_IDLE_STATE) { + cmd_val |= SDXC_SendInitSeq; + imask |= SDXC_CmdDone; + } + +#ifdef CONFIG_SDC_SUPPORT_1V8 + if (cmd->opcode == SD_SWITCH_VOLTAGE) { + cmd_val |= SDXC_VolSwitch; + imask |= SDXC_VolChgDone; + host->voltage_switching = 1; + wait = SDC_WAIT_SWITCH1V8; + /* switch controller to high power mode */ + HAL_SDC_Clk_PWR_Opt(host, 1, 0); + } +#endif + + if (cmd->flags & MMC_RSP_PRESENT) { /* with response */ + cmd_val |= SDXC_RspExp; + if (cmd->flags & MMC_RSP_136) /* long response */ + cmd_val |= SDXC_LongRsp; + if (cmd->flags & MMC_RSP_CRC) + cmd_val |= SDXC_CheckRspCRC; + + if (mmc_cmd_type(cmd) == MMC_CMD_ADTC) { /* with data */ + SDC_BUG_ON(!data); + cmd_val |= SDXC_DataExp | SDXC_WaitPreOver; + wait = SDC_WAIT_DATA_OVER; + if (data->flags & MMC_DATA_STREAM) { /* sequence mode */ + imask |= SDXC_AutoCMDDone; + cmd_val |= SDXC_Seqmod | SDXC_SendAutoStop; + wait = SDC_WAIT_AUTOCMD_DONE; + } + if (cmd->stop) { + imask |= SDXC_AutoCMDDone; + cmd_val |= SDXC_SendAutoStop; + wait = SDC_WAIT_AUTOCMD_DONE; + } else + imask |= SDXC_DataOver; + + if (data->flags & MMC_DATA_WRITE) { + cmd_val |= SDXC_Write; + } else if (host->dma_hdle) { + wait |= SDC_WAIT_IDMA_DONE; + } + SDC_LOGD("blk_size:%d, sg len:%d\n", data->blksz, data->sg->len); + } else + imask |= SDXC_CmdDone; + } else + imask |= SDXC_CmdDone; + + SDC_LOGD("smc cmd:%d(%x), arg:%x ie:%x wt:%x len:%d\n", + (cmd_val & SDXC_CMD_OPCODE), cmd_val, cmd->arg, imask, wait, + cmd->data ? cmd->data->blksz * cmd->data->blocks : 0); + + iflags = arch_irq_save(); + host->smc_cmd = cmd_val; + host->wait = wait; + + writel(imask, SDXC_REG_IMASK); + + if (cmd_val & SDXC_SendAutoStop) + writel(0, SDXC_REG_A12A); + else + writel(0xffff, SDXC_REG_A12A); + + writel(cmd->arg, SDXC_REG_CARG); + writel(cmd_val, SDXC_REG_CMDR); + + arch_irq_restore(iflags); + + if (data && NULL == host->dma_hdle) { + __mci_trans_by_ahb(host, data); + } +} + +int32_t HAL_SDC_Request(struct mmc_host *host, struct mmc_request *mrq) +{ + int32_t ret = 0; + struct mmc_command *cmd = mrq->cmd; + struct mmc_data *data = mrq->data; +#ifdef CONFIG_SDC_DMA_USED + uint32_t sdc_use_dma = 1; +#else + uint32_t sdc_use_dma = 0; +#endif + + if (!host->present) { + SDC_LOGW("sdc no medium present\n"); + return -1; + } + +#ifdef CONFIG_SD_PM + if (host->suspend) { + SDC_LOGE("sdc has suspended!\n"); + return -1; + } +#endif + + OS_MutexLock(&host->thread_lock, SDC_THREAD_TIMEOUT); + + /* disable debounce */ + __mci_debounce_onoff(0); + + host->mrq = mrq; + + if (data) { + uint32_t i; + uint32_t byte_cnt = 0; + struct scatterlist *sg = data->sg; + uint32_t sg_len = data->sg_len; + + for (i = 0; i < sg_len; i++) { + byte_cnt += sg[i].len; +#ifdef CONFIG_SDC_DMA_USED + if (((uint32_t)sg[i].buffer & 0x03)) { + sdc_use_dma = 0; + break; + } +#endif + } +#ifdef CONFIG_SDC_DMA_USED + if (byte_cnt <= SDC_MAX_CPU_TRANS_LEN) { + sdc_use_dma = 0; + } +#endif + + writel(data->blksz, SDXC_REG_BLKSZ); + writel(byte_cnt, SDXC_REG_BCNTR); + + if (sdc_use_dma && byte_cnt > SDC_MAX_CPU_TRANS_LEN) { /* transfer by idma */ + __mci_sel_access_mode(SDXC_ACCESS_BY_DMA); + host->dma_hdle = __mci_prepare_dma(host, data); + if (NULL == host->dma_hdle) { + SDC_LOGW("SDC prepare DMA failed\n"); + __mci_sel_access_mode(SDXC_ACCESS_BY_AHB); + } + } else { + /* switch data bus to ahb */ + __mci_sel_access_mode(SDXC_ACCESS_BY_AHB); + } + } + + __mci_send_cmd(host, cmd); + + ret = SDC_SemPend(&host->lock, SDC_DMA_TIMEOUT); + if (ret != HAL_OK){ + SDC_LOGE("sdc cmd:%d, wait command done timeout !!\n", + (host->smc_cmd & SDXC_CMD_OPCODE)); + goto out; + } + + ret = __mci_request_done(host); + if (ret) { + SDC_LOGE("int err %x\n", host->int_err); + goto out; + } + +out: + /* enable debounce */ + __mci_debounce_onoff(1); + + SDC_MutexUnlock(&host->thread_lock); + + return ret; +} + +#ifdef CONFIG_SDC_EXCLUSIVE_HOST +/** + * HAL_SDC_Claim_Host - exclusively claim a host + * @host: mmc host to claim + * + * Claim a host for a set of operations. + */ +int32_t HAL_SDC_Claim_Host(struct mmc_host *host) +{ + return (SDC_SemPend(&host->exclusive_lock, OS_WAIT_FOREVER) == HAL_OK ? 0 : -1); +} + +/** + * HAL_SDC_Release_Host - release a host + * @host: mmc host to release + * + * Release a MMC host, allowing others to claim the host + * for their operations. + */ +void HAL_SDC_Release_Host(struct mmc_host *host) +{ + SDC_SemPost(&host->exclusive_lock); +} +#endif + +#ifdef CONFIG_SDC_READONLY_USED +int32_t HAL_SDC_Get_ReadOnly(struct mmc_host *host) +{ + uint32_t wp_val; + GPIO_PinMuxParam *ro_gpio = &host->ro_gpio; + + wp_val = = (GPIO_PIN_HIGH == HAL_GPIO_ReadPin(ro_gpio->port, ro_gpio->pin)) ? 1 : 0; + SDC_LOGN("sdc fetch card wp pin status: %d \n", wp_val); + + if (!wp_val) { + host->read_only = 0; + return 0; + } else { + SDC_LOGN("Card is write-protected\n"); + host->read_only = 1; + return 1; + } + + return 0; +} +#endif + +#ifdef CONFIG_DETECT_CARD +static void __mci_cd_timer(void *arg) +{ + struct mmc_host *host = (struct mmc_host *)arg; + uint32_t gpio_val = 0; + uint32_t present; + + gpio_val = (GPIO_PIN_HIGH == HAL_GPIO_ReadPin(host->cd_port, host->cd_pin)) ? 1 : 0; + + if (gpio_val) { + present = 0; + } else if (!gpio_val) + present = 1; + + SDC_LOGD("cd %d, host present %d, cur present %d\n", gpio_val, host->present, present); + + if (host->present ^ present) { + SDC_LOGD("sdc detect change, present %d\n", present); + host->present = present; + host->param.cd_cb(present); + } + + return ; +} + +static void __mci_dat3_det(void *arg) +{ + struct mmc_host *host = (struct mmc_host *)arg; + + SDC_LOGD("***dat3 det***\n"); + + host->param.cd_cb(host->present); +} + +static void __mci_cd_irq(void *arg) +{ + struct mmc_host *host = (struct mmc_host *)arg; + + SDC_LOGD("***in cd***\n"); + + SDC_ModTimer(&host->cd_timer, 10); + + return ; +} +#endif + +int32_t HAL_SDC_PowerOn(struct mmc_host *host) +{ + uint32_t rval; + + SDC_BUG_ON(!host); + + SDC_LOGD("MMC Driver init host\n"); + + __mci_restore_io(host); + __mci_clk_prepare_enable(); + + /* delay 1ms ? */ + __mci_update_clock(400000); + host->clk = 400000; + + /* reset controller */ + rval = readl(SDXC_REG_GCTRL)|SDXC_HWReset|SDXC_INTEnb|SDXC_AccessDoneDirect; + writel(rval, SDXC_REG_GCTRL); + + writel(0xffffffff, SDXC_REG_RINTR); + +#ifdef CONFIG_DETECT_CARD + if (host->param.cd_mode == CARD_DETECT_BY_D3){ + writel(SDXC_CardInsert|SDXC_CardRemove, SDXC_REG_IMASK); + } else +#endif + { + writel(0, SDXC_REG_IMASK); + } + +#define SDMC_DATA_TIMEOUT 0x0ffffffU +#define SDMC_RESP_TIMEOUT 0xffU + /* Set Data & Response Timeout Value */ + writel((SDMC_DATA_TIMEOUT<<8)|SDMC_RESP_TIMEOUT, SDXC_REG_TMOUT); +#undef SDMC_RESP_TIMEOUT +#undef SDMC_DATA_TIMEOUT + + HAL_SDC_Set_BusWidth(host, MMC_BUS_WIDTH_1); + +#ifdef SDC_DEBUG + writel(0xdeb, SDXC_REG_DBGC); + writel(0xceaa0000, SDXC_REG_FUNS); +#endif + +#ifdef CONFIG_DETECT_CARD + if (host->param.cd_mode == CARD_DETECT_BY_D3){ + rval |= SDXC_DebounceEnb; + } + writel(rval, SDXC_REG_GCTRL); +#endif + + SDC_ENABLE_IRQ(SDC_IRQn); + + host->power_on = 1; + + return 0; +} + +int32_t HAL_SDC_PowerOff(struct mmc_host *host) +{ + SDC_BUG_ON(!host); + +#ifdef CONFIG_DETECT_CARD + if (host->param.cd_mode != CARD_DETECT_BY_D3) +#endif + { + uint32_t rval; + + SDC_DISABLE_IRQ(SDC_IRQn); + + rval = readl(SDXC_REG_GCTRL) | SDXC_HWReset; + writel(rval, SDXC_REG_GCTRL); + rval = readl(SDXC_REG_MISTA); + writel(rval & (~SDXC_SDIOInt), SDXC_REG_RINTR); + + SDC_CLEAR_IRQPINGD(SDC_IRQn); + + __mci_clk_disable_unprepare(); + + __mci_hold_io(host); + host->power_on = 0; + } + + return 0; +} + +static struct mmc_host _mci_host_rel; + +#ifdef CONFIG_SD_PM + +struct __mci_ctrl_regs { + uint32_t gctrl; + uint32_t clkc; + uint32_t timeout; + uint32_t buswid; + uint32_t waterlvl; + uint32_t funcsel; + uint32_t idmacc; +}; + +static struct __mci_ctrl_regs _mci_pm_bak_regs; +#ifdef __CONFIG_ARCH_APP_CORE +static SDC_InitTypeDef g_sdc_param; +#endif + +static void __mci_regs_save(struct mmc_host *host) +{ + struct __mci_ctrl_regs* bak_regs = &_mci_pm_bak_regs; + + bak_regs->gctrl = readl(SDXC_REG_GCTRL); + bak_regs->clkc = readl(SDXC_REG_CLKCR); + bak_regs->timeout = readl(SDXC_REG_TMOUT); + bak_regs->buswid = readl(SDXC_REG_WIDTH); + bak_regs->waterlvl = readl(SDXC_REG_FTRGL); + bak_regs->funcsel = readl(SDXC_REG_FUNS); + bak_regs->idmacc = readl(SDXC_REG_DMAC); +} + +static void __mci_regs_restore(struct mmc_host *host) +{ + struct __mci_ctrl_regs* bak_regs = &_mci_pm_bak_regs; + + writel(bak_regs->gctrl, SDXC_REG_GCTRL); + writel(bak_regs->clkc, SDXC_REG_CLKCR); + writel(bak_regs->timeout, SDXC_REG_TMOUT); + writel(bak_regs->buswid, SDXC_REG_WIDTH); + writel(bak_regs->waterlvl, SDXC_REG_FTRGL); + writel(bak_regs->funcsel, SDXC_REG_FUNS); +#ifdef SDC_DEBUG + writel(0xdeb, SDXC_REG_DBGC); + writel(0xceaa0000, SDXC_REG_FUNS); +#endif + writel(bak_regs->idmacc, SDXC_REG_DMAC); +} + +static int __mci_suspend(struct soc_device *dev, enum suspend_state_t state) +{ + int ret = 0; + struct mmc_host *host = _mci_host; + +#ifdef CONFIG_DETECT_CARD + if (cancel_delayed_work(&host->detect)) + wake_unlock(&host->detect_wake_lock); +#endif + if (host->bus_ops && host->bus_ops->suspend) + ret = host->bus_ops->suspend(host); + + host->suspend = 1; + __mci_regs_save(host); + + /* gate clock for lower power */ + HAL_CCM_SDC_DisableMClock(); + SDC_CCM_BusForceReset(); + __mci_clk_disable_unprepare(); + +#ifdef CONFIG_DETECT_CARD + if (host->cd_mode == CARD_DETECT_BY_D3) { + __mci_exit_host(host); + + HAL_CCM_SDC_DisableMClock(); + SDC_CCM_BusForceReset(); + __mci_hold_io(host); + + SDC_LOGD("sdc card_power_off ok\n"); + + //__mci_set_vddio(host, SDC_WOLTAGE_OFF); + //usleep_range(1000, 1500); + host->power_on = 0; + + } +#endif + + SDC_LOGD("%s okay\n", __func__); + + return ret; +} + +static int __mci_resume(struct soc_device *dev, enum suspend_state_t state) +{ + int ret = 0; + struct mmc_host *host = _mci_host; + uint32_t clk = host->clk; + + __mci_restore_io(host); + + SDC_CCM_BusForceReset(); + __mci_clk_disable_unprepare(); + mmc_udelay(35); + + /* enable clock for resotre */ + __mci_clk_prepare_enable(); + mmc_udelay(50); + __mci_update_clock(clk); + mmc_udelay(100); + + __mci_regs_restore(host); +#ifdef CONFIG_DETECT_CARD +#else + writel(SDXC_CardInsert, SDXC_REG_RINTR); +#endif + __mci_update_clk(host); + + /* register IRQ */ + SDC_REQUEST_IRQ(SDC_IRQn, (SDC_IRQHandler)&__mci_irq_handler); + SDC_SetPriority(SDC_IRQn, NVIC_PERIPHERAL_PRIORITY_DEFAULT); + SDC_ENABLE_IRQ(SDC_IRQn); + mmc_udelay(100); + +#ifdef CONFIG_DETECT_CARD + if (host->cd_mode == CARD_DETECT_BY_GPIO_IRQ) + __mci_cd_timer(host); + + if (host->cd_mode == CARD_DETECT_BY_D3) { + uint32_t rval = 0; + + //__mci_set_vddio(host, host->regulator_voltage); + //usleep_range(1000, 1500); + host->power_on = 1; + + __mci_restore_io(host); + __mci_clk_prepare_enable(); + + mmc_mdelay(1); + rval = readl(SDXC_REG_RINTR); + SDC_LOGD(">> REG_RINTR=0x%x\n", rval); + } +#endif + + host->suspend = 0; + + if (host->bus_ops && host->bus_ops->resume) + ret = host->bus_ops->resume(host); + + SDC_LOGD("sdc resume okay\n"); + + return ret; +} + +static struct soc_device_driver sdc_drv = { + .name = "sdc", + .suspend = __mci_suspend, + .resume = __mci_resume, +}; + +static struct soc_device sdc_dev = { + .name = "sdc", + .driver = &sdc_drv, + .platform_data = (void *)&_mci_host_rel, +}; + +#define SDC_DEV (&sdc_dev) + +#else /* CONFIG_SD_PM */ + +#define SDC_DEV NULL + +#endif /* CONFIG_SD_PM */ + +/** + * @brief Initializes the SDC peripheral. + * @param sdc_id: + * @arg sdc_id->SDC ID. + * @param param: + * @arg param->[in] The configuration information. + * @retval SDC handler. + */ +#ifdef __CONFIG_ARCH_APP_CORE +struct mmc_host *HAL_SDC_Init(uint32_t sdc_id, SDC_InitTypeDef *param) +#else +/** + * @brief Initializes the SDC peripheral. + * @param sdc_id: + * @arg sdc_id->SDC ID. + * @retval SDC handler. + */ +struct mmc_host *HAL_SDC_Init(uint32_t sdc_id) +#endif +{ + struct mmc_host *host; +#ifdef __CONFIG_ARCH_APP_CORE + const HAL_SDCGPIOCfg *sd_gpio_cfg; +#endif + + host = &_mci_host_rel; + _mci_host = host; + + if (host->State != SDC_STATE_RESET) { + SDC_LOGW("%s reinit sdc!\n", __func__); + return NULL; + } + + host->sdc_id = sdc_id; +#ifdef __CONFIG_ARCH_APP_CORE + memcpy(&host->param, param, sizeof(SDC_InitTypeDef)); +#endif + + host->caps = MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED | MMC_CAP_WAIT_WHILE_BUSY | + MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25 | MMC_CAP_UHS_SDR50; +#ifdef CONFIG_SD_PM + host->pm_caps = MMC_PM_KEEP_POWER|MMC_PM_WAKE_SDIO_IRQ; +#endif + +#ifdef __CONFIG_ARCH_APP_CORE + HAL_BoardIoctl(HAL_BIR_GET_CFG, HAL_MKDEV(HAL_DEV_MAJOR_SDC, host->sdc_id), + (uint32_t)&sd_gpio_cfg); + if (sd_gpio_cfg->data_bits == 8) + host->caps |= MMC_CAP_8_BIT_DATA; + else if (sd_gpio_cfg->data_bits == 4) + host->caps |= MMC_CAP_4_BIT_DATA; +#else + host->caps |= MMC_CAP_4_BIT_DATA; +#endif + + __mci_restore_io(host); + + /* register IRQ */ + SDC_REQUEST_IRQ(SDC_IRQn, (SDC_IRQHandler)&__mci_irq_handler); + SDC_SetPriority(SDC_IRQn, NVIC_PERIPHERAL_PRIORITY_DEFAULT); + +#ifdef CONFIG_DETECT_CARD + if (host->param.cd_mode == CARD_ALWAYS_PRESENT) { + host->present = 1; + } else if (host->param.cd_mode == CARD_DETECT_BY_GPIO_IRQ) { + SDC_BUG_ON(!param->cd_cb); + SDC_BUG_ON(!sd_gpio_cfg->has_detect_gpio); + GPIO_IrqParam Irq_param; + + host->cd_port = sd_gpio_cfg->detect_port; + host->cd_pin = sd_gpio_cfg->detect_pin; + HAL_BoardIoctl(HAL_BIR_PINMUX_INIT, HAL_MKDEV(HAL_DEV_MAJOR_SDC, host->sdc_id), SDCGPIO_DET); + + SDC_InitTimer(&host->cd_timer, &__mci_cd_timer, host, 300); + + //HAL_GPIO_SetDebounce(&Irq_param, (2U << 4) | 1); /* set debounce clock */ + Irq_param.event = GPIO_IRQ_EVT_BOTH_EDGE; + Irq_param.callback = &__mci_cd_irq; + Irq_param.arg = host; + HAL_GPIO_EnableIRQ(host->cd_port, host->cd_pin, &Irq_param); + host->present = !((GPIO_PIN_HIGH == + HAL_GPIO_ReadPin(host->cd_port, host->cd_pin)) ? 1 : 0); + } else if (host->param.cd_mode == CARD_DETECT_BY_D3) { + uint32_t rval; + SDC_BUG_ON(!param->cd_cb); + + __mci_clk_prepare_enable(); + mmc_mdelay(1); + host->present = 1; + rval = readl(SDXC_REG_RINTR); + SDC_LOGD("sdc +> REG_RINTR=0x%x\n", rval); + if ((rval & SDXC_CardRemove)) { + SDC_LOGD("sdc data[3] detect Card Remove\n"); + host->present = 0; + } + SDC_InitTimer(&host->cd_timer, &__mci_dat3_det, host, 300); + } +#else + host->present = 1; +#endif + + host->max_blk_count = 8192; + host->max_blk_size = 4096; + host->max_req_size = host->max_blk_size * host->max_blk_count; + host->max_seg_size = host->max_req_size; + host->max_segs = 128; + host->ocr_avail = MMC_VDD_28_29 | MMC_VDD_29_30 | MMC_VDD_30_31 | MMC_VDD_31_32 + | MMC_VDD_32_33 | MMC_VDD_33_34; + + SDC_LOGD("SDC Host Capability:0x%x Ocr avail:0x%x\n", host->caps, host->ocr_avail); + + /* init semaphore */ + SDC_SemCreate(&host->lock, 0); + SDC_MutexCreate(&host->thread_lock); +#ifdef CONFIG_SDC_EXCLUSIVE_HOST + SDC_SemCreate(&host->exclusive_lock, 1); +#endif + +#ifdef CONFIG_DETECT_CARD + if (host->param.cd_mode == CARD_DETECT_BY_D3 && host->present == 0) { + SDC_LOGD("sdc power init.\n"); + HAL_SDC_PowerOn(host); + } else if (host->present == 0) { + /* if card is not present and the card detect mode is not CARD_DETECT_BY_D3, + * we shutdown io voltage to save power. */ + SDC_LOGD("sdc no card detected, shutdown io voltage.\n"); + __mci_hold_io(host); + //__mci_set_vddio(host, SDC_VOLTAGE_OFF); + } +#endif + +#ifdef CONFIG_SD_PM +#ifdef __CONFIG_ARCH_APP_CORE + memcpy(&g_sdc_param, param, sizeof(SDC_InitTypeDef)); +#endif + pm_register_ops(SDC_DEV); +#endif + + host->State = SDC_STATE_READY; + SDC_LOGD("sdc init ok.\n"); + + return host; +} + +/** + * @brief DeInitializes the SDC peripheral. + * @param sdc_id: + * @arg sdc_id-> SDC ID. + * @retval None. + */ +int32_t HAL_SDC_Deinit(uint32_t sdc_id) +{ + struct mmc_host *host = _mci_host; +#ifdef CONFIG_DETECT_CARD + host->param.cd_mode = 0; +#endif + SDC_BUG_ON(!host); + +#ifdef CONFIG_SD_PM + pm_unregister_ops(SDC_DEV); +#endif + + __mci_exit_host(host); + HAL_CCM_SDC_DisableMClock(); + SDC_CCM_BusForceReset(); + +#ifdef CONFIG_DETECT_CARD + HAL_BoardIoctl(HAL_BIR_PINMUX_DEINIT, HAL_MKDEV(HAL_DEV_MAJOR_SDC, host->sdc_id), SDCGPIO_DET); +#endif + +#ifdef CONFIG_SDC_EXCLUSIVE_HOST + SDC_SemDel(&host->exclusive_lock); +#endif + SDC_MutexDelete(&host->thread_lock); + SDC_SemDel(&host->lock); + +#ifdef CONFIG_DETECT_CARD + if ((host->param.cd_mode == CARD_DETECT_BY_GPIO_IRQ)|| + (host->param.cd_mode == CARD_DETECT_BY_D3)) { + SDC_DelTimer(&host->cd_timer); + } +#endif + + memset((void *)_mci_host, 0, sizeof(struct mmc_host)); /* SDC_STATE_RESET */ + _mci_host = NULL; + SDC_LOGD("sdc deinit ok.\n"); + + return 0; +} diff --git a/platform/mcu/xr871/src/driver/chip/sdmmc/sd.c b/platform/mcu/xr871/src/driver/chip/sdmmc/sd.c new file mode 100644 index 0000000000..f747b5e000 --- /dev/null +++ b/platform/mcu/xr871/src/driver/chip/sdmmc/sd.c @@ -0,0 +1,773 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "../hal_base.h" + +#include "sys/endian.h" + +#include "driver/chip/sdmmc/hal_sdhost.h" +#include "driver/chip/sdmmc/sdmmc.h" +#include "sdhost.h" +#include "core.h" +#ifdef CONFIG_USE_MMC +#include "_mmc.h" +#endif +#include "_sd.h" + +#ifdef CONFIG_USE_SD +static const unsigned int tran_exp[] = { + 10000, 100000, 1000000, 10000000, + 0, 0, 0, 0 +}; + +static const unsigned char tran_mant[] = { + 0, 10, 12, 13, 15, 20, 25, 30, + 35, 40, 45, 50, 55, 60, 70, 80, +}; +/* +static const unsigned int tacc_exp[] = { + 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, +}; + +static const unsigned int tacc_mant[] = { + 0, 10, 12, 13, 15, 20, 25, 30, + 35, 40, 45, 50, 55, 60, 70, 80, +}; +*/ + +static int32_t mmc_send_app_op_cond(struct mmc_host *host, uint32_t ocr, uint32_t *rocr) +{ + struct mmc_command cmd = {0}; + int32_t i, err; + + SD_BUG_ON(!host); + + cmd.opcode = SD_APP_OP_COND; + cmd.arg = ocr; + cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R3 | MMC_CMD_BCR; + + for (i = 100; i; i--) { + err = mmc_wait_for_app_cmd(host, NULL, &cmd); + if (err) + break; + + /* otherwise wait until reset completes */ + if (cmd.resp[0] & MMC_CARD_BUSY) + break; + + cmd.arg = 0x41000000 | (cmd.resp[0] & 0xFF8000); + + err = -1; + + mmc_mdelay(10); + } + + if (rocr) + *rocr = cmd.resp[0]; + + return err; +} + +int32_t mmc_app_sd_status(struct mmc_card *card, uint8_t *ssr) +{ + struct mmc_request mrq; + struct mmc_command cmd = {0}; + struct mmc_data data = {0}; + struct scatterlist sg; + + SD_BUG_ON(!ssr); + + if (mmc_app_cmd(card->host, card)) { + return -1; + } + + mrq.cmd = &cmd; + mrq.data = &data; + + cmd.opcode = SD_APP_SD_STATUS; + cmd.arg = 0; + cmd.flags = MMC_RSP_SPI_R2 | MMC_RSP_R1 | MMC_CMD_ADTC; + + data.blksz = 64; + data.blocks = 1; + data.flags = MMC_DATA_READ; + data.sg = &sg; + data.sg_len = 1; + + sg.len = 64; + sg.buffer = ssr; + + if (mmc_wait_for_req(card->host, &mrq)) { + return -1; + } + + SD_LOGN("card raw SD status:\n"); + sd_hex_dump_bytes((void *)ssr, 64); + + return 0; +} + +int32_t mmc_app_send_scr(struct mmc_card *card, uint32_t *raw_scr) +{ + struct mmc_command cmd = {0}; + struct mmc_data data = {0}; + struct mmc_request mrq; + struct scatterlist sg; + + if (mmc_app_cmd(card->host, card)) { + return -1; + } + + mrq.cmd = &cmd; + mrq.data = &data; + + cmd.opcode = SD_APP_SEND_SCR; + cmd.arg = 0; + cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC; + + + data.blksz = 8; + data.blocks = 1; + data.flags = MMC_DATA_READ; + data.sg = &sg; + data.sg_len = 1; + + sg.len = 8; + sg.buffer = (void *)raw_scr; + + /* get scr, 8 bytes */ + if (mmc_wait_for_req(card->host, &mrq)) { + return -1; + } + + raw_scr[0] = be32_to_cpu(raw_scr[0]); + raw_scr[1] = be32_to_cpu(raw_scr[1]); + + return 0; +} + +/* + * Given the decoded CSD structure, decode the raw CID to our CID structure. + */ +void mmc_decode_cid(struct mmc_card *card, uint32_t *resp) +{ + memset(&card->cid, 0, sizeof(struct mmc_cid)); + + /* + * SD doesn't currently have a version field so we will + * have to assume we can parse this. + */ + card->cid.manfid = UNSTUFF_BITS(resp, 120, 8); + card->cid.oemid = UNSTUFF_BITS(resp, 104, 8); + card->cid.prod_name[0] = UNSTUFF_BITS(resp, 96, 8); + card->cid.prod_name[1] = UNSTUFF_BITS(resp, 88, 8); + card->cid.prod_name[2] = UNSTUFF_BITS(resp, 80, 8); + card->cid.prod_name[3] = UNSTUFF_BITS(resp, 72, 8); + card->cid.prod_name[4] = UNSTUFF_BITS(resp, 64, 8); + card->cid.hwrev = UNSTUFF_BITS(resp, 60, 4); + card->cid.fwrev = UNSTUFF_BITS(resp, 56, 4); + card->cid.serial = UNSTUFF_BITS(resp, 24, 32); + card->cid.year = UNSTUFF_BITS(resp, 12, 8); + card->cid.month = UNSTUFF_BITS(resp, 8, 4); + + card->cid.year += 2000; /* SD cards year offset */ +} + +int32_t mmc_send_cid(struct mmc_card *card) +{ + struct mmc_command cmd = {0}; + uint32_t cid[4] = {0}; + + cmd.opcode = MMC_SEND_CID; + cmd.arg = card->rca << 16; + cmd.flags = MMC_RSP_R2 | MMC_CMD_AC; + + if (mmc_wait_for_cmd(card->host, &cmd)) { + return -1; + } + + memcpy((void *)cid, (void *)cmd.resp, 16); + SD_LOGN("card raw cid:\n"); + sd_hex_dump_bytes((void *)cid, 16); + + mmc_decode_cid(card, cid); + + return 0; +} + +static int32_t mmc_decode_csd(struct mmc_card *card, uint32_t *csd) +{ + int32_t e, m, csd_struct; + uint32_t *resp = csd; + + csd_struct = UNSTUFF_BITS(resp, 126, 2); + card->csd.csd_ver = csd_struct; + + switch (csd_struct) { + case 0: + //m = UNSTUFF_BITS(resp, 115, 4); + //e = UNSTUFF_BITS(resp, 112, 3); + //csd->tacc_ns = (tacc_exp[e] * tacc_mant[m] + 9) / 10; + //csd->tacc_clks = UNSTUFF_BITS(resp, 104, 8) * 100; + + m = UNSTUFF_BITS(resp, 99, 4); + e = UNSTUFF_BITS(resp, 96, 3); + card->csd.max_dtr = tran_exp[e] * tran_mant[m]; + card->csd.cmdclass = UNSTUFF_BITS(resp, 84, 12); + + e = UNSTUFF_BITS(resp, 47, 3); + m = UNSTUFF_BITS(resp, 62, 12); + card->csd.c_size_mult = e; + card->csd.c_size = m; + card->csd.capacity = (1 + m) << (e + 2); + + //csd->read_blkbits = UNSTUFF_BITS(resp, 80, 4); + card->csd.read_blk_len = UNSTUFF_BITS(resp, 80, 4); + //csd->read_partial = UNSTUFF_BITS(resp, 79, 1); + //csd->write_misalign = UNSTUFF_BITS(resp, 78, 1); + //csd->read_misalign = UNSTUFF_BITS(resp, 77, 1); + //csd->r2w_factor = UNSTUFF_BITS(resp, 26, 3); + //csd->write_blkbits = UNSTUFF_BITS(resp, 22, 4); + //csd->write_partial = UNSTUFF_BITS(resp, 21, 1); + + //if (UNSTUFF_BITS(resp, 46, 1)) { + // csd->erase_size = 1; + //} else if (csd->write_blkbits >= 9) { + // csd->erase_size = UNSTUFF_BITS(resp, 39, 7) + 1; + // csd->erase_size <<= csd->write_blkbits - 9; + //} + break; + case 1: + /* + * This is a block-addressed SDHC or SDXC card. Most + * interesting fields are unused and have fixed + * values. To avoid getting tripped by buggy cards, + * we assume those fixed values ourselves. + */ + mmc_card_set_blockaddr(card); + + //csd->tacc_ns = 0; /* Unused */ + //csd->tacc_clks = 0; /* Unused */ + + m = UNSTUFF_BITS(resp, 99, 4); + e = UNSTUFF_BITS(resp, 96, 3); + card->csd.cmdclass = UNSTUFF_BITS(resp, 84, 12); + card->csd.read_blk_len = UNSTUFF_BITS(resp, 80, 4); + card->csd.max_dtr = tran_exp[e] * tran_mant[m]; + + m = UNSTUFF_BITS(resp, 48, 22); + card->csd.c_size = m; + card->csd.capacity = (1 + m) << 9; + /* SDXC cards have a minimum C_SIZE of 0x00FFFF */ + if (card->csd.c_size >= 0xFFFF) + mmc_card_set_ext_capacity(card); + + card->csd.c_size_mult = 0; + + //csd->read_blkbits = 9; + //csd->read_partial = 0; + //csd->write_misalign = 0; + //csd->read_misalign = 0; + //csd->r2w_factor = 4; /* Unused */ + //csd->write_blkbits = 9; + //csd->write_partial = 0; + //csd->erase_size = 1; + break; + default: + SD_LOGE("%s: unrecognised CSD structure version %d\n", + __func__, csd_struct); + return -1; + } + + //card->erase_size = csd->erase_size; + SD_LOGD("%s %d c_z:%d ca:%d %d\n", __func__, csd_struct, card->csd.c_size, + card->csd.capacity, card->csd.c_size_mult); + + return 0; +} + +/* + * Given a 64-bit response, decode to our card SCR structure. + */ +static int32_t mmc_decode_scr(struct mmc_card *card, uint32_t *raw_scr) +{ + struct sd_scr *scr = &card->scr; + uint32_t scr_struct; + uint32_t resp[4]; + + resp[3] = raw_scr[1]; + resp[2] = raw_scr[0]; + + scr_struct = UNSTUFF_BITS(resp, 60, 4); + if (scr_struct != 0) { + SD_LOGW("sdc unrecognised SCR structure version %d\n", scr_struct); + return -1; + } + + scr->sda_vsn = UNSTUFF_BITS(resp, 56, 4); + scr->security_sup = UNSTUFF_BITS(resp, 52, 3); + scr->bus_widths = UNSTUFF_BITS(resp, 48, 4); + + if (scr->sda_vsn == SCR_SPEC_VER_2) + /* Check if Physical Layer Spec v3.0 is supported */ + scr->sda_spec3 = UNSTUFF_BITS(resp, 47, 1); + + //if (UNSTUFF_BITS(resp, 55, 1)) + // card->erased_byte = 0xFF; + //else + // card->erased_byte = 0x0; + + if (scr->sda_spec3) { + scr->cmds = UNSTUFF_BITS(resp, 32, 2); + scr->sda_spec4 = UNSTUFF_BITS(resp, 42, 1); + scr->sda_spec5 = UNSTUFF_BITS(resp, 38, 4); + } + + return 0; +} + +/* + * Fetch and process SD Status register. + */ +static int32_t mmc_read_ssr(struct mmc_card *card) +{ + int32_t err, i; + uint32_t ssr[64/4]; + + if (!(card->csd.cmdclass & CCC_APP_SPEC)) { + SD_LOGW("%s: card lacks mandatory SD Status function.\n", __func__); + return 0; + } + + err = mmc_app_sd_status(card, (uint8_t *)ssr); + if (err) { + SD_LOGW("%s: problem reading SD Status register.\n", __func__); + err = 0; + goto out; + } + + for (i = 0; i < 16; i++) + ssr[i] = be32_to_cpu(ssr[i]); + + card->speed_class = UNSTUFF_BITS(ssr, 440 - 384, 8) * 2; + if (card->speed_class == 8) + card->speed_class = 10; +out: + return err; +} + +/* + * Fetches and decodes switch information + */ +static int32_t mmc_read_switch(struct mmc_card *card) +{ + int32_t err; + uint32_t status[64/sizeof(uint32_t)] = {0}; + uint8_t *p_sta = (uint8_t *)status; + + if (card->scr.sda_vsn < SCR_SPEC_VER_1) { + SD_LOGW("Card ver. does not support to read switch info!\n"); + return -1; + } + if (!(card->csd.cmdclass & CCC_SWITCH)) { + SD_LOGW("Card cmdclass does not support to read switch info!\n"); + return -1; + } + + /* Find out the supported Bus Speed Modes. */ + err = mmc_sd_switch(card, SD_SWITCH_CHECK, SD_SWITCH_GRP_ACCESS_MODE, + SD_SWITCH_ACCESS_HS, p_sta); + if (err) { + SD_LOGW("%s: problem reading Bus Speed modes.\n", __func__); + goto out; + } + + if (p_sta[13] & SD_MODE_HIGH_SPEED) + card->sw_caps.hs_max_dtr = HIGH_SPEED_MAX_DTR; + + if (card->scr.sda_spec3) { + card->sw_caps.sd3_bus_mode = p_sta[13]; + + /* Find out Driver Strengths supported by the card */ + err = mmc_sd_switch(card, SD_SWITCH_CHECK, SD_SWITCH_GRP_DRV_STRENGTH, + SD_SWITCH_ACCESS_HS, p_sta); + if (err) { + SD_LOGW("%s: problem reading Driver Strength.\n", __func__); + goto out; + } + + card->sw_caps.sd3_drv_type = p_sta[9]; + + /* Find out Current Limits supported by the card */ + err = mmc_sd_switch(card, SD_SWITCH_CHECK, SD_SWITCH_GRP_CUR_LIMIT, + SD_SWITCH_ACCESS_HS, p_sta); + if (err) { + SD_LOGW("%s: problem reading Current Limit.\n", __func__); + goto out; + } + + card->sw_caps.sd3_curr_limit = p_sta[7]; + } + +out: + return err; +} + +/* + * Test if the card supports high-speed mode and, if so, switch to it. + */ +int32_t mmc_sd_switch_hs(struct mmc_card *card) +{ + int32_t err; + uint32_t status[64/sizeof(uint32_t)] = {0}; + uint8_t *p_sta = (uint8_t *)status; + + if (card->scr.sda_vsn < SCR_SPEC_VER_1) { + SD_LOGW("Card ver. does not support to switch to high speed!\n"); + return -1; + } + + if (!(card->csd.cmdclass & CCC_SWITCH)) { + SD_LOGW("Card cmdclass not support to switch to high speed!\n"); + return -1; + } + + if (!(card->host->caps & MMC_CAP_SD_HIGHSPEED)) + return -1; + + /* Check function */ + err = mmc_sd_switch(card, SD_SWITCH_CHECK, SD_SWITCH_GRP_ACCESS_MODE, + SD_SWITCH_ACCESS_HS, p_sta); + if (err) + return err; + + if ((p_sta[16] & 0x0F) != 1) { + SD_LOGW("%s: Problem switching card into high-speed mode!\n", __func__); + err = -1; + } else { + err = 0; + } + + return err; +} + +static int32_t mmc_send_cxd_native(struct mmc_host *host, uint32_t arg, uint32_t *cxd, int32_t opcode) +{ + int32_t err; + struct mmc_command cmd = {0}; + + SD_BUG_ON(!host); + SD_BUG_ON(!cxd); + + cmd.opcode = opcode; + cmd.arg = arg; + cmd.flags = MMC_RSP_R2 | MMC_CMD_AC; + + err = mmc_wait_for_cmd(host, &cmd); + if (err) + return err; + + memcpy(cxd, cmd.resp, sizeof(uint32_t) * 4); + + return 0; +} + +int32_t mmc_send_csd(struct mmc_card *card, uint32_t *csd) +{ + return mmc_send_cxd_native(card->host, card->rca << 16, + csd, MMC_SEND_CSD); +} + +int32_t mmc_sd_get_csd(struct mmc_card *card) +{ + int32_t err; + uint32_t csd[4] = {0}; + + /* Fetch CSD from card. */ + err = mmc_send_csd(card, csd); + if (err) + return err; + + SD_LOGN("card raw csd:\n"); + sd_hex_dump_bytes((void *)csd, 16); + + err = mmc_decode_csd(card, csd); + if (err) + return err; + + return 0; +} + +int32_t mmc_sd_setup_card(struct mmc_host *host, struct mmc_card *card) +{ + int32_t err; + int32_t retries; + uint32_t raw_scr[2] = {0}; + + /* Fetch SCR from card. */ + err = mmc_app_send_scr(card, raw_scr); + if (err) + return err; + + SD_LOGN("card raw scr:\n"); + sd_hex_dump_bytes((void *)raw_scr, 8); + + err = mmc_decode_scr(card, raw_scr); + if (err) + return err; + + /* Fetch and process SD Status register. */ + err = mmc_read_ssr(card); + if (err) + return err; + + /* Fetch switch information from card. */ + for (retries = 1; retries <= 3; retries++) { + err = mmc_read_switch(card); + if (!err) { + if (retries > 1) { + SD_LOGW("%s: recovered\n", __func__); + } + break; + } else { + SD_LOGW("%s: read switch failed (attempt %d)\n", + __func__, retries); + } + } + + if (err) + return err; + + return 0; +} + +uint32_t mmc_sd_get_max_clock(struct mmc_card *card) +{ + uint32_t max_dtr = (uint32_t)-1; + + if (mmc_card_highspeed(card)) { + if (max_dtr > card->sw_caps.hs_max_dtr) + max_dtr = card->sw_caps.hs_max_dtr; + } else if (max_dtr > card->csd.max_dtr) { + max_dtr = card->csd.max_dtr; + } + + return max_dtr; +} + +/* + * Handle the detection and initialisation of a card. + * + * In the case of a resume, "oldcard" will contain the card + * we're trying to reinitialise. + */ +static int32_t mmc_sd_init_card(struct mmc_card *card, struct mmc_host *host) +{ + int32_t err = 0; + + /* + * I/O voltage should be 3.3 V here for the initialization needed. + */ + + /* cmd2, send cid, check if card support 3.3V */ + err = mmc_all_send_cid(host, card->cidno); + if (err) { + SD_LOGW("Cards all send CID number failed !!\n"); + return -1; + } else + SD_LOGN("Card CID number:%x\n", card->cidno[0]); + + card->type = MMC_TYPE_SD; + + /* cmd3, For native busses: get card RCA and quit open drain mode. */ + err = mmc_send_relative_addr(card->host, &card->rca); + if (err) { + SD_LOGW("Card public new RCA failed !!\n"); + return -1; + } else + SD_LOGD("Card public new RCA:%x\n", card->rca); + + /* cmd10, get CID register */ + if (mmc_send_cid(card)) { + SD_LOGW("Card send CID reg failed !!\n"); + return -1; + } + + /* cmd9, get CSD register */ + if (mmc_sd_get_csd(card)) { + SD_LOGW("Card send CSD reg failed !!\n"); + return -1; + } + + /* cmd7, Select card to standby state, as all following commands rely on that. */ + if (mmc_select_card(card, 1)) { + SD_LOGW("mmc_select_card failed !!\n"); + return -1; + } + + err = mmc_sd_setup_card(host, card); + if (err) + goto free_card; + + /* Initialization sequence for UHS-I cards */ + { + uint32_t clk; + uint32_t retries = 3; + + while (retries) { + err = mmc_sd_switch_hs(card); + if (err < 0) { + SD_LOGE("%s: Re-switch hs, err %d (retries = %d)\n", + __func__, err, retries); + mmc_mdelay(5); + retries--; + continue; + } + break; + } + + if (err) { + SD_LOGW("switch to high speed error, use DS: 25 MHz\n"); + clk = 25000000; + HAL_SDC_Update_Clk(card->host, clk); + err = 0; + } else { + mmc_card_set_highspeed(card); + card->sd_bus_speed = HIGH_SPEED_BUS_SPEED; + + clk = mmc_sd_get_max_clock(card); + HAL_SDC_Update_Clk(card->host, clk); + SD_LOGN("card is switched to high speed mode, clk:%d KHz\n", clk/1000); + } + + /* Switch to wider bus (if supported). */ + if ((host->caps & MMC_CAP_4_BIT_DATA) && + (card->scr.bus_widths & SD_SCR_BUS_WIDTH_4)) { + err = mmc_app_set_bus_width(card, MMC_BUS_WIDTH_4); + if (err) { + SD_LOGW("Set bus width error, use default 1 bit !!\n"); + return -1; + } + + /* config SDMMC controller bus width */ + HAL_SDC_Set_BusWidth(card->host, MMC_BUS_WIDTH_4); + SD_LOGN("Set bus width type: %d\n", MMC_BUS_WIDTH_4); + } + } + + mmc_add_card(card); + card->host->card = card; + + return 0; + +free_card: + card->host->card = NULL; + return err; +} + +#ifdef CONFIG_SD_PM +static uint32_t mmc_sd_suspending; + +static int32_t mmc_sd_suspend(struct mmc_host *host) +{ + mmc_sd_suspending = 1; + mmc_card_deinit(host->card); + SD_LOGD("%s ok\n", __func__); + + return 0; +} + +static int32_t mmc_sd_resume(struct mmc_host *host) +{ + mmc_rescan(host->card, host->sdc_id); + mmc_sd_suspending = 0; + SD_LOGD("%s ok\n", __func__); + + return 0; +} + +static const struct mmc_bus_ops sd_bus_ops = { + .suspend = mmc_sd_suspend, + .resume = mmc_sd_resume, +}; +#endif + +/* + * Starting point for SD card init. + */ +int32_t mmc_attach_sd(struct mmc_card *card, struct mmc_host *host) +{ + int32_t err = 0; + uint32_t ocr; + + SD_BUG_ON(!host); + + /* send cmd41/55 to check operation condition */ + err = mmc_send_app_op_cond(host, 0, &ocr); + if (err) { + return err; + } + SD_LOGN("card ocr: %08x\n", ocr); + + /* + * Sanity check the voltages that the card claims to + * support. + */ + if (ocr & 0x7F) { + SD_LOGW("%s: card claims to support voltages below the defined range." + " These will be ignored.\n", __func__); + ocr &= ~0x7F; + } + + card->ocr.ocr = 0x7fffffff & ocr; /* set card not in busy state */ + + err = mmc_sd_init_card(card, host); + +#ifdef CONFIG_SD_PM + if (!mmc_sd_suspending) { + mmc_attach_bus(host, &sd_bus_ops); + } +#endif + + return err; +} + +void mmc_deattach_sd(struct mmc_card *card, struct mmc_host *host) +{ + mmc_select_card(host->card, 0); + host->card->state &= ~MMC_STATE_HIGHSPEED; + +#ifdef CONFIG_SD_PM + if (!mmc_sd_suspending) { + mmc_detach_bus(host); + } +#endif +} + +#endif /* CONFIG_USE_SD */ diff --git a/platform/mcu/xr871/src/driver/chip/sdmmc/sdhost.h b/platform/mcu/xr871/src/driver/chip/sdmmc/sdhost.h new file mode 100644 index 0000000000..b7b0987571 --- /dev/null +++ b/platform/mcu/xr871/src/driver/chip/sdmmc/sdhost.h @@ -0,0 +1,553 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _DRIVER_CHIP_SDMMC_SDHOST_H_ +#define _DRIVER_CHIP_SDMMC_SDHOST_H_ + +#include "../hal_debug.h" + +#include "driver/chip/hal_util.h" +#include "driver/chip/sdmmc/card.h" +#include "driver/chip/sdmmc/hal_sdhost.h" +#ifdef CONFIG_USE_SDIO +#include "driver/chip/sdmmc/sdio.h" +#endif +#include "driver/chip/sdmmc/sdmmc.h" +#include "pm/pm.h" + +#define CONFIG_SDC_OS_USED + +#ifdef SD_SUPPORT_VERSION3 +#define CONFIG_SDC_SUPPORT_1V8 /* board not support */ +#endif +#ifndef __CONFIG_ARCH_DUAL_CORE +//#define CONFIG_SDC_DMA_USED +#endif +#ifdef SD_SUPPORT_WRITEPROTECT +#define CONFIG_SDC_READONLY_USED /* check readonly */ +#endif + +#define SDC_DEBUG 1 + +#ifdef CONFIG_SDC_OS_USED +#include "./../hal_os.h" +#include "kernel/os/os_semaphore.h" +#include "kernel/os/os_mutex.h" +#ifdef __CONFIG_ARCH_APP_CORE +#include "kernel/os/os_timer.h" +#endif + +#define SDC_Semaphore OS_Semaphore_t +#define SDC_Mutex OS_Mutex_t +#ifdef __CONFIG_ARCH_APP_CORE +#define SDC_Timer OS_Timer_t +#endif +#endif + +#if SDC_DEBUG +#define SDC_LOGD(format, args...) HAL_LOG(0, format, ##args) +#define SDC_LOGN(format, args...) HAL_LOG(SDC_DEBUG, format, ##args) +#define SDC_LOGW(format, args...) HAL_LOG(SDC_DEBUG, format, ##args) +#define SDC_LOGE(format, args...) HAL_LOG(SDC_DEBUG, format, ##args) +#define SDC_LOGA(format, args...) HAL_LOG(SDC_DEBUG, format, ##args) +#else +#define SDC_LOGD(x...) +#define SDC_LOGN(x...) +#define SDC_LOGW(x...) +#define SDC_LOGE(x...) +#define SDC_LOGA(x...) +#endif +#define SDC_BUG_ON(v) do {if(v) {printf("BUG at %s:%d!\n", __func__, __LINE__); while (1);}} while (0) +#define SDC_WARN_ON(v) do {if(v) {printf("WARN at %s:%d!\n", __func__, __LINE__);}} while (0) + +#define mmc_mdelay(ms) OS_MSleep(ms) +#define mmc_udelay(us) HAL_UDelay(us) + +#ifdef CONFIG_PM +#define CONFIG_SD_PM +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#if ((defined SDIO_EXCLUSIVE_HOST) || (defined SD_EXCLUSIVE_HOST) || (defined MMC_EXCLUSIVE_HOST)) +#define CONFIG_SDC_EXCLUSIVE_HOST +#endif + +struct scatterlist { + void *buffer; + uint32_t len; +}; + +/* IDMC structure */ +typedef struct { + uint32_t config; + +#define SDXC_IDMAC_DES0_DIC BIT(1) /* disable interrupt on completion */ +#define SDXC_IDMAC_DES0_LD BIT(2) /* 1-this data buffer is the last buffer */ +#define SDXC_IDMAC_DES0_FD BIT(3) /* 1-data buffer is the first buffer, 0-data buffer contained in the next descriptor is the first data buffer */ +#define SDXC_IDMAC_DES0_CH BIT(4) /* 1-the 2nd address in the descriptor is the next descriptor address */ +#define SDXC_IDMAC_DES0_ER BIT(5) /* 1-last descriptor flag when using dual data buffer in descriptor */ +#define SDXC_IDMAC_DES0_CES BIT(30) /* transfer error flag */ +#define SDXC_IDMAC_DES0_OWN BIT(31) /* des owner:1-idma owns it, 0-host owns it */ + + uint32_t data_buf1_sz :16, + data_buf2_sz :16; + + uint32_t buf_addr_ptr1; + uint32_t buf_addr_ptr2; +} smc_idma_des; + +typedef enum +{ + SDC_STATE_RESET = 0x00, /* Peripheral is not yet Initialized */ + SDC_STATE_READY = 0x02, /* Peripheral Initialized and ready for use */ + SDC_STATE_BUSY = 0x04, /* An internal process is ongoing */ + SDC_STATE_ERROR = 0x08 /* Error */ +} SDC_StateTypeDef; + +struct mmc_bus_ops { + int (*suspend)(struct mmc_host *); + int (*resume)(struct mmc_host *); +}; + +struct mmc_host { + uint32_t sdc_id; + struct mmc_card *card; +#ifdef CONFIG_SDC_SUPPORT_1V8 + uint32_t voltage; +#define SDC_WOLTAGE_3V3 (0) +#define SDC_WOLTAGE_1V8 (1) +#define SDC_WOLTAGE_1V2 (2) +#define SDC_WOLTAGE_OFF (3) +#define SDC_WOLTAGE_ON (4) + uint32_t voltage_switching; +#endif + volatile uint32_t present; + uint32_t power_on; + uint32_t suspend; + uint32_t int_err; /* for Interrupt Controller */ + + uint32_t int_use; /* Control */ + uint32_t int_sum; /* interrupt summary */ + uint32_t trans_done; + uint32_t dma_done; + uint32_t buswidth; /* current card bus width */ + uint32_t blkcnt; + + smc_idma_des *dma_hdle; + + /* host specific block data */ + uint32_t max_seg_size; /* see blk_queue_max_segment_size */ + uint32_t max_segs; /* see blk_queue_max_segments */ + uint32_t max_req_size; /* maximum number of bytes in one req */ + uint32_t max_blk_size; /* maximum size of one mmc block */ + uint32_t max_blk_count; /* maximum number of blocks in one req */ + uint32_t ocr_avail; + +#define MMC_VDD_165_195 0x00000080 /* VDD voltage 1.65 - 1.95 */ +#define MMC_VDD_20_21 0x00000100 /* VDD voltage 2.0 ~ 2.1 */ +#define MMC_VDD_21_22 0x00000200 /* VDD voltage 2.1 ~ 2.2 */ +#define MMC_VDD_22_23 0x00000400 /* VDD voltage 2.2 ~ 2.3 */ +#define MMC_VDD_23_24 0x00000800 /* VDD voltage 2.3 ~ 2.4 */ +#define MMC_VDD_24_25 0x00001000 /* VDD voltage 2.4 ~ 2.5 */ +#define MMC_VDD_25_26 0x00002000 /* VDD voltage 2.5 ~ 2.6 */ +#define MMC_VDD_26_27 0x00004000 /* VDD voltage 2.6 ~ 2.7 */ +#define MMC_VDD_27_28 0x00008000 /* VDD voltage 2.7 ~ 2.8 */ +#define MMC_VDD_28_29 0x00010000 /* VDD voltage 2.8 ~ 2.9 */ +#define MMC_VDD_29_30 0x00020000 /* VDD voltage 2.9 ~ 3.0 */ +#define MMC_VDD_30_31 0x00040000 /* VDD voltage 3.0 ~ 3.1 */ +#define MMC_VDD_31_32 0x00080000 /* VDD voltage 3.1 ~ 3.2 */ +#define MMC_VDD_32_33 0x00100000 /* VDD voltage 3.2 ~ 3.3 */ +#define MMC_VDD_33_34 0x00200000 /* VDD voltage 3.3 ~ 3.4 */ +#define MMC_VDD_34_35 0x00400000 /* VDD voltage 3.4 ~ 3.5 */ +#define MMC_VDD_35_36 0x00800000 /* VDD voltage 3.5 ~ 3.6 */ + + uint32_t caps; /* Host capabilities */ + +#define MMC_CAP_4_BIT_DATA (1 << 0) /* Can the host do 4 bit transfers */ +#define MMC_CAP_MMC_HIGHSPEED (1 << 1) /* Can do MMC high-speed timing */ +#define MMC_CAP_SD_HIGHSPEED (1 << 2) /* Can do SD high-speed timing */ +#define MMC_CAP_SDIO_IRQ (1 << 3) /* Can signal pending SDIO IRQs */ +#define MMC_CAP_SPI (1 << 4) /* Talks only SPI protocols */ +#define MMC_CAP_NEEDS_POLL (1 << 5) /* Needs polling for card-detection */ +#define MMC_CAP_8_BIT_DATA (1 << 6) /* Can the host do 8 bit transfers */ + +#define MMC_CAP_NONREMOVABLE (1 << 8) /* Nonremovable e.g. eMMC */ +#define MMC_CAP_WAIT_WHILE_BUSY (1 << 9) /* Waits while card is busy */ +#define MMC_CAP_ERASE (1 << 10) /* Allow erase/trim commands */ +#define MMC_CAP_1_8V_DDR (1 << 11) /* can support */ + /* DDR mode at 1.8V */ +#define MMC_CAP_1_2V_DDR (1 << 12) /* can support */ + /* DDR mode at 1.2V */ +#define MMC_CAP_POWER_OFF_CARD (1 << 13) /* Can power off after boot */ +#define MMC_CAP_BUS_WIDTH_TEST (1 << 14) /* CMD14/CMD19 bus width ok */ +#define MMC_CAP_UHS_SDR12 (1 << 15) /* Host supports UHS SDR12 mode */ +#define MMC_CAP_UHS_SDR25 (1 << 16) /* Host supports UHS SDR25 mode */ +#define MMC_CAP_UHS_SDR50 (1 << 17) /* Host supports UHS SDR50 mode */ +#define MMC_CAP_UHS_SDR104 (1 << 18) /* Host supports UHS SDR104 mode */ +#define MMC_CAP_UHS_DDR50 (1 << 19) /* Host supports UHS DDR50 mode */ +#define MMC_CAP_SET_XPC_330 (1 << 20) /* Host supports >150mA current at 3.3V */ +#define MMC_CAP_SET_XPC_300 (1 << 21) /* Host supports >150mA current at 3.0V */ +#define MMC_CAP_SET_XPC_180 (1 << 22) /* Host supports >150mA current at 1.8V */ +#define MMC_CAP_DRIVER_TYPE_A (1 << 23) /* Host supports Driver Type A */ +#define MMC_CAP_DRIVER_TYPE_C (1 << 24) /* Host supports Driver Type C */ +#define MMC_CAP_DRIVER_TYPE_D (1 << 25) /* Host supports Driver Type D */ +#define MMC_CAP_MAX_CURRENT_200 (1 << 26) /* Host max current limit is 200mA */ +#define MMC_CAP_MAX_CURRENT_400 (1 << 27) /* Host max current limit is 400mA */ +#define MMC_CAP_MAX_CURRENT_600 (1 << 28) /* Host max current limit is 600mA */ +#define MMC_CAP_MAX_CURRENT_800 (1 << 29) /* Host max current limit is 800mA */ +#define MMC_CAP_CMD23 (1 << 30) /* CMD23 supported. */ +#define MMC_CAP_HW_RESET (1 << 31) /* Hardware reset */ + + uint32_t caps2; /* More host capabilities */ + +#define MMC_CAP2_BOOTPART_NOACC (1 << 0) /* Boot partition no access */ +#define MMC_CAP2_CACHE_CTRL (1 << 1) /* Allow cache control */ +#define MMC_CAP2_POWEROFF_NOTIFY (1 << 2) /* Notify poweroff supported */ +#define MMC_CAP2_NO_MULTI_READ (1 << 3) /* Multiblock reads don't work */ +#define MMC_CAP2_NO_SLEEP_CMD (1 << 4) /* Don't allow sleep command */ +#define MMC_CAP2_HS200_1_8V_SDR (1 << 5) /* can support */ +#define MMC_CAP2_HS200_1_2V_SDR (1 << 6) /* can support */ +#define MMC_CAP2_HS200 (MMC_CAP2_HS200_1_8V_SDR | MMC_CAP2_HS200_1_2V_SDR) +#define MMC_CAP2_BROKEN_VOLTAGE (1 << 7) /* Use the broken voltage */ +#define MMC_CAP2_DETECT_ON_ERR (1 << 8) /* On I/O err check card removal */ +#define MMC_CAP2_HC_ERASE_SZ (1 << 9) /* High-capacity erase size */ + +#ifdef CONFIG_SDC_OS_USED + SDC_Semaphore lock; + SDC_Mutex thread_lock; +#ifdef CONFIG_DETECT_CARD + SDC_Timer cd_timer; +#endif +#ifdef CONFIG_SDC_EXCLUSIVE_HOST + SDC_Semaphore exclusive_lock; /* lock for claim and bus ops */ +#endif +#endif + + //uint8_t bus_width; /* data bus width */ + uint32_t clk; + +#define MMC_BUS_WIDTH_1 0 +#define MMC_BUS_WIDTH_4 2 +#define MMC_BUS_WIDTH_8 3 + + struct mmc_request *mrq; + +#define SDC_WAIT_NONE BIT(0) +#define SDC_WAIT_CMD_DONE BIT(1) +#define SDC_WAIT_DATA_OVER BIT(2) +#define SDC_WAIT_AUTOCMD_DONE BIT(3) +#define SDC_WAIT_IDMA_DONE BIT(4) +#define SDC_WAIT_IDMA_ERR BIT(5) +#define SDC_WAIT_ERROR BIT(6) +#define SDC_WAIT_RXDATA_OVER (SDC_WAIT_DATA_OVER|SDC_WAIT_IDMA_DONE) +#define SDC_WAIT_RXAUTOCMD_DONE (SDC_WAIT_AUTOCMD_DONE|SDC_WAIT_IDMA_DONE) +#define SDC_WAIT_SWITCH1V8 BIT(7) +#define SDC_WAIT_FINALIZE BIT(8) + volatile uint32_t smc_cmd; + + uint32_t wait; +#ifdef CONFIG_SDIO_IRQ_SUPPORT + uint32_t sdio_int; +#endif +#ifdef CONFIG_SD_PM + const struct mmc_bus_ops *bus_ops; /* current bus driver */ + uint32_t pm_flags; /* requested pm features */ + uint32_t pm_caps; /* supported pm features */ +#endif +#ifdef __CONFIG_ARCH_APP_CORE + SDC_InitTypeDef param; + GPIO_Port cd_port; + GPIO_Pin cd_pin; +#endif +#ifdef CONFIG_SDC_READONLY_USED + uint32_t read_only; + GPIO_PinMuxParam ro_gpio; +#endif + SDC_StateTypeDef State; +}; + +#define SDC_MAX_CPU_TRANS_LEN 64 + +//#define SMC_LOW_POWER_MODE 0 /* 1--Close Clock when Idle, 0--Clock always on */ +/* IDMA control */ +#define IDMAC_DES_MODE 0 /* 0-chain mode, 1-fix skip length */ + +#define SMC_RX_WLEVEL 7 +#define SMC_TX_WLEVEL 8 +#define BURST_SIZE 2 + +#define IDMA_MAX_TBKNUM_ONETIME 16 + +/* registers define */ +#ifdef __CONFIG_ARCH_APP_CORE +#define SMC0_BASE (0x40002000) +#else +#define SMC0_BASE (0xA0001000) +#endif + +#define SDXC_REG_GCTRL (SMC0_BASE + 0x00) /* SMC Global Control Register */ +#define SDXC_REG_CLKCR (SMC0_BASE + 0x04) /* SMC Clock Control Register */ +#define SDXC_REG_TMOUT (SMC0_BASE + 0x08) /* SMC Time Out Register */ +#define SDXC_REG_WIDTH (SMC0_BASE + 0x0C) /* SMC Bus Width Register */ +#define SDXC_REG_BLKSZ (SMC0_BASE + 0x10) /* SMC Block Size Register */ +#define SDXC_REG_BCNTR (SMC0_BASE + 0x14) /* SMC Byte Count Register */ +#define SDXC_REG_CMDR (SMC0_BASE + 0x18) /* SMC Command Register */ +#define SDXC_REG_CARG (SMC0_BASE + 0x1C) /* SMC Argument Register */ +#define SDXC_REG_RESP0 (SMC0_BASE + 0x20) /* SMC Response Register 0 */ +#define SDXC_REG_RESP1 (SMC0_BASE + 0x24) /* SMC Response Register 1 */ +#define SDXC_REG_RESP2 (SMC0_BASE + 0x28) /* SMC Response Register 2 */ +#define SDXC_REG_RESP3 (SMC0_BASE + 0x2C) /* SMC Response Register 3 */ +#define SDXC_REG_IMASK (SMC0_BASE + 0x30) /* SMC Interrupt Mask Register */ +#define SDXC_REG_MISTA (SMC0_BASE + 0x34) /* SMC Masked Interrupt Status Register */ +#define SDXC_REG_RINTR (SMC0_BASE + 0x38) /* SMC Raw Interrupt Status Register */ +#define SDXC_REG_STAS (SMC0_BASE + 0x3C) /* SMC Status Register */ +#define SDXC_REG_FTRGL (SMC0_BASE + 0x40) /* SMC FIFO Threshold Watermark Register */ +#define SDXC_REG_FUNS (SMC0_BASE + 0x44) /* SMC Function Select Register */ +#define SDXC_REG_CBCR (SMC0_BASE + 0x48) /* SMC CIU Byte Count Register */ +#define SDXC_REG_BBCR (SMC0_BASE + 0x4C) /* SMC BIU Byte Count Register */ +#define SDXC_REG_DBGC (SMC0_BASE + 0x50) /* SMC Debug Enable Register */ +#define SDXC_REG_A12A (SMC0_BASE + 0x58) /* SMC auto command 12 argument */ +#define SDXC_REG_NTSR (SMC0_BASE + 0x5C) /* SMC NewTiming Set Register(RX TX) */ +#define SDXC_REG_SDEG (SMC0_BASE + 0x60) /* SMC NewTiming Set debg */ +#define SDXC_REG_HWST (SMC0_BASE + 0x78) /* SMC SMC hardware reset register */ +#define SDXC_REG_DMAC (SMC0_BASE + 0x80) /* SMC IDMAC Control Register */ +#define SDXC_REG_DLBA (SMC0_BASE + 0x84) /* SMC IDMAC Descriptor List Base Address Register */ +#define SDXC_REG_IDST (SMC0_BASE + 0x88) /* SMC IDMAC Status Register */ +#define SDXC_REG_IDIE (SMC0_BASE + 0x8C) /* SMC IDMAC Interrupt Enable Register */ +#define SDXC_REG_CHDA (SMC0_BASE + 0x90) /* SMC Current Host Descriptor Address Register */ +#define SDXC_REG_CBDA (SMC0_BASE + 0x94) /* SMC Current Buffer Descriptor Address Register */ +#define SDXC_REG_THLDC (SMC0_BASE + 0x100) /* SMC Threshold Control Register */ +#define SDXC_REG_DSBD (SMC0_BASE + 0x10C) +#define SDXC_REG_RESP_CRC (SMC0_BASE + 0x110) +#define SDXC_REG_DAT7_CRC (SMC0_BASE + 0x114) +#define SDXC_REG_DAT6_CRC (SMC0_BASE + 0x118) +#define SDXC_REG_DAT5_CRC (SMC0_BASE + 0x11C) +#define SDXC_REG_DAT4_CRC (SMC0_BASE + 0x120) +#define SDXC_REG_DAT3_CRC (SMC0_BASE + 0x124) +#define SDXC_REG_DAT2_CRC (SMC0_BASE + 0x128) +#define SDXC_REG_DAT1_CRC (SMC0_BASE + 0x12C) +#define SDXC_REG_DAT0_CRC (SMC0_BASE + 0x130) +#define SDXC_REG_CRC_STA (SMC0_BASE + 0x134) +#define SDXC_REG_FIFO (SMC0_BASE + 0x200) /* SMC FIFO Access Address */ + +#define SDXC_REG_FCTL (SMC0_BASE + 0x64) /* SMC FIFO Access Address */ +#define SDXC_REG_FCTL_OS (0x64) /* SMC FIFO Access Address */ + +/* global control register */ +#define SDXC_SoftReset BIT(0 ) +#define SDXC_FIFOReset BIT(1 ) +#define SDXC_DMAReset BIT(2 ) +#define SDXC_HWReset (SDXC_SoftReset|SDXC_FIFOReset|SDXC_DMAReset) +#define SDXC_INTEnb BIT(4 ) +#define SDXC_DMAEnb BIT(5 ) +#define SDXC_DebounceEnb BIT(8 ) +#define SDXC_DDR_MODE BIT(10) +#define SDXC_MemAccessDone BIT(29) +#define SDXC_AccessDoneDirect BIT(30) +#define SDXC_ACCESS_BY_AHB BIT(31) +#define SDXC_ACCESS_BY_DMA (0x0U << 31) + +/* Clock control */ +#define SDXC_CardClkOn (0x1U << 16) +#define SDXC_LowPowerOn (0x1U << 17) + +/* bus width */ +#define SDXC_WIDTH1 (0) +#define SDXC_WIDTH4 (1) +#define SDXC_WIDTH8 (2) + +/* Struct for SMC Commands */ +#define SDXC_CMD_OPCODE (0x3F ) /* 0x00000040 */ +#define SDXC_RspExp BIT(6 ) /* 0x00000080 */ +#define SDXC_LongRsp BIT(7 ) /* 0x00000100 */ +#define SDXC_CheckRspCRC BIT(8 ) /* 0x00000200 */ +#define SDXC_DataExp BIT(9 ) /* 0x00000000 */ +#define SDXC_Read (0x0U<<10 ) /* 0x00000400 */ +#define SDXC_Write BIT(10) /* 0x00000000 */ +#define SDXC_Blockmod (0x0U<<11 ) /* 0x00000800 */ +#define SDXC_Seqmod BIT(11) /* 0x00001000 */ +#define SDXC_SendAutoStop BIT(12) /* 0x00002000 */ +#define SDXC_WaitPreOver BIT(13) /* 0x00004000 */ +#define SDXC_StopAbortCMD BIT(14) /* 0x00008000 */ +#define SDXC_SendInitSeq BIT(15) /* 0x00200000 */ +#define SDXC_UPCLKOnly BIT(21) /* 0x00400000 */ +#define SDXC_RdCEATADev BIT(22) /* 0x00800000 */ +#define SDXC_CCSExp BIT(23) /* 0x01000000 */ +#define SDXC_EnbBoot BIT(24) /* 0x02000000 */ +#define SDXC_AltBootOpt BIT(25) /* 0x00000000 */ +#define SDXC_MandBootOpt (0x0U<<25) /* 0x04000000 */ +#define SDXC_BootACKExp BIT(26) /* 0x08000000 */ +#define SDXC_DisableBoot BIT(27) /* 0x10000000 */ +#define SDXC_VolSwitch BIT(28) /* 0x80000000 */ +#define SDXC_Start BIT(31) + +/* Struct for Intrrrupt Information */ +#define SDXC_RespErr BIT(1) /* 0x00000002 */ +#define SDXC_CmdDone BIT(2) /* 0x00000004 */ +#define SDXC_DataOver BIT(3) /* 0x00000008 */ +#define SDXC_TxDataReq BIT(4) /* 0x00000010 */ +#define SDXC_RxDataReq BIT(5) /* 0x00000020 */ +#define SDXC_RespCRCErr BIT(6) /* 0x00000040 */ +#define SDXC_DataCRCErr BIT(7) /* 0x00000080 */ +#define SDXC_RespTimeout BIT(8) /* 0x00000100 */ +#define SDXC_ACKRcv BIT(8) /* 0x00000100 */ +#define SDXC_DataTimeout BIT(9) /* 0x00000200 */ +#define SDXC_BootStart BIT(9) /* 0x00000200 */ +#define SDXC_DataStarve BIT(10) /* 0x00000400 */ +#define SDXC_VolChgDone BIT(10) /* 0x00000400 */ +#define SDXC_FIFORunErr BIT(11) /* 0x00000800 */ +#define SDXC_HardWLocked BIT(12) /* 0x00001000 */ +#define SDXC_StartBitErr BIT(13) /* 0x00002000 */ +#define SDXC_AutoCMDDone BIT(14) /* 0x00004000 */ +#define SDXC_EndBitErr BIT(15) /* 0x00008000 */ +#define SDXC_SDIOInt BIT(16) /* 0x00010000 */ +#define SDXC_CardInsert BIT(30) /* 0x40000000 */ +#define SDXC_CardRemove BIT(31) /* 0x80000000 */ +#define SDXC_IntErrBit (SDXC_RespErr | SDXC_RespCRCErr | SDXC_DataCRCErr | SDXC_RespTimeout | SDXC_DataTimeout | \ + SDXC_FIFORunErr | SDXC_HardWLocked | SDXC_StartBitErr | SDXC_EndBitErr) /* 0xbbc2 */ +/* status */ +#define SDXC_RXWLFlag BIT(0) +#define SDXC_TXWLFlag BIT(1) +#define SDXC_FIFOEmpty BIT(2) +#define SDXC_FIFOFull BIT(3) +#define SDXC_CardPresent BIT(8) +#define SDXC_CardDataBusy BIT(9) +#define SDXC_DataFSMBusy BIT(10) +#define SDXC_DMAReq BIT(31) + +/* Function select */ +#define SDXC_CEATAOn (0xceaaU << 16) +#define SDXC_SendIrqRsp BIT(0) +#define SDXC_SDIORdWait BIT(1) +#define SDXC_AbtRdData BIT(2) +#define SDXC_SendCCSD BIT(8) +#define SDXC_SendAutoStopCCSD BIT(9) +#define SDXC_CEATADevIntEnb BIT(10) +/* status bit */ +#define SDXC_CardBusy BIT(9) +/* IDMA controller bus mod bit field */ +#define SDXC_IDMACSoftRST BIT(0) +#define SDXC_IDMACFixBurst BIT(1) +#define SDXC_IDMACIDMAOn BIT(7) +#define SDXC_IDMACRefetchDES BIT(31) + +/* IDMA status bit field */ +#define SDXC_IDMACTransmitInt BIT(0) +#define SDXC_IDMACReceiveInt BIT(1) +#define SDXC_IDMACFatalBusErr BIT(2) +#define SDXC_IDMACDesInvalid BIT(4) +#define SDXC_IDMACCardErrSum BIT(5) +#define SDXC_IDMACNormalIntSum BIT(8) +#define SDXC_IDMACAbnormalIntSum BIT(9) +#define SDXC_IDMACHostAbtInTx BIT(10) +#define SDXC_IDMACHostAbtInRx BIT(10) +#define SDXC_IDMACIdle (0x0U << 13) +#define SDXC_IDMACSuspend (0x1U << 13) +#define SDXC_IDMACDESCRd (0x2U << 13) +#define SDXC_IDMACDESCCheck (0x3U << 13) +#define SDXC_IDMACRdReqWait (0x4U << 13) +#define SDXC_IDMACWrReqWait (0x5U << 13) +#define SDXC_IDMACRd (0x6U << 13) +#define SDXC_IDMACWr (0x7U << 13) +#define SDXC_IDMACDESCClose (0x8U << 13) + +#define SDXC_IDMA_OVER (SDXC_IDMACTransmitInt|SDXC_IDMACReceiveInt|SDXC_IDMACNormalIntSum) +#define SDXC_IDMA_ERR (SDXC_IDMACFatalBusErr|SDXC_IDMACDesInvalid|SDXC_IDMACCardErrSum|SDXC_IDMACAbnormalIntSum) + +/* + * the max length of buffer which IDMA description supported is 8192, + * transport data by several IDMA descriptions if data lenght more than 8192, + * and the max number IDMA description support is 1024. + * which meas the mas length transport data is 1024 * 8192 = 8MB in a signal transfer. + */ +#ifdef __CONFIG_ARCH_APP_CORE +#define SDXC_MAX_TRANS_LEN (1 << 18) /* max len is 256K */ +#else +#define SDXC_MAX_TRANS_LEN (1 << 14) /* max len is 16K */ +#endif +#define SDXC_DES_NUM_SHIFT (13) +#define SDXC_DES_BUFFER_MAX_LEN (1 << SDXC_DES_NUM_SHIFT) /* 8192 == 1<<13; */ +#define SDXC_MAX_DES_NUM (SDXC_MAX_TRANS_LEN >> SDXC_DES_NUM_SHIFT) /* 2 is the least */ +#define SDXC_DES_MODE (0) /* 0-chain mode, 1-fix length skip */ + +extern struct mmc_host *_mci_host; + +/* + * These flags are used to describe power management features that + * some cards (typically SDIO cards) might wish to benefit from when + * the host system is being suspended. There are several layers of + * abstractions involved, from the host controller driver, to the MMC core + * code, to the SDIO core code, to finally get to the actual SDIO function + * driver. This file is therefore used for common definitions shared across + * all those layers. + */ + +#define MMC_PM_KEEP_POWER (1 << 0) /* preserve card power during suspend */ +#define MMC_PM_WAKE_SDIO_IRQ (1 << 1) /* wake up host system on SDIO IRQ assertion */ +#define MMC_PM_IGNORE_PM_NOTIFY (1 << 2) /* ignore mmc pm notify */ + +#ifdef CONFIG_SD_PM +static inline int mmc_card_keep_power(struct mmc_host *host) +{ + return host->pm_flags & MMC_PM_KEEP_POWER; +} + +static inline int mmc_card_wake_sdio_irq(struct mmc_host *host) +{ + return host->pm_flags & MMC_PM_WAKE_SDIO_IRQ; +} +#endif + +#ifdef CONFIG_SDC_READONLY_USED +extern int32_t HAL_SDC_Get_ReadOnly(struct mmc_host *host); +#endif +extern void HAL_SDC_Set_BusWidth(struct mmc_host *host, uint32_t width); +extern int32_t HAL_SDC_Update_Clk(struct mmc_host *host, uint32_t clk); +extern uint32_t HAL_SDC_Is_Busy(struct mmc_host *host); +extern int32_t HAL_SDC_Clk_PWR_Opt(struct mmc_host *host, uint32_t oclk_en, uint32_t pwr_save); +#ifdef CONFIG_SDC_EXCLUSIVE_HOST +extern int32_t HAL_SDC_Claim_Host(struct mmc_host *host); +extern void HAL_SDC_Release_Host(struct mmc_host *host); +#else +static inline int32_t HAL_SDC_Claim_Host(struct mmc_host *host) { return 0; } +static inline void HAL_SDC_Release_Host(struct mmc_host *host) { ; } +#endif +extern int32_t HAL_SDC_PowerOn(struct mmc_host *host); +extern int32_t HAL_SDC_PowerOff(struct mmc_host *host); + +extern int32_t HAL_SDC_Request(struct mmc_host *host, struct mmc_request *mrq); + +#ifdef __cplusplus +} +#endif + +#endif /* _DRIVER_CHIP_SDMMC_SDHOST_H_ */ diff --git a/platform/mcu/xr871/src/driver/chip/sdmmc/test.c b/platform/mcu/xr871/src/driver/chip/sdmmc/test.c new file mode 100644 index 0000000000..0d7139c272 --- /dev/null +++ b/platform/mcu/xr871/src/driver/chip/sdmmc/test.c @@ -0,0 +1,261 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include "sys/io.h" +#include "sys/xr_debug.h" + +#include "kernel/os/os_time.h" +#include "kernel/os/os_semaphore.h" + +#include "driver/chip/hal_def.h" +#include "driver/chip/hal_ccm.h" +#include "driver/chip/sdmmc/hal_sdhost.h" +#include "driver/chip/sdmmc/sdmmc.h" + +#define TEST_SD +#define TEST_SD_WRITE + +#ifdef TEST_SD + +#define SIZE_1K (1024) +#define SIZE_1M (SIZE_1K*SIZE_1K) + +#define READ_WRITE_SINGLE_SIZE (16*SIZE_1K) +#define READ_WRITE_TOTAL_SIZE (8*SIZE_1M) + +static uint8_t wbuf[READ_WRITE_SINGLE_SIZE]; +static uint8_t rbuf[READ_WRITE_SINGLE_SIZE]; + +static struct mmc_card card; + +void print_hex_dump_words(const void *addr, unsigned int len); + +#ifdef CONFIG_DETECT_CARD +static OS_Semaphore_t card_present_sem; + +void card_detect(uint32_t present) +{ + if (present) { + printf("%s insert\n", __func__); + OS_MSleep(500); /* wait voltage stable */ + + if (mmc_rescan(&card, 0)) { + printf("Initial card failed!!\n"); + return ; + } else + printf("Initial card success\n"); + OS_SemaphoreRelease(&card_present_sem); + } else { + printf("%s removed\n", __func__); + } +} +#endif + +int32_t mmc_test_init(void) +{ + SDC_InitTypeDef sdc_param; +#ifdef CONFIG_DETECT_CARD + uint32_t card_exist = 1; + + OS_SemaphoreCreate(&card_present_sem, 0, OS_SEMAPHORE_MAX_COUNT); + + XR_ASSERT((cd_mode != CARD_ALWAYS_PRESENT) && (cd_mode != CARD_DETECT_BY_GPIO_IRQ) && \ + (cd_mode != CARD_DETECT_BY_D3), 1, " cd_mode config err!\n"); + + sdc_param.cd_mode = cd_mode; + sdc_param.cd_cb = &card_detect; +#endif + HAL_SDC_Init(0, &sdc_param); + + return 0; +} + +int32_t mmc_test_exit(void) +{ + if (!card.host) + return 0; + + mmc_card_deinit(&card); + HAL_SDC_Deinit(0); + + return 0; +} + +struct mmc_card *mmc_scan_init(void) +{ + if (!mmc_rescan(&card, 0)) + return &card; + else + return NULL; +} + +int32_t mmc_test(uint32_t cd_mode) +{ + int32_t err; + uint32_t i, cnt = 0; + + mmc_test_init(); /* reinit is ok */ + + memset((void *)wbuf, 0x55, 128); + memset((void *)&wbuf[128], 0xaa, 128); + + for (i = 0; i < 256; i ++) + wbuf[256 + i] = i; + + memcpy((void *)&wbuf[512], (void *)wbuf, 512); + + /* scan card for detect card is exist? */ + if (mmc_rescan(&card, 0)) { + printf("Initial card failed!!\n"); +#ifdef CONFIG_DETECT_CARD + card_exist = 0; +#else + goto out; +#endif + } else { + printf("Initial card success\n"); + } + + while (cnt++ < 100) { + uint32_t throuth_mb, throuth_kb; + OS_Time_t tick_use; +#ifdef CONFIG_DETECT_CARD + if (!card_exist || (cd_mode != CARD_ALWAYS_PRESENT)) + OS_SemaphoreWait(&card_present_sem, OS_WAIT_FOREVER); +#endif + + printf("%s,%d test count:%d\n", __func__, __LINE__, cnt); + +#ifdef TEST_SD_WRITE + tick_use = OS_GetTicks(); + err = mmc_block_write(&card, wbuf, 0, 1); + tick_use = OS_GetTicks() - tick_use; + if (err) { + goto err_out; + } else + printf("%s 1 block write ok, 512B use:%d ms\n", __func__, + (uint32_t)OS_TicksToMSecs(tick_use)); +#endif + memset((void *)rbuf, 0, 512); + tick_use = OS_GetTicks(); + err = mmc_block_read(&card, rbuf, 0, 1); + tick_use = OS_GetTicks() - tick_use; + if (err) { + goto err_out; + } else { + printf("%s 1 block read ok, 512B use:%d ms\n", __func__, + (uint32_t)OS_TicksToMSecs(tick_use)); +#ifndef TEST_SD_WRITE + print_hex_dump_words(rbuf, 512); +#endif + } +#ifdef TEST_SD_WRITE + if (memcmp((void *)wbuf, (void *)rbuf, 512)) { + goto err_out; + } else + printf("%s,%d mmc 1 block rw ok\n", __func__, __LINE__); + + tick_use = OS_GetTicks(); + for (i = 0; i < READ_WRITE_TOTAL_SIZE/READ_WRITE_SINGLE_SIZE; i++) { + err = mmc_block_write(&card, wbuf, 3 + i * (READ_WRITE_SINGLE_SIZE/512), + READ_WRITE_SINGLE_SIZE/512); + if (err) + break; + if (i % 50 == 0) + printf("%s, wirite cnt:%d\n", __func__, i); + } + tick_use = OS_GetTicks() - tick_use; + if (err) { + printf("%s,%d mmc mult blocks write err!\n", __func__, __LINE__); + goto err_out; + } else { + throuth_kb = READ_WRITE_TOTAL_SIZE/SIZE_1K*1000/(uint32_t)OS_TicksToMSecs(tick_use); + throuth_mb = throuth_kb/1000; + printf("%s mult blocks write ok, %d MB use:%d ms, throughput:%d.%d MB/S\n", + __func__, READ_WRITE_TOTAL_SIZE/SIZE_1M, (uint32_t)OS_TicksToMSecs(tick_use), + throuth_mb, throuth_kb - throuth_mb); + } +#endif + + tick_use = OS_GetTicks(); + for (i = 0; i < READ_WRITE_TOTAL_SIZE/READ_WRITE_SINGLE_SIZE; i++) { + err = mmc_block_read(&card, rbuf, 3 + i * (READ_WRITE_SINGLE_SIZE/512), + READ_WRITE_SINGLE_SIZE/512); + if (err) + break; + } + tick_use = OS_GetTicks() - tick_use; + if (err) { + printf("%s,%d mmc mult blocks read err!\n", __func__, __LINE__); + goto err_out; + } else { + throuth_kb = READ_WRITE_TOTAL_SIZE/SIZE_1K*1000/(uint32_t)OS_TicksToMSecs(tick_use); + throuth_mb = throuth_kb/1000; + printf("%s mult blocks read ok, %d MB use:%d ms, throughput:%d.%d MB/S\n", + __func__, READ_WRITE_TOTAL_SIZE/SIZE_1M, (uint32_t)OS_TicksToMSecs(tick_use), + throuth_mb, throuth_kb - throuth_mb); + } + + memset((void *)rbuf, 0, READ_WRITE_SINGLE_SIZE); + err = mmc_block_read(&card, rbuf, 3, READ_WRITE_SINGLE_SIZE/512); + if (err) { + goto err_out; + } else + printf("%s %d blocks read ok\n", __func__, READ_WRITE_SINGLE_SIZE/512); + +#ifdef TEST_SD_WRITE + if (memcmp((void *)wbuf, (void *)rbuf, 1024)) { /* check 1024B */ + printf("%s %d mmc blocks rw failed\n", __func__, READ_WRITE_SINGLE_SIZE/512); + goto err_out; + } else + printf("%s %d mmc blocks rw ok\n", __func__, READ_WRITE_SINGLE_SIZE/512); +#endif + + OS_MSleep(1000); + } + + mmc_test_exit(); + + return 0; + +err_out: +#ifdef TEST_SD_WRITE + printf("%s,%d mmc block rw failed\n", __func__, __LINE__); + printf("rbuf:\n"); + print_hex_dump_words(rbuf, SIZE_1K); + printf("wbuf:\n"); + print_hex_dump_words(wbuf, 512); +#endif +out: + return -1; +} +#endif /* TEST_SD */ diff --git a/platform/mcu/xr871/src/driver/chip/system_chip.c b/platform/mcu/xr871/src/driver/chip/system_chip.c new file mode 100644 index 0000000000..82045e9bd7 --- /dev/null +++ b/platform/mcu/xr871/src/driver/chip/system_chip.c @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "hal_base.h" +#include "driver/chip/system_chip.h" +#include "pm/pm.h" + +uint32_t SystemCoreClock; +extern const unsigned char __RAM_BASE[]; /* SRAM start address */ + +/** + * @brief Initialize the chip system, including power, FPU setting, vector + * table location, clock, etc. + * @return None + */ +void SystemInit(void) +{ +#ifdef __CONFIG_CHIP_XR871 + HAL_PRCM_SetDCDCVoltage(PRCM_DCDC_VOLT_1V51); + HAL_PRCM_SetSRAMVoltage(PRCM_SRAM_VOLT_1V10, PRCM_SRAM_VOLT_0V70); +#if 0 + /* overwite efuse values */ + HAL_MODIFY_REG(PRCM->DCDC_PARAM_CTRL, + PRCM_DCDC_BANDGAP_TRIM_MASK, + 1U << PRCM_DCDC_BANDGAP_TRIM_SHIFT); + HAL_MODIFY_REG(PRCM->DIG_LDO_PARAM, + PRCM_DIG_LDO_BANDGAP_TRIM_MASK, + 2U << PRCM_DIG_LDO_BANDGAP_TRIM_SHIFT); +#endif +#endif + + SCB->VTOR = (uint32_t)__RAM_BASE; /* Vector Table Relocation in Internal SRAM. */ + +#if ((__FPU_PRESENT == 1) && (__FPU_USED == 1)) + /* FPU settings, set CP10 and CP11 Full Access */ + SCB->CPACR |= ((3UL << 20) | (3UL << 22)); +#endif + + /* set clock */ + HAL_BoardIoctl(HAL_BIR_CHIP_CLOCK_INIT, 0, 0); + + SystemCoreClock = HAL_GetCPUClock(); + pm_init(); + HAL_NVIC_Init(); + HAL_CCM_Init(); +} + +/** + * @brief DeInitialize the chip system by disabling and cleaning the system IRQ + * @param flag Rest system clock if bit SYSTEM_DEINIT_FLAG_RESET_CLK is set in + * the flag + * @return None + */ +void SystemDeInit(uint32_t flag) +{ + /* disable irq0~63 */ + NVIC->ICER[0] = NVIC->ISER[0]; + NVIC->ICER[1] = NVIC->ISER[1]; + + /* clear irq pending0~63 */ + NVIC->ICPR[0] = NVIC->ISPR[0]; + NVIC->ICPR[1] = NVIC->ISPR[1]; + + /* disable systick irq */ + SysTick->CTRL = 0; + + /* clear pendsv irq */ + SCB->ICSR = (SCB->ICSR & SCB_ICSR_PENDSVSET_Msk) >> (SCB_ICSR_PENDSVSET_Pos - SCB_ICSR_PENDSVCLR_Pos); + + /* clear systick irq */ + SCB->ICSR = (SCB->ICSR & SCB_ICSR_PENDSTSET_Msk) >> (SCB_ICSR_PENDSTSET_Pos - SCB_ICSR_PENDSTCLR_Pos); + + if (flag & SYSTEM_DEINIT_FLAG_RESET_CLK) { + /* reset clock for restart */ + HAL_PRCM_SetCPUAClk(PRCM_CPU_CLK_SRC_HFCLK, PRCM_SYS_CLK_FACTOR_80M); + HAL_PRCM_DisCLK1(PRCM_SYS_CLK_FACTOR_80M); + HAL_CCM_BusSetClock(CCM_AHB2_CLK_DIV_1, CCM_APB_CLK_SRC_HFCLK, CCM_APB_CLK_DIV_2); + HAL_PRCM_DisableSysPLL(); + } +} + +/** + * @brief Update system core (cpu) clock + * @return None + */ +void SystemCoreClockUpdate(void) +{ + SystemCoreClock = HAL_GetCPUClock(); +} diff --git a/platform/mcu/xr871/src/efpg/efpg.c b/platform/mcu/xr871/src/efpg/efpg.c new file mode 100644 index 0000000000..9452144d63 --- /dev/null +++ b/platform/mcu/xr871/src/efpg/efpg.c @@ -0,0 +1,199 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "efpg_i.h" +#include "efpg_debug.h" +#include "efpg/efpg.h" +#include "driver/chip/hal_uart.h" +#include "driver/chip/hal_efuse.h" + +static void efpg_task(void *arg) +{ + uint8_t *buf; + int32_t recv_len; + efpg_priv_t *efpg = arg; + efpg_state_t state = EFPG_STATE_NUM; + + if (efpg->start_cb) + efpg->start_cb(); + +efpg_reset: + efpg->is_cmd = 1; + efpg->ext_cmd = EFPG_NORMAL_CMD; + efpg->expt_len = EFPG_CMD_FRAME_LEN; + efpg->recv_len = 0; + efpg->op = EFPG_OP_NUM; + efpg->field = EFPG_FIELD_NUM; + efpg->start_bit_addr = 0; + efpg->bit_length = 0; + + while (1) { +efpg_continue: + if (efpg->is_cmd) + if (efpg->ext_cmd == EFPG_EXT_CMD) + buf = efpg->ext_cmd_frame; + else + buf = efpg->cmd_frame; + else + buf = efpg->data_frame; + recv_len = 0; + while (recv_len == 0) { + recv_len = HAL_UART_Receive_IT(efpg->uart_id, buf, efpg->expt_len, EFPG_RECV_TIMEOUT_MS); + } + + if (recv_len == -1) { + EFPG_ERR("UART receive failed\n"); + goto efpg_stop; + } + + if ((uint16_t)recv_len != efpg->expt_len) { + EFPG_WARN("%s(), %d, recv len %d, expt len %d\n", + __func__, __LINE__, recv_len, efpg->expt_len); + goto efpg_reset; + } + + efpg->recv_len = (uint16_t)recv_len; + + if (efpg->is_cmd) + state = efpg_cmd_frame_process(efpg); + else + state = efpg_data_frame_process(efpg); + + switch (state) { + case EFPG_STATE_CONTINUE: + goto efpg_continue; + case EFPG_STATE_RESET: + goto efpg_reset; + case EFPG_STATE_STOP: + goto efpg_stop; + default: + EFPG_ERR("invalid state %d\n", state); + goto efpg_stop; + } + } + +efpg_stop: + if (efpg->stop_cb) + efpg->stop_cb(); + + if (efpg) + efpg_free(efpg); + + OS_ThreadDelete(NULL); +} + +/** + * @brief Start communicating with the OEM programming tool + * @param[in] key Pointer to the key + * @param[in] key_len The length of the key + * @param[in] uart_id ID of the specified UART used to communication + * @param[in] start_cb Function called when start communication + * @param[in] stop_cb Function called when stop communication + * @return 0 on success, -1 on failure + * + * @note Just for OEM programming tool to entry eFuse programming mode. + */ +int efpg_start(uint8_t *key, uint8_t key_len, UART_ID uart_id, efpg_cb_t start_cb, efpg_cb_t stop_cb) +{ + efpg_priv_t *efpg; + OS_Thread_t thread; + + if ((key == NULL) || (key_len == 0) || (key_len > EFPG_KEY_LEN_MAX) || (uart_id == UART_NUM)) { + EFPG_ERR("key %p, key len %d, uart id %d\n", key, key_len, uart_id); + return -1; + } + + efpg = efpg_malloc(sizeof(efpg_priv_t)); + if (efpg == NULL) { + EFPG_ERR("malloc failed\n"); + return -1; + } + + efpg->uart_id = uart_id; + efpg->start_cb = start_cb; + efpg->stop_cb = stop_cb; + + efpg_memcpy(efpg->key, key, key_len); + efpg->key_len = key_len; + + OS_ThreadSetInvalid(&thread); + if (OS_ThreadCreate(&thread, + "efpg task", + efpg_task, + efpg, + OS_THREAD_PRIO_CONSOLE, + EFPG_THREAD_STACK_SIZE) != OS_OK) { + EFPG_ERR("create efpg task failed\n"); + return -1; + } + + return 0; +} + +/** + * @brief Read data of the specified field in eFuse + * @param[in] field The filed in eFuse + * @param[in] data Pointer to the data buffer + * @return 0 on success, -1 on failure + * + * @note The rest bit(s) in data will be cleared to be 0. + */ +int efpg_read(efpg_field_t field, uint8_t *data) +{ + if (data == NULL) { + EFPG_ERR("data %p\n", data); + return -1; + } + + uint16_t ack = efpg_read_field(field, data, 0, 0); + if (ack != EFPG_ACK_OK) { + EFPG_WARN("%s(), %d, ack %d\n", __func__, __LINE__, ack); + return -1; + } + + return 0; +} + +/** + * @brief Read data from user area (OEM reserved field) on EFUSE + * @param[in] start The first bit to be read in user area (OEM reserved field) + * @param[in] num Number of bits to be read + * @param[in] data Pointer to the data buffer + * @return 0 on success, -1 on failure + * + * @note The rest bit(s) in data will be cleared to be 0. + */ +int efpg_read_ua(uint32_t start, uint32_t num, uint8_t *data) +{ + if (efpg_read_field(EFPG_FIELD_UA, data, start, num) != EFPG_ACK_OK) { + return -1; + } + return 0; +} + diff --git a/platform/mcu/xr871/src/efpg/efpg.mk b/platform/mcu/xr871/src/efpg/efpg.mk new file mode 100644 index 0000000000..c405c6bdb1 --- /dev/null +++ b/platform/mcu/xr871/src/efpg/efpg.mk @@ -0,0 +1,6 @@ +NAME := efpg + +$(NAME)_TYPE := kernel +$(NAME)_SOURCES := efpg_efuse.c \ + efpg.c + diff --git a/platform/mcu/xr871/src/efpg/efpg_debug.h b/platform/mcu/xr871/src/efpg/efpg_debug.h new file mode 100644 index 0000000000..9bf939a0c9 --- /dev/null +++ b/platform/mcu/xr871/src/efpg/efpg_debug.h @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _EFPG_DEBUG_H_ +#define _EFPG_DEBUG_H_ + +#include +#include "sys/xr_util.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define EFPG_DBG_ON 0 +#define EFPG_WARN_ON 0 +#define EFPG_ERR_ON 1 +#define EFPG_ABORT_ON 0 + +#define EFPG_SYSLOG printf +#define EFPG_ABORT() sys_abort() + +#define EFPG_LOG(flags, fmt, arg...) \ + do { \ + if (flags) \ + EFPG_SYSLOG(fmt, ##arg); \ + } while (0) + +#define EFPG_DBG(fmt, arg...) EFPG_LOG(EFPG_DBG_ON, "[efpg] "fmt, ##arg) +#define EFPG_WARN(fmt, arg...) EFPG_LOG(EFPG_WARN_ON, "[efpg WARN] "fmt, ##arg) +#define EFPG_ERR(fmt, arg...) \ + do { \ + EFPG_LOG(EFPG_ERR_ON, "[efpg ERR] %s():%d, "fmt, \ + __func__, __LINE__, ##arg); \ + if (EFPG_ABORT_ON) \ + EFPG_ABORT(); \ + } while (0) + +#ifdef __cplusplus +} +#endif + +#endif /* _EFPG_DEBUG_H_ */ \ No newline at end of file diff --git a/platform/mcu/xr871/src/efpg/efpg_efuse.c b/platform/mcu/xr871/src/efpg/efpg_efuse.c new file mode 100644 index 0000000000..394695f4be --- /dev/null +++ b/platform/mcu/xr871/src/efpg/efpg_efuse.c @@ -0,0 +1,532 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "efpg_i.h" +#include "efpg_debug.h" +#include "driver/chip/hal_efuse.h" + +static int efpg_boot_hash_cmp(const uint8_t *data, const uint8_t *buf, uint8_t *err_cnt, + uint8_t *err_1st_no, uint8_t *err_2nd_no) +{ + uint8_t byte_cnt; + uint8_t bit_cnt; + + if (efpg_memcmp(data, buf, EFPG_BOOT_BUF_LEN) == 0) + return 0; + + *err_cnt = 0; + for (byte_cnt = 0; byte_cnt < EFPG_BOOT_BUF_LEN; byte_cnt++) { + if (data[byte_cnt] == buf[byte_cnt]) + continue; + + for (bit_cnt = 0; bit_cnt < 8; bit_cnt++) { + if ((data[byte_cnt] & (0x1 << bit_cnt)) == (buf[byte_cnt] & (0x1 << bit_cnt))) + continue; + + if (*err_cnt == 0) { + *err_1st_no = (byte_cnt << 3) + bit_cnt; + *err_cnt = 1; + } else if (*err_cnt == 1) { + *err_2nd_no = (byte_cnt << 3) + bit_cnt; + *err_cnt = 2; + } else { + return -1; + } + } + } + + return 0; +} + +static uint16_t efpg_read_hosc(uint8_t *data) +{ + if (HAL_EFUSE_Read(EFPG_HOSC_TYPE_START, EFPG_HOSC_TYPE_NUM, data) != HAL_OK) + return EFPG_ACK_RW_ERR; + + return EFPG_ACK_OK; +} + +static uint16_t efpg_read_boot(uint8_t *data) +{ + uint8_t tmp = 0; + uint8_t byte_cnt; + uint8_t bit_cnt; + + /* flag */ + if (HAL_EFUSE_Read(EFPG_BOOT_FLAG_START, EFPG_BOOT_FLAG_NUM, &tmp) != HAL_OK) + return EFPG_ACK_RW_ERR; + + if (tmp == 0) { + EFPG_WARN("%s(), %d, boot flag 0\n", __func__, __LINE__); + return EFPG_ACK_NODATA_ERR; + } + + /* hash */ + if (HAL_EFUSE_Read(EFPG_BOOT_HASH_START, EFPG_BOOT_HASH_NUM, data) != HAL_OK) + return EFPG_ACK_RW_ERR; + + /* correct bit error */ + if (HAL_EFUSE_Read(EFPG_BOOT_1ST_EN_START, EFPG_BOOT_1ST_EN_NUM, &tmp) != HAL_OK) + return EFPG_ACK_RW_ERR; + + if (tmp != 0) { + if (HAL_EFUSE_Read(EFPG_BOOT_1ST_NO_START, EFPG_BOOT_1ST_NO_NUM, &tmp) != HAL_OK) + return EFPG_ACK_RW_ERR; + byte_cnt = tmp >> 3; + bit_cnt = tmp & 0x07; + data[byte_cnt] ^= (0x1 << bit_cnt); + + if (HAL_EFUSE_Read(EFPG_BOOT_2ND_EN_START, EFPG_BOOT_2ND_EN_NUM, &tmp) != HAL_OK) + return EFPG_ACK_RW_ERR; + + if (tmp != 0) { + if (HAL_EFUSE_Read(EFPG_BOOT_2ND_NO_START, EFPG_BOOT_2ND_NO_NUM, &tmp) != HAL_OK) + return EFPG_ACK_RW_ERR; + byte_cnt = tmp >> 3; + bit_cnt = tmp & 0x07; + data[byte_cnt] ^= (0x1 << bit_cnt); + } + } + + return EFPG_ACK_OK; +} + +static uint16_t efpg_read_dcxo(uint8_t *data) +{ + uint8_t idx = 1; + uint8_t flag = 0; + uint32_t start_bit; + + /* flag */ + if (HAL_EFUSE_Read(EFPG_DCXO_FLAG_START, EFPG_DCXO_FLAG_NUM, &flag) != HAL_OK) + return EFPG_ACK_RW_ERR; + + if (flag == 0) { + EFPG_WARN("%s(), %d, dcxo flag 0\n", __func__, __LINE__); + return EFPG_ACK_NODATA_ERR; + } + + while ((flag & 0x3) == 0) { + flag = flag >> 2; + idx++; + } + + /* DCXO TRIM */ + start_bit = EFPG_DCXO_TRIM_START + (idx - 1) * EFPG_DCXO_TRIM_NUM; + if (HAL_EFUSE_Read(start_bit, EFPG_DCXO_TRIM_NUM, data) != HAL_OK) + return EFPG_ACK_RW_ERR; + + return EFPG_ACK_OK; +} + +static uint16_t efpg_read_pout(uint8_t *data) +{ + uint8_t idx = 1; + uint8_t flag = 0; + uint32_t start_bit; + + /* flag */ + if (HAL_EFUSE_Read(EFPG_POUT_FLAG_START, EFPG_POUT_FLAG_NUM, &flag) != HAL_OK) + return EFPG_ACK_RW_ERR; + + if (flag == 0) { + EFPG_WARN("%s(), %d, pout flag 0\n", __func__, __LINE__); + return EFPG_ACK_NODATA_ERR; + } + + while ((flag & 0x3) == 0) { + flag = flag >> 2; + idx++; + } + + /* POUT CAL */ + start_bit = EFPG_POUT_CAL_START + (idx - 1) * EFPG_POUT_CAL_NUM; + if (HAL_EFUSE_Read(start_bit, EFPG_POUT_CAL_NUM, data) != HAL_OK) + return EFPG_ACK_RW_ERR; + + return EFPG_ACK_OK; +} + +static uint16_t efpg_read_mac(uint8_t *data) +{ + uint8_t idx = 1; + uint32_t flag = 0; + uint32_t start_bit; + + /* flag */ + if (HAL_EFUSE_Read(EFPG_MAC_FLAG_START, EFPG_MAC_FLAG_NUM, (uint8_t *)&flag) != HAL_OK) + return EFPG_ACK_RW_ERR; + + if (flag == 0) { + EFPG_WARN("%s(), %d, mac flag 0\n", __func__, __LINE__); + return EFPG_ACK_NODATA_ERR; + } + + while ((flag & 0x3) == 0) { + flag = flag >> 2; + idx++; + } + + /* MAC */ + start_bit = EFPG_MAC_ADDR_START + (idx - 1) * EFPG_MAC_ADDR_NUM; + if (HAL_EFUSE_Read(start_bit, EFPG_MAC_ADDR_NUM, data) != HAL_OK) + return EFPG_ACK_RW_ERR; + + return EFPG_ACK_OK; +} + +static uint16_t efpg_read_chipid(uint8_t *data) +{ + uint8_t flag = 0; + + /* flag */ + if (HAL_EFUSE_Read(EFPG_CHIPID_FLAG_START, EFPG_CHIPID_FLAG_NUM, &flag) != HAL_OK) + return EFPG_ACK_RW_ERR; + + /* chipid */ + if (flag == 0) { + if (HAL_EFUSE_Read(EFPG_CHIPID_1ST_START, EFPG_CHIPID_1ST_NUM, data) != HAL_OK) + return EFPG_ACK_RW_ERR; + } else { + if (HAL_EFUSE_Read(EFPG_CHIPID_2ND_START, EFPG_CHIPID_2ND_NUM, data) != HAL_OK) + return EFPG_ACK_RW_ERR; + } + + return EFPG_ACK_OK; +} + +static uint16_t efpg_read_user_area(uint16_t start, uint16_t num, uint8_t *data) +{ + if ((start >= EFPG_USER_AREA_NUM) + || (num == 0) + || (num > EFPG_USER_AREA_NUM) + || (start + num > EFPG_USER_AREA_NUM)) { + EFPG_ERR("start %d, num %d\n", start, num); + return EFPG_ACK_RW_ERR; + } + + if (HAL_EFUSE_Read(start + EFPG_USER_AREA_START, num, data) != HAL_OK) { + EFPG_ERR("eFuse read failed\n"); + return EFPG_ACK_RW_ERR; + } + + return EFPG_ACK_OK; +} + +static uint16_t efpg_write_hosc(uint8_t *data) +{ + uint8_t buf[EFPG_HOSC_BUF_LEN] = {0}; + + if ((HAL_EFUSE_Write(EFPG_HOSC_TYPE_START, EFPG_HOSC_TYPE_NUM, data) != HAL_OK) + || (HAL_EFUSE_Read(EFPG_HOSC_TYPE_START, EFPG_HOSC_TYPE_NUM, buf) != HAL_OK)) + return EFPG_ACK_RW_ERR; + + if (efpg_memcmp(data, buf, EFPG_HOSC_BUF_LEN)) { + EFPG_WARN("%s(), %d, hosc: write %#04x, read %#04x\n", + __func__, __LINE__, data[0], buf[0]); + return EFPG_ACK_DI_ERR; + } + + return EFPG_ACK_OK; +} + +static uint16_t efpg_write_boot(uint8_t *data) +{ + uint8_t tmp; + uint8_t err_cnt = 0; + uint8_t err_1st_no = 0; + uint8_t err_2nd_no = 0; + uint8_t buf[EFPG_BOOT_BUF_LEN] = {0}; + + /* flag */ + tmp = 0x03; + if ((HAL_EFUSE_Write(EFPG_BOOT_FLAG_START, EFPG_BOOT_FLAG_NUM, &tmp) != HAL_OK) + || (HAL_EFUSE_Read(EFPG_BOOT_FLAG_START, EFPG_BOOT_FLAG_NUM, &tmp) != HAL_OK)) + return EFPG_ACK_RW_ERR; + + if (tmp == 0) { + EFPG_WARN("%s(), %d, boot flag 0\n", __func__, __LINE__); + return EFPG_ACK_DI_ERR; + } + + /* hash */ + if ((HAL_EFUSE_Write(EFPG_BOOT_HASH_START, EFPG_BOOT_HASH_NUM, data) != HAL_OK) + || (HAL_EFUSE_Read(EFPG_BOOT_HASH_START, EFPG_BOOT_HASH_NUM, buf) != HAL_OK)) + return EFPG_ACK_RW_ERR; + + if (efpg_boot_hash_cmp(data, buf, &err_cnt, &err_1st_no, &err_2nd_no) < 0) { + EFPG_WARN("%s(), %d, boot hash: compare failed\n", __func__, __LINE__); + return EFPG_ACK_DI_ERR; + } + + /* error bit */ + if (err_cnt > 0) { + /* bit en */ + tmp = 0x01; + if ((HAL_EFUSE_Write(EFPG_BOOT_1ST_EN_START, EFPG_BOOT_1ST_EN_NUM, &tmp) != HAL_OK) + || (HAL_EFUSE_Read(EFPG_BOOT_1ST_EN_START, EFPG_BOOT_1ST_EN_NUM, &tmp) != HAL_OK)) + return EFPG_ACK_RW_ERR; + + if (tmp != 0x01) { + EFPG_WARN("%s(), %d, boot 1st en %d\n", __func__, __LINE__, tmp); + return EFPG_ACK_DI_ERR; + } + + /* bit no */ + if ((HAL_EFUSE_Write(EFPG_BOOT_1ST_NO_START, EFPG_BOOT_1ST_NO_NUM, &err_1st_no) != HAL_OK) + || (HAL_EFUSE_Read(EFPG_BOOT_1ST_NO_START, EFPG_BOOT_1ST_NO_NUM, &tmp) != HAL_OK)) + return EFPG_ACK_RW_ERR; + + if (err_1st_no != tmp) { + EFPG_WARN("%s(), %d, boot 1st no: write %d, read %d\n", + __func__, __LINE__, err_1st_no, tmp); + return EFPG_ACK_DI_ERR; + } + + if (err_cnt == 2) { + /* bit en */ + tmp = 0x01; + if ((HAL_EFUSE_Write(EFPG_BOOT_2ND_EN_START, EFPG_BOOT_2ND_EN_NUM, &tmp) != HAL_OK) + || (HAL_EFUSE_Read(EFPG_BOOT_2ND_EN_START, EFPG_BOOT_2ND_EN_NUM, &tmp) != HAL_OK)) + return EFPG_ACK_RW_ERR; + + if (tmp != 0x01) { + EFPG_WARN("%s(), %d, boot 2nd en %d\n", __func__, __LINE__, tmp); + return EFPG_ACK_DI_ERR; + } + + /* bit no */ + if ((HAL_EFUSE_Write(EFPG_BOOT_2ND_NO_START, EFPG_BOOT_2ND_NO_NUM, &err_2nd_no) != HAL_OK) + || (HAL_EFUSE_Read(EFPG_BOOT_2ND_NO_START, EFPG_BOOT_2ND_NO_NUM, &tmp) != HAL_OK)) + return EFPG_ACK_RW_ERR; + + if (err_2nd_no != tmp) { + EFPG_WARN("%s(), %d, boot 2nd no: write %d, read %d\n", + __func__, __LINE__, err_2nd_no, tmp); + return EFPG_ACK_DI_ERR; + } + } + } + + return EFPG_ACK_OK; +} + +static uint16_t efpg_write_dcxo(uint8_t *data) +{ + uint8_t tmp; + uint8_t idx = 0; + uint8_t flag = 0; + uint8_t buf[EFPG_DCXO_BUF_LEN] = {0}; + uint32_t start_bit; + + if (HAL_EFUSE_Read(EFPG_DCXO_FLAG_START, EFPG_DCXO_FLAG_NUM, &flag) != HAL_OK) + return EFPG_ACK_RW_ERR; + + while (((flag & 0x3) == 0) && (idx < EFPG_DCXO_IDX_MAX)) { + flag = flag >> 2; + idx++; + } + + while (idx > 0) { + tmp = 0x3; + start_bit = EFPG_DCXO_FLAG_START + (idx - 1) * 2; + if ((HAL_EFUSE_Write(start_bit, 2, &tmp) != HAL_OK) + || (HAL_EFUSE_Read(start_bit, 2, &tmp) != HAL_OK)) + return EFPG_ACK_RW_ERR; + + if (tmp == 0) { + idx--; + continue; + } + + start_bit = EFPG_DCXO_TRIM_START + (idx - 1) * EFPG_DCXO_TRIM_NUM; + if ((HAL_EFUSE_Write(start_bit, EFPG_DCXO_TRIM_NUM, data) != HAL_OK) + || (HAL_EFUSE_Read(start_bit, EFPG_DCXO_TRIM_NUM, buf) != HAL_OK)) + return EFPG_ACK_RW_ERR; + + if (efpg_memcmp(data, buf, EFPG_DCXO_BUF_LEN)) { + idx--; + continue; + } + + return EFPG_ACK_OK; + } + + return EFPG_ACK_DI_ERR; +} + +static uint16_t efpg_write_pout(uint8_t *data) +{ + uint8_t tmp; + uint8_t idx = 0; + uint8_t flag = 0; + uint8_t buf[EFPG_POUT_BUF_LEN] = {0}; + uint32_t start_bit; + + if (HAL_EFUSE_Read(EFPG_POUT_FLAG_START, EFPG_POUT_FLAG_NUM, &flag) != HAL_OK) + return EFPG_ACK_RW_ERR; + + while (((flag & 0x3) == 0) && (idx < EFPG_POUT_IDX_MAX)) { + flag = flag >> 2; + idx++; + } + + while (idx > 0) { + tmp = 0x3; + start_bit = EFPG_POUT_FLAG_START + (idx - 1) * 2; + if ((HAL_EFUSE_Write(start_bit, 2, &tmp) != HAL_OK) + || (HAL_EFUSE_Read(start_bit, 2, &tmp) != HAL_OK)) + return EFPG_ACK_RW_ERR; + + if (tmp == 0) { + idx--; + continue; + } + + start_bit = EFPG_POUT_CAL_START + (idx - 1) * EFPG_POUT_CAL_NUM; + if ((HAL_EFUSE_Write(start_bit, EFPG_POUT_CAL_NUM, data) != HAL_OK) + || (HAL_EFUSE_Read(start_bit, EFPG_POUT_CAL_NUM, buf) != HAL_OK)) + return EFPG_ACK_RW_ERR; + + if (efpg_memcmp(data, buf, EFPG_POUT_BUF_LEN)) { + idx--; + continue; + } + + return EFPG_ACK_OK; + } + + return EFPG_ACK_DI_ERR; +} + +static uint16_t efpg_write_mac(uint8_t *data) +{ + uint8_t tmp; + uint8_t idx = 0; + uint32_t flag = 0; + uint8_t buf[EFPG_MAC_BUF_LEN] = {0}; + uint32_t start_bit; + + if (HAL_EFUSE_Read(EFPG_MAC_FLAG_START, EFPG_MAC_FLAG_NUM, (uint8_t *)&flag) != HAL_OK) + return EFPG_ACK_RW_ERR; + + while (((flag & 0x3) == 0) && (idx < EFPG_MAC_IDX_MAX)) { + flag = flag >> 2; + idx++; + } + + while (idx > 0) { + tmp = 0x3; + start_bit = EFPG_MAC_FLAG_START + (idx - 1) * 2; + if ((HAL_EFUSE_Write(start_bit, 2, &tmp) != HAL_OK) + || (HAL_EFUSE_Read(start_bit, 2, &tmp) != HAL_OK)) + return EFPG_ACK_RW_ERR; + + if (tmp == 0) { + idx--; + continue; + } + + start_bit = EFPG_MAC_ADDR_START + (idx - 1) * EFPG_MAC_ADDR_NUM; + if ((HAL_EFUSE_Write(start_bit, EFPG_MAC_ADDR_NUM, data) != HAL_OK) + || (HAL_EFUSE_Read(start_bit, EFPG_MAC_ADDR_NUM, buf) != HAL_OK)) + return EFPG_ACK_RW_ERR; + + if (efpg_memcmp(data, buf, EFPG_MAC_BUF_LEN)) { + idx--; + continue; + } + + return EFPG_ACK_OK; + } + + return EFPG_ACK_DI_ERR; +} + +static uint16_t efpg_write_user_area(uint16_t start, uint16_t num, uint8_t *data) +{ + if ((start >= EFPG_USER_AREA_NUM) + || (num == 0) + || (num > EFPG_USER_AREA_NUM) + || (start + num > EFPG_USER_AREA_NUM)) { + EFPG_ERR("start %d, num %d\n", start, num); + return EFPG_ACK_RW_ERR; + } + + if (HAL_EFUSE_Write(start + EFPG_USER_AREA_START, num, data) != HAL_OK) { + EFPG_ERR("eFuse write failed\n"); + return EFPG_ACK_RW_ERR; + } + + return EFPG_ACK_OK; +} + +uint16_t efpg_read_field(efpg_field_t field, uint8_t *data, uint16_t start_bit_addr, uint16_t bit_len) +{ + switch (field) { + case EFPG_FIELD_HOSC: + return efpg_read_hosc(data); + case EFPG_FIELD_BOOT: + return efpg_read_boot(data); + case EFPG_FIELD_DCXO: + return efpg_read_dcxo(data); + case EFPG_FIELD_POUT: + return efpg_read_pout(data); + case EFPG_FIELD_MAC: + return efpg_read_mac(data); + case EFPG_FIELD_CHIPID: + return efpg_read_chipid(data); + case EFPG_FIELD_UA: + return efpg_read_user_area(start_bit_addr, bit_len, data); + default: + EFPG_WARN("%s(), %d, read field %d\n", __func__, __LINE__, field); + return EFPG_ACK_RW_ERR; + } +} + +uint16_t efpg_write_field(efpg_field_t field, uint8_t *data, uint16_t start_bit_addr, uint16_t bit_len) +{ + switch (field) { + case EFPG_FIELD_HOSC: + return efpg_write_hosc(data); + case EFPG_FIELD_BOOT: + return efpg_write_boot(data); + case EFPG_FIELD_DCXO: + return efpg_write_dcxo(data); + case EFPG_FIELD_POUT: + return efpg_write_pout(data); + case EFPG_FIELD_MAC: + return efpg_write_mac(data); + case EFPG_FIELD_UA: + return efpg_write_user_area(start_bit_addr, bit_len, data); + default: + EFPG_WARN("%s(), %d, write field %d\n", __func__, __LINE__, field); + return EFPG_ACK_RW_ERR; + } +} diff --git a/platform/mcu/xr871/src/efpg/efpg_frame.c b/platform/mcu/xr871/src/efpg/efpg_frame.c new file mode 100644 index 0000000000..51a0c42bb3 --- /dev/null +++ b/platform/mcu/xr871/src/efpg/efpg_frame.c @@ -0,0 +1,340 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "efpg_i.h" +#include "efpg_debug.h" +#include "driver/chip/hal_uart.h" +#include "driver/chip/hal_crypto.h" + +static uint8_t efpg_checksum8(uint8_t *data, uint32_t len) +{ + uint8_t cs = 0; + + while (len > 0) { + cs += *data++; + len--; + } + + return cs; +} + +static int efpg_check_msg_dgst(efpg_priv_t *efpg) +{ + CE_SHA256_Handler hdl; + uint8_t msg_dgst_cal[EFPG_MSG_DGST_LEN]; + uint8_t *msg_dgst = efpg->data_frame + efpg->recv_len - EFPG_MSG_DGST_LEN; + + if ((HAL_SHA256_Init(&hdl, CE_CTL_IVMODE_SHA_MD5_FIPS180, NULL) != HAL_OK) + || (HAL_SHA256_Append(&hdl, efpg->cmd_frame, EFPG_CMD_FRAME_LEN) != HAL_OK) + || (HAL_SHA256_Append(&hdl, efpg->data_frame, efpg->recv_len - EFPG_MSG_DGST_LEN) != HAL_OK) + || (HAL_SHA256_Append(&hdl, efpg->key, efpg->key_len) != HAL_OK) + || (HAL_SHA256_Finish(&hdl, (uint32_t *)msg_dgst_cal) != HAL_OK)) { + EFPG_ERR("failed to calculate msg dgst\n"); + return -1; + } + + if (efpg_memcmp(msg_dgst, msg_dgst_cal, EFPG_MSG_DGST_LEN)) { + EFPG_WARN("%s(), %d, check msg dgst failed\n", __func__, __LINE__); + return -1; + } + + return 0; +} + +static int efpg_send_ack(efpg_priv_t *efpg, uint16_t status) +{ + uint8_t cs; + int32_t send_len; + + uint8_t *ack_frame = efpg_malloc(EFPG_ACK_FRAME_LEN); + if (ack_frame == NULL) { + EFPG_ERR("malloc failed\n"); + return -1; + } + + efpg_memset(ack_frame, 0, EFPG_ACK_FRAME_LEN); + efpg_memcpy(ack_frame, &status, 2); + cs = 0xFF - efpg_checksum8(ack_frame, EFPG_ACK_FRAME_LEN); + efpg_memcpy(ack_frame + 3, &cs, sizeof(cs)); + + send_len = HAL_UART_Transmit_Poll(efpg->uart_id, ack_frame, EFPG_ACK_FRAME_LEN); + efpg_free(ack_frame); + + if (send_len != EFPG_ACK_FRAME_LEN) { + EFPG_WARN("%s(), %d, send len %d\n", __func__, __LINE__, send_len); + return -1; + } else { + return 0; + } +} + +static int efpg_parse_cmd(efpg_priv_t *efpg) +{ + uint16_t op_code; + uint16_t type; + uint16_t len; + uint8_t *p; + uint8_t extCmd; + + if (efpg->ext_cmd == EFPG_EXT_CMD) + p = efpg->ext_cmd_frame; + else + p = efpg->cmd_frame; + + op_code = *((uint16_t *)p); + p += 2; + type = *((uint16_t *)p); + p += 2; + len = *((uint16_t *)p); + + switch (op_code) { + case EFPG_OP_CODE_READ: + efpg->op = EFPG_OP_READ; + break; + case EFPG_OP_CODE_WRITE: + efpg->op = EFPG_OP_WRITE; + break; + case EFPG_OP_CODE_EXIT: + efpg->op = EFPG_OP_EXIT; + return 0; + default: + EFPG_WARN("%s(), %d, op_code %#06x\n", __func__, __LINE__, op_code); + return -1; + } + + switch (type) { + case EFPG_TYPE_HOSC: + efpg->field = EFPG_FIELD_HOSC; + efpg->expt_len = EFPG_HOSC_FRAME_LEN; + break; + case EFPG_TYPE_BOOT: + efpg->field = EFPG_FIELD_BOOT; + efpg->expt_len = EFPG_BOOT_FRAME_LEN; + break; + case EFPG_TYPE_DCXO: + efpg->field = EFPG_FIELD_DCXO; + efpg->expt_len = EFPG_DCXO_FRAME_LEN; + break; + case EFPG_TYPE_POUT: + efpg->field = EFPG_FIELD_POUT; + efpg->expt_len = EFPG_POUT_FRAME_LEN; + break; + case EFPG_TYPE_MAC: + efpg->field = EFPG_FIELD_MAC; + efpg->expt_len = EFPG_MAC_FRAME_LEN; + break; + case EFPG_TYPE_USER_AREA: + if (efpg->ext_cmd == EFPG_NORMAL_CMD) { + p += 2; + extCmd = *p; + if (extCmd != EFPG_EXT_CMD) { + EFPG_WARN("%s(), %d, extCmd %d, expt extCmd 0\n", + __func__, __LINE__, extCmd); + return -1; + } + efpg->ext_cmd = EFPG_EXT_CMD; + efpg->field = EFPG_FIELD_UA; + efpg->expt_len = EFPG_UAER_AREA_EXT_LEN; + } + else { + p += 2; + efpg->protocol_version = *((uint16_t *)p); + p += 2; + efpg->start_bit_addr= *((uint16_t *)p); + p += 2; + efpg->bit_length = *((uint16_t *)p); + efpg->ext_cmd = EFPG_NORMAL_CMD; + efpg->field = EFPG_FIELD_UA; + efpg->expt_len = efpg->bit_length / 8 + EFPG_MSG_DGST_LEN; + if (efpg->bit_length % 8) + efpg->expt_len ++; + len = efpg->expt_len; + } + break; + default: + EFPG_WARN("%s(), %d, type %#06x\n", __func__, __LINE__, type); + return -1; + } + + if (len != efpg->expt_len) { + EFPG_WARN("%s(), %d, len %d, expt len %d\n", + __func__, __LINE__, len, efpg->expt_len); + return -1; + } + + return 0; +} + +static efpg_state_t efpg_ext_cmd_process(efpg_priv_t *efpg) +{ + efpg->is_cmd = 1; + efpg->recv_len = 0; + + if (efpg_send_ack(efpg, EFPG_ACK_OK) < 0) { + EFPG_WARN("%s(), %d, send ack failed\n", __func__, __LINE__); + return EFPG_STATE_RESET; + } + + return EFPG_STATE_CONTINUE; +} + +static efpg_state_t efpg_read_process(efpg_priv_t *efpg) +{ + uint16_t status; + uint8_t *data; + uint8_t *msg_dgst; + int32_t send_len; + CE_SHA256_Handler hdl; + + uint8_t *frame = efpg_malloc(efpg->expt_len); + if (frame == NULL) { + EFPG_ERR("malloc failed\n"); + return EFPG_STATE_RESET; + } + + efpg_memset(frame, 0, efpg->expt_len); + data = frame; + msg_dgst = frame + efpg->expt_len - EFPG_MSG_DGST_LEN; + + status = efpg_read_field(efpg->field, data, efpg->start_bit_addr, efpg->bit_length); + if ((HAL_SHA256_Init(&hdl, CE_CTL_IVMODE_SHA_MD5_FIPS180, NULL) != HAL_OK) + || (HAL_SHA256_Append(&hdl, efpg->cmd_frame, EFPG_CMD_FRAME_LEN) != HAL_OK) + || (HAL_SHA256_Append(&hdl, data, efpg->expt_len - EFPG_MSG_DGST_LEN) != HAL_OK) + || (HAL_SHA256_Append(&hdl, efpg->key, efpg->key_len) != HAL_OK) + || (HAL_SHA256_Finish(&hdl, (uint32_t *)msg_dgst) != HAL_OK)) { + EFPG_WARN("%s(), %d, SHA256 failed\n", __func__, __LINE__); + status = EFPG_ACK_RW_ERR; + } + + if (status != EFPG_ACK_OK) { + EFPG_WARN("%s(), %d, status %d\n", __func__, __LINE__, status); + efpg_free(frame); + efpg_send_ack(efpg, status); + return EFPG_STATE_RESET; + } + + if (efpg_send_ack(efpg, EFPG_ACK_OK) < 0) { + EFPG_WARN("%s(), %d, send ack failed\n", __func__, __LINE__); + efpg_free(frame); + return EFPG_STATE_RESET; + } + + send_len = HAL_UART_Transmit_Poll(efpg->uart_id, frame, efpg->expt_len); + if (send_len != efpg->expt_len) + EFPG_WARN("%s(), %d, send len %d, expt %d\n", + __func__, __LINE__, send_len, efpg->expt_len); + + efpg_free(frame); + return EFPG_STATE_RESET; +} + +static efpg_state_t efpg_write_process(efpg_priv_t *efpg) +{ + efpg->is_cmd = 0; + efpg->recv_len = 0; + + if (efpg_send_ack(efpg, EFPG_ACK_OK) < 0) { + EFPG_WARN("%s(), %d, send ack failed\n", __func__, __LINE__); + return EFPG_STATE_RESET; + } + + return EFPG_STATE_CONTINUE; +} + +static efpg_state_t efpg_stop_process(efpg_priv_t *efpg) +{ + if (efpg_send_ack(efpg, EFPG_ACK_OK) < 0) { + EFPG_WARN("%s(), %d, send ack failed\n", __func__, __LINE__); + return EFPG_STATE_RESET; + } else { + return EFPG_STATE_STOP; + } +} + +efpg_state_t efpg_cmd_frame_process(efpg_priv_t *efpg) +{ + uint8_t * pFrame; + if (efpg->ext_cmd == EFPG_EXT_CMD) + pFrame = efpg->ext_cmd_frame; + else + pFrame = efpg->cmd_frame; + + /* checksum */ + if (efpg_checksum8(pFrame, efpg->recv_len) != 0xFF) { + EFPG_WARN("%s(), %d, checksum failed\n", __func__, __LINE__); + efpg_send_ack(efpg, EFPG_ACK_CS_ERR); + return EFPG_STATE_RESET; + } + + /* parse frame */ + if (efpg_parse_cmd(efpg) < 0) { + EFPG_WARN("%s(), %d, parse cmd failed\n", __func__, __LINE__); + efpg_send_ack(efpg, EFPG_ACK_PARSE_ERR); + return EFPG_STATE_RESET; + } + + /* check ext cmd */ + if (efpg->ext_cmd == EFPG_EXT_CMD) { + return efpg_ext_cmd_process(efpg); + } + + switch (efpg->op) { + case EFPG_OP_READ: + return efpg_read_process(efpg); + case EFPG_OP_WRITE: + return efpg_write_process(efpg); + case EFPG_OP_EXIT: + return efpg_stop_process(efpg); + default: + EFPG_ERR("invalid op %d\n", efpg->op); + return EFPG_STATE_STOP; + } +} + +efpg_state_t efpg_data_frame_process(efpg_priv_t *efpg) +{ + uint16_t status; + uint8_t *data; + + /* message digest */ + if (efpg_check_msg_dgst(efpg) < 0) { + EFPG_WARN("%s(), %d, check msg dgst failed\n", __func__, __LINE__); + efpg_send_ack(efpg, EFPG_ACK_MD_ERR); + return EFPG_STATE_RESET; + } + + /* write data */ + data = efpg->data_frame; + status = efpg_write_field(efpg->field, data, efpg->start_bit_addr, efpg->bit_length); + EFPG_DBG("%s(), %d, write field status %d\n", __func__, __LINE__, status); + + efpg_send_ack(efpg, status); + return EFPG_STATE_RESET; +} + diff --git a/platform/mcu/xr871/src/efpg/efpg_i.h b/platform/mcu/xr871/src/efpg/efpg_i.h new file mode 100644 index 0000000000..ab2518e4b5 --- /dev/null +++ b/platform/mcu/xr871/src/efpg/efpg_i.h @@ -0,0 +1,181 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _EFPG_I_H_ +#define _EFPG_I_H_ + +#include +#include +#include "efpg/efpg.h" +#include "kernel/os/os.h" +#include "driver/chip/hal_uart.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define EFPG_KEY_LEN_MAX (64) +#define EFPG_RECV_TIMEOUT_MS (3000) +#define EFPG_THREAD_STACK_SIZE (3 * 1024) + +#define EFPG_CMD_FRAME_LEN (8) +#define EFPG_ACK_FRAME_LEN (4) +#define EFPG_MSG_DGST_LEN (32) +#define EFPG_DATA_FRAME_LEN_MAX ((76) + EFPG_MSG_DGST_LEN) +#define EFPG_HOSC_FRAME_LEN ((1) + EFPG_MSG_DGST_LEN) +#define EFPG_BOOT_FRAME_LEN ((32) + EFPG_MSG_DGST_LEN) +#define EFPG_DCXO_FRAME_LEN ((1) + EFPG_MSG_DGST_LEN) +#define EFPG_POUT_FRAME_LEN ((3) + EFPG_MSG_DGST_LEN) +#define EFPG_MAC_FRAME_LEN ((6) + EFPG_MSG_DGST_LEN) +#define EFPG_UA_FRAME_LEN ((76) + EFPG_MSG_DGST_LEN) + +#define EFPG_ACK_OK (200) +#define EFPG_ACK_CS_ERR (400) +#define EFPG_ACK_MD_ERR (401) +#define EFPG_ACK_PARSE_ERR (402) +#define EFPG_ACK_RW_ERR (403) +#define EFPG_ACK_DI_ERR (404) +#define EFPG_ACK_NODATA_ERR (405) + +#define EFPG_OP_CODE_READ (0x0FF0) +#define EFPG_OP_CODE_WRITE (0xF00F) +#define EFPG_OP_CODE_EXIT (0xFFFF) + +#define EFPG_TYPE_HOSC (0xFF00) +#define EFPG_TYPE_BOOT (0xFE01) +#define EFPG_TYPE_DCXO (0xFD02) +#define EFPG_TYPE_POUT (0xFC03) +#define EFPG_TYPE_MAC (0xFB04) +#define EFPG_TYPE_USER_AREA (0xFA05) + +#define EFPG_NORMAL_CMD (0) +#define EFPG_EXT_CMD (1) + +#define EFPG_UAER_AREA_EXT_LEN (8 + (2 + 2) + 1) + +#define EFPG_HOSC_TYPE_START (328) +#define EFPG_HOSC_TYPE_NUM (4) +#define EFPG_BOOT_FLAG_START (332) +#define EFPG_BOOT_FLAG_NUM (2) +#define EFPG_BOOT_1ST_EN_START (334) +#define EFPG_BOOT_1ST_EN_NUM (1) +#define EFPG_BOOT_1ST_NO_START (335) +#define EFPG_BOOT_1ST_NO_NUM (8) +#define EFPG_BOOT_2ND_EN_START (343) +#define EFPG_BOOT_2ND_EN_NUM (1) +#define EFPG_BOOT_2ND_NO_START (344) +#define EFPG_BOOT_2ND_NO_NUM (8) +#define EFPG_BOOT_HASH_START (352) +#define EFPG_BOOT_HASH_NUM (256) +#define EFPG_DCXO_FLAG_START (898) +#define EFPG_DCXO_FLAG_NUM (6) +#define EFPG_DCXO_TRIM_START (904) +#define EFPG_DCXO_TRIM_NUM (8) +#define EFPG_POUT_FLAG_START (928) +#define EFPG_POUT_FLAG_NUM (6) +#define EFPG_POUT_CAL_START (934) +#define EFPG_POUT_CAL_NUM (21) +#define EFPG_MAC_FLAG_START (997) +#define EFPG_MAC_FLAG_NUM (18) +#define EFPG_MAC_ADDR_START (1015) +#define EFPG_MAC_ADDR_NUM (48) +#define EFPG_CHIPID_FLAG_START (608) +#define EFPG_CHIPID_FLAG_NUM (2) +#define EFPG_CHIPID_1ST_START (200) +#define EFPG_CHIPID_1ST_NUM (128) +#define EFPG_CHIPID_2ND_START (610) +#define EFPG_CHIPID_2ND_NUM (128) +#define EFPG_USER_AREA_START (1447) +#define EFPG_USER_AREA_NUM (601) + +#define EFPG_HOSC_BUF_LEN (1) +#define EFPG_BOOT_BUF_LEN (32) +#define EFPG_DCXO_BUF_LEN (1) +#define EFPG_POUT_BUF_LEN (3) +#define EFPG_MAC_BUF_LEN (6) + +#define EFPG_DCXO_IDX_MAX (3) +#define EFPG_POUT_IDX_MAX (3) +#define EFPG_MAC_IDX_MAX (9) + +#define efpg_malloc(l) malloc(l) +#define efpg_free(p) free(p) +#define efpg_memcpy(d, s, n) memcpy(d, s, n) +#define efpg_memset(s, c, n) memset(s, c, n) +#define efpg_memcmp(s1, s2, n) memcmp(s1, s2, n) + +typedef enum efpg_op { + EFPG_OP_READ = 0, + EFPG_OP_WRITE = 1, + EFPG_OP_EXIT = 2, + EFPG_OP_NUM = 3, +} efpg_op_t; + +typedef enum efpg_state { + EFPG_STATE_CONTINUE = 0, + EFPG_STATE_RESET = 1, + EFPG_STATE_STOP = 2, + EFPG_STATE_NUM = 3, +} efpg_state_t; + +typedef struct efpg_priv { + UART_ID uart_id; + efpg_cb_t start_cb; + efpg_cb_t stop_cb; + + uint8_t key[EFPG_KEY_LEN_MAX]; + uint8_t key_len; + + uint8_t is_cmd; + uint8_t ext_cmd; + uint8_t cmd_frame[EFPG_CMD_FRAME_LEN]; + uint8_t data_frame[EFPG_DATA_FRAME_LEN_MAX]; + uint8_t ext_cmd_frame[EFPG_UAER_AREA_EXT_LEN]; + uint16_t expt_len; + uint16_t recv_len; + + uint16_t protocol_version; + uint16_t start_bit_addr; + uint16_t bit_length; + + efpg_op_t op; + efpg_field_t field; +} efpg_priv_t; + +efpg_state_t efpg_cmd_frame_process(efpg_priv_t *efpg); +efpg_state_t efpg_data_frame_process(efpg_priv_t *efpg); + +uint16_t efpg_read_field(efpg_field_t field, uint8_t *data, uint16_t start_bit_addr, uint16_t bit_len); +uint16_t efpg_write_field(efpg_field_t field, uint8_t *data, uint16_t start_bit_addr, uint16_t bit_len); + +#ifdef __cplusplus +} +#endif + +#endif /* _EFPG_I_H_ */ \ No newline at end of file diff --git a/platform/mcu/xr871/src/image/fdcm.c b/platform/mcu/xr871/src/image/fdcm.c new file mode 100644 index 0000000000..25cb28be80 --- /dev/null +++ b/platform/mcu/xr871/src/image/fdcm.c @@ -0,0 +1,262 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include "flash.h" +#include "sys/fdcm.h" +#include "image_debug.h" + +#define fdcm_malloc(l) malloc(l) +#define fdcm_free(p) free(p) +#define fdcm_memcpy(d, s, n) memcpy(d, s, n) +#define fdcm_memset(s, c, n) memset(s, c, n) + +#define FDCM_ID_CODE (0xA55A5AA5) +#define FDCM_INDEX_MAX (1 << 10) +#define FDCM_BYTE_BITS (8) + +typedef struct fdcm_header { + uint32_t id_code; + uint16_t bitmap_size; + uint16_t data_size; +} fdcm_header_t; + +#define FDCM_HEADER_SIZE sizeof(fdcm_header_t) + +static uint32_t fdcm_bit_count(uint8_t *bitmap, uint16_t size) +{ + uint32_t byte_cnt = 0; + uint32_t bit_cnt = 0; + uint8_t val; + + while (byte_cnt < size) { + if (*bitmap == 0) { + bit_cnt += FDCM_BYTE_BITS; + } + else { + val = ~(*bitmap); + while (val) { + val = val >> 1; + bit_cnt++; + } + break; + } + bitmap++; + byte_cnt++; + } + + return bit_cnt; +} + +/** + * @brief Erase the area and then write a data chunk to the area + * @param[in] hdl Pointer to the FDCM handle + * @param[in] data Pointer to the data + * @param[in] data_size Size of the data + * @return Number of bytes written + */ +static uint32_t fdcm_rewrite(fdcm_handle_t *hdl, void *data, uint16_t data_size) +{ + uint8_t bitmap_byte = 0xFE; + fdcm_header_t header; + + if (flash_erase(hdl->flash, hdl->addr, hdl->size) != hdl->size) { + FDCM_ERR("flash %d, addr %#010x, size %#010x\n", hdl->flash, hdl->addr, hdl->size); + return 0; + } + + header.id_code = FDCM_ID_CODE; + header.bitmap_size = (hdl->size - FDCM_HEADER_SIZE - 1) / (data_size * FDCM_BYTE_BITS + 1) + 1; + if (header.bitmap_size > FDCM_INDEX_MAX) + header.bitmap_size = FDCM_INDEX_MAX; + header.data_size = data_size; + + FDCM_DBG("%s(), %d, flash %d, addr %#010x, size %#010x, bitmap size %d, data size %d\n", + __func__, __LINE__, hdl->flash, hdl->addr, hdl->size, header.bitmap_size, header.data_size); + + if (flash_write(hdl->flash, hdl->addr, &header, FDCM_HEADER_SIZE) + && flash_write(hdl->flash, hdl->addr + FDCM_HEADER_SIZE, &bitmap_byte, 1) + && flash_write(hdl->flash, hdl->addr + FDCM_HEADER_SIZE + header.bitmap_size, data, data_size)) { + return data_size; + } else { + FDCM_ERR("flash %d, addr %#010x, size %#010x\n", hdl->flash, hdl->addr, hdl->size); + return 0; + } +} + +/** + * @brief Open an area in a flash to be managed by FDCM module + * @param[in] flash Flash device number + * @param[in] addr Start address of the area + * @param[in] size Size of the area + * @retval Pointer to the FDCM handle, NULL on failure + * + * @note The area must be aligned to the flash erase block + */ +fdcm_handle_t *fdcm_open(uint32_t flash, uint32_t addr, uint32_t size) +{ + fdcm_handle_t *hdl; + + if (flash_erase_check(flash, addr, size) != 0) { + FDCM_ERR("flash %d, addr %#010x, size %#010x\n", flash, addr, size); + return NULL; + } + + hdl = (fdcm_handle_t *)fdcm_malloc(sizeof(fdcm_handle_t)); + if (hdl == NULL) { + FDCM_ERR("hdl %p\n", hdl); + return NULL; + } + + hdl->flash = flash; + hdl->addr = addr; + hdl->size = size; + + FDCM_DBG("%s(), %d, flash %d, addr %#010x, size %#010x\n", + __func__, __LINE__, flash, addr, size); + + return hdl; +} + +/** + * @brief Read a data chunk from the specified area + * @param[in] hdl Pointer to the FDCM handle + * @param[in] data Pointer to the data + * @param[in] data_size Size of the data + * @return Number of bytes read + */ +uint32_t fdcm_read(fdcm_handle_t *hdl, void *data, uint16_t data_size) +{ + fdcm_header_t header; + uint8_t *bitmap; + uint32_t bit_cnt; + uint32_t addr; + + if ((hdl == NULL) || (data == NULL)) { + FDCM_ERR("hdl %p, data %p\n", hdl, data); + return 0; + } + + flash_read(hdl->flash, hdl->addr, &header, FDCM_HEADER_SIZE); + + FDCM_DBG("%s(), %d, id code %#010x, bitmap size %#06x, data size %#06x\n", + __func__, __LINE__, header.id_code, header.bitmap_size, header.data_size); + + if ((header.id_code != FDCM_ID_CODE) + || (header.data_size != data_size) + || (header.bitmap_size == 0) + || (header.bitmap_size + FDCM_HEADER_SIZE >= hdl->size)) { + FDCM_WARN("id code %#010x, bitmap size %#06x, data size %#06x\n", + header.id_code, header.bitmap_size, header.data_size); + return 0; + } + + bitmap = (uint8_t *)fdcm_malloc(header.bitmap_size); + if (bitmap == NULL) { + FDCM_ERR("bitmap %p\n", bitmap); + return 0; + } + + flash_read(hdl->flash, hdl->addr + FDCM_HEADER_SIZE, bitmap, header.bitmap_size); + bit_cnt = fdcm_bit_count(bitmap, header.bitmap_size); + fdcm_free(bitmap); + + addr = hdl->addr + FDCM_HEADER_SIZE + header.bitmap_size + data_size * (bit_cnt - 1); + return flash_read(hdl->flash, addr, data, data_size); +} + +/** + * @brief Write a data chunk to the specified area + * @param[in] hdl Pointer to the FDCM handle + * @param[in] data Pointer to the data + * @param[in] data_size Size of the data + * @return Number of bytes written + */ +uint32_t fdcm_write(fdcm_handle_t *hdl, void *data, uint16_t data_size) +{ + fdcm_header_t header; + uint8_t *bitmap; + uint32_t bit_cnt; + uint32_t addr; + + if ((hdl == NULL) || (data == NULL)) { + FDCM_ERR("hdl %p, data %p\n", hdl, data); + return 0; + } + + flash_read(hdl->flash, hdl->addr, &header, FDCM_HEADER_SIZE); + + FDCM_DBG("%s(), %d, id code %#010x, bitmap size %#06x, data size %#06x\n", + __func__, __LINE__, header.id_code, header.bitmap_size, header.data_size); + + if ((header.id_code != FDCM_ID_CODE) + || (header.data_size != data_size) + || (header.bitmap_size == 0) + || (header.bitmap_size + FDCM_HEADER_SIZE >= hdl->size)) { + return fdcm_rewrite(hdl, data, data_size); + } + + bitmap = (uint8_t *)fdcm_malloc(header.bitmap_size); + if (bitmap == NULL) { + FDCM_ERR("bitmap %p\n", bitmap); + return 0; + } + + flash_read(hdl->flash, hdl->addr + FDCM_HEADER_SIZE, bitmap, header.bitmap_size); + bit_cnt = fdcm_bit_count(bitmap, header.bitmap_size); + if ((bit_cnt == FDCM_BYTE_BITS * header.bitmap_size) + || (FDCM_HEADER_SIZE + header.bitmap_size + data_size * (bit_cnt + 1) > hdl->size)) { + fdcm_free(bitmap); + return fdcm_rewrite(hdl, data, data_size); + } + + addr = hdl->addr + FDCM_HEADER_SIZE + bit_cnt / FDCM_BYTE_BITS; + bitmap[bit_cnt / FDCM_BYTE_BITS] &= ~(0x1 << (bit_cnt % FDCM_BYTE_BITS)); + flash_write(hdl->flash, addr, bitmap + bit_cnt / FDCM_BYTE_BITS, 1); + fdcm_free(bitmap); + + addr = hdl->addr + FDCM_HEADER_SIZE + header.bitmap_size + data_size * bit_cnt; + return flash_write(hdl->flash, addr, data, data_size); +} + +/** + * @brief Close the area managed by FDCM module + * @param[in] hdl Pointer to the FDCM handle + * @return None + */ +void fdcm_close(fdcm_handle_t *hdl) +{ + if (hdl != NULL) { + fdcm_free(hdl); + hdl = NULL; + } +} + diff --git a/platform/mcu/xr871/src/image/flash.c b/platform/mcu/xr871/src/image/flash.c new file mode 100644 index 0000000000..501b1629d5 --- /dev/null +++ b/platform/mcu/xr871/src/image/flash.c @@ -0,0 +1,166 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "flash.h" +#include "image_debug.h" + +#include "driver/chip/hal_flash.h" + +/** + * @brief Read an amount of data from flash + * @param[in] flash Flash device number + * @param[in] src_addr The source address of reading + * @param[in] buf Pointer to the data buffer + * @param[in] size Number of bytes to be read + * @return Number of bytes read + */ +uint32_t flash_read(uint32_t flash, uint32_t src_addr, void *buf, uint32_t size) +{ + if (HAL_Flash_Open(flash, FLASH_OPEN_TIMEOUT) != HAL_OK) { + FLASH_ERR("flash %d\n", flash); + return 0; + } + + if (HAL_Flash_Read(flash, src_addr, buf, size) != HAL_OK) { + FLASH_ERR("flash %d, src addr %#010x, buf %p, size %#010x\n", + flash, src_addr, buf, size); + HAL_Flash_Close(flash); + return 0; + } + + HAL_Flash_Close(flash); + return size; +} + +/** + * @brief Write an amount of data to flash + * @param[in] flash Flash device number + * @param[in] dst_addr The destination address of writing + * @param[in] buf Pointer to the data buffer + * @param[in] size Number of bytes to be written + * @return Number of bytes written + */ +uint32_t flash_write(uint32_t flash, uint32_t dst_addr, void *buf, uint32_t size) +{ + if (HAL_Flash_Open(flash, FLASH_OPEN_TIMEOUT) != HAL_OK) { + FLASH_ERR("flash %d\n", flash); + return 0; + } + + if (HAL_Flash_Write(flash, dst_addr, buf, size) != HAL_OK) { + FLASH_ERR("flash %d, dst addr %#010x, buf %p, size %#010x\n", + flash, dst_addr, buf, size); + HAL_Flash_Close(flash); + return 0; + } + + HAL_Flash_Close(flash); + return size; +} + +/** + * @brief Check whether the specified area is aligned to the flash erase block + * @param[in] flash Flash device number + * @param[in] addr Start address of the specified area + * @param[in] size Size of the specified area + * @return 0 on aligned, -1 on misaligned + */ +int32_t flash_erase_check(uint32_t flash, uint32_t addr, uint32_t size) +{ + uint32_t start; + + if ((size >= (64 << 10)) + && (HAL_Flash_MemoryOf(flash, FLASH_ERASE_64KB, addr, &start) == HAL_OK) + && (addr == start) + && ((size & 0xFFFF) == 0)) { + } else if ((size >= (32 << 10)) + && (HAL_Flash_MemoryOf(flash, FLASH_ERASE_32KB, addr, &start) == HAL_OK) + && (addr == start) + && ((size & 0x7FFF) == 0)) { + } else if ((size >= (4 << 10)) + && (HAL_Flash_MemoryOf(flash, FLASH_ERASE_4KB, addr, &start) == HAL_OK) + && (addr == start) + && ((size & 0xFFF) == 0)) { + } else { + FLASH_WARN("%s(), %d, flash %d, addr %#010x, size %#010x\n", + __func__, __LINE__, flash, addr, size); + return -1; + } + + return 0; +} + +/** + * @brief Erase a specified area in flash + * @param[in] flash Flash device number + * @param[in] addr Start address of the specified area + * @param[in] size Size of the specified area + * @return Number of bytes erased + */ +uint32_t flash_erase(uint32_t flash, uint32_t addr, uint32_t size) +{ + uint32_t start; + uint32_t multiples; + FlashEraseMode erase_size; + HAL_Status status; + + if ((size >= (64 << 10)) + && (HAL_Flash_MemoryOf(flash, FLASH_ERASE_64KB, addr, &start) == HAL_OK)) { + multiples = size / (64 << 10); + erase_size = FLASH_ERASE_64KB; + } else if ((size >= (32 << 10)) + && (HAL_Flash_MemoryOf(flash, FLASH_ERASE_32KB, addr, &start) == HAL_OK)) { + multiples = size / (32 << 10); + erase_size = FLASH_ERASE_32KB; + } else if ((size >= (4 << 10)) + && (HAL_Flash_MemoryOf(flash, FLASH_ERASE_4KB, addr, &start) == HAL_OK)) { + multiples = size / (4 << 10); + erase_size = FLASH_ERASE_4KB; + } else { + FLASH_ERR("flash %d, addr %#010x, size %#010x\n", flash, addr, size); + return 0; + } + + if (HAL_Flash_Open(flash, FLASH_OPEN_TIMEOUT) != HAL_OK) { + FLASH_ERR("flash %d\n", flash); + return 0; + } + + status = HAL_Flash_Erase(flash, erase_size, addr, multiples); + if (status != HAL_OK) { + FLASH_ERR("flash %d, erase size %#010x, addr %#010x, mul %d\n", + flash, erase_size, addr, multiples); + HAL_Flash_Close(flash); + return 0; + } + + HAL_Flash_Close(flash); + return size; +} + diff --git a/platform/mcu/xr871/src/image/flash.h b/platform/mcu/xr871/src/image/flash.h new file mode 100644 index 0000000000..bb801aa314 --- /dev/null +++ b/platform/mcu/xr871/src/image/flash.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _IMAGE_FLASH_H_ +#define _IMAGE_FLASH_H_ + +#include "types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define FLASH_OPEN_TIMEOUT (5000) + +uint32_t flash_read(uint32_t flash, uint32_t src_addr, void *buf, uint32_t size); +uint32_t flash_write(uint32_t flash, uint32_t dst_addr, void *buf, uint32_t size); + +int32_t flash_erase_check(uint32_t flash, uint32_t addr, uint32_t size); +uint32_t flash_erase(uint32_t flash, uint32_t addr, uint32_t size); + +#ifdef __cplusplus +} +#endif + +#endif /* _IMAGE_FLASH_H_ */ diff --git a/platform/mcu/xr871/src/image/image.c b/platform/mcu/xr871/src/image/image.c new file mode 100644 index 0000000000..e3d2867edb --- /dev/null +++ b/platform/mcu/xr871/src/image/image.c @@ -0,0 +1,568 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include "flash.h" +#include "sys/image.h" +#include "image_debug.h" + +#define image_malloc(l) malloc(l) +#define image_free(p) free(p) +#define image_memcpy(d, s, n) memcpy(d, s, n) +#define image_memset(s, c, n) memset(s, c, n) + +#define IMAGE_CHECK_SIZE (1024) +#define IMAGE_EXPAND_SIZE (5) + +typedef struct { + uint32_t id; + uint32_t addr; +} sec_addr_t; + +typedef struct { + uint8_t sec_num[IMAGE_SEQ_NUM]; + sec_addr_t *sec_addr[IMAGE_SEQ_NUM]; + uint32_t flash[IMAGE_SEQ_NUM]; + uint32_t addr[IMAGE_SEQ_NUM]; + uint32_t image_size; + uint32_t boot_size; + image_seq_t seq; +} image_priv_t; + +image_priv_t image_priv; + +static void image_clear_sec_addr(void) +{ + image_seq_t seq; + + for (seq = IMAGE_SEQ_1ST; seq < IMAGE_SEQ_NUM; seq++) { + image_priv.sec_num[seq] = 0; + if (image_priv.sec_addr[seq]) { + image_free(image_priv.sec_addr[seq]); + image_priv.sec_addr[seq] = NULL; + } + } +} + +/** + * @brief Initialize the image module + * @param[in] flash Flash device number + * @param[in] addr Start address of the image region + * @param[in] size Size of the image region + * @return None + */ +void image_init(uint32_t flash, uint32_t addr, uint32_t size) +{ + section_header_t sh; + + IMAGE_DBG("%s(), %d, flash %d, addr %#010x, size %#010x\n", + __func__, __LINE__, flash, addr, size); + + if (flash_read(flash, addr, &sh, IMAGE_HEADER_SIZE) != IMAGE_HEADER_SIZE) { + IMAGE_ERR("read flash failed\n"); + return; + } + + image_clear_sec_addr(); + image_priv.flash[IMAGE_SEQ_1ST] = flash; + image_priv.addr[IMAGE_SEQ_1ST] = addr; + if (sh.priv[0] == IMAGE_INVALID_FLASH) + image_priv.flash[IMAGE_SEQ_2ND] = flash; + else + image_priv.flash[IMAGE_SEQ_2ND] = sh.priv[0]; + image_priv.addr[IMAGE_SEQ_2ND] = sh.priv[1]; + image_priv.image_size = size; + image_priv.boot_size = sh.next_addr; + image_priv.seq = IMAGE_SEQ_NUM; + + IMAGE_DBG("%s(), %d, flash_1st %d, addr_1st %#010x, flash_2nd %d, " + "addr_2nd %#010x, image_size %#010x, boot_size %#010x, seq %d\n", + __func__, __LINE__, + image_priv.flash[IMAGE_SEQ_1ST], image_priv.addr[IMAGE_SEQ_1ST], + image_priv.flash[IMAGE_SEQ_2ND], image_priv.addr[IMAGE_SEQ_2ND], + image_priv.image_size, image_priv.boot_size, image_priv.seq); +} + +/** + * @brief DeInitialize the image module + * @return None + */ +void image_deinit(void) +{ + image_clear_sec_addr(); + image_priv.flash[IMAGE_SEQ_1ST] = 0; + image_priv.addr[IMAGE_SEQ_1ST] = IMAGE_INVALID_ADDR; + image_priv.flash[IMAGE_SEQ_2ND] = 0; + image_priv.addr[IMAGE_SEQ_2ND] = IMAGE_INVALID_ADDR; + image_priv.image_size = 0; + image_priv.boot_size = 0; + image_priv.seq = IMAGE_SEQ_NUM; +} + +/** + * @brief Get OTA parameter + * @param[in] param Pointer to the OTA parameter + * @return None + */ +void image_get_ota_param(image_ota_param_t *param) +{ + if (param == NULL) { + IMAGE_ERR("param %p\n", param); + return; + } + + param->flash[IMAGE_SEQ_1ST] = image_priv.flash[IMAGE_SEQ_1ST]; + param->addr[IMAGE_SEQ_1ST] = image_priv.addr[IMAGE_SEQ_1ST]; + param->flash[IMAGE_SEQ_2ND] = image_priv.flash[IMAGE_SEQ_2ND]; + param->addr[IMAGE_SEQ_2ND] = image_priv.addr[IMAGE_SEQ_2ND]; + param->image_size = image_priv.image_size; + param->boot_size = image_priv.boot_size; + + IMAGE_DBG("%s(), %d, flash_1st %d, addr_1st %#010x, flash_2nd %d, " + "addr_2nd %#010x, image_size %#010x, boot_size %#010x\n", + __func__, __LINE__, + param->flash[IMAGE_SEQ_1ST], param->addr[IMAGE_SEQ_1ST], + param->flash[IMAGE_SEQ_2ND], param->addr[IMAGE_SEQ_2ND], + param->image_size, param->boot_size); +} + +/** + * @brief Set running image sequence + * @param[in] seq Image sequence + * @return None + */ +void image_set_running_seq(image_seq_t seq) +{ + IMAGE_DBG("%s(), %d, seq %d\n", __func__, __LINE__, seq); + + image_priv.seq = seq; +} + +/** + * @brief Get running image sequence + * @param[in] seq Pointer to image sequence + * @return None + */ +void image_get_running_seq(image_seq_t *seq) +{ + if (seq == NULL) { + IMAGE_ERR("seq %p\n", seq); + return; + } + + IMAGE_DBG("%s(), %d, seq %d\n", __func__, __LINE__, image_priv.seq); + + *seq = image_priv.seq; +} + +static sec_addr_t *image_expand_addr(image_seq_t seq) +{ + sec_addr_t *sec_addr; + + if (image_priv.sec_num[seq] == 0) + return image_malloc(IMAGE_EXPAND_SIZE * sizeof(sec_addr_t)); + + sec_addr = image_malloc((image_priv.sec_num[seq] + IMAGE_EXPAND_SIZE) * sizeof(sec_addr_t)); + if (sec_addr) { + image_memcpy(sec_addr, image_priv.sec_addr[seq], image_priv.sec_num[seq] * sizeof(sec_addr_t)); + image_free(image_priv.sec_addr[seq]); + } + + return sec_addr; +} + +static uint32_t image_get_addr(image_seq_t seq, uint32_t id) +{ + uint8_t index; + uint32_t addr; + uint32_t next_addr; + sec_addr_t *sec_addr; + + if (seq >= IMAGE_SEQ_NUM) { + IMAGE_ERR("seq %d\n", seq); + return IMAGE_INVALID_ADDR; + } + + for (index = 0; index < image_priv.sec_num[seq]; index++) { + if (id == image_priv.sec_addr[seq][index].id) + return image_priv.sec_addr[seq][index].addr; + } + + if (image_priv.sec_num[seq] == 0) { + addr = image_priv.addr[IMAGE_SEQ_1ST]; + } else { + addr = image_priv.sec_addr[seq][image_priv.sec_num[seq] - 1].addr; + flash_read(image_priv.flash[seq], addr + offsetof(section_header_t, next_addr), &next_addr, sizeof(next_addr)); + addr = image_priv.addr[seq] + next_addr; + } + + while (next_addr != IMAGE_INVALID_ADDR) { + if ((image_priv.sec_num[seq] % IMAGE_EXPAND_SIZE) == 0) { + sec_addr = image_expand_addr(seq); + if (sec_addr == NULL) { + IMAGE_ERR("sec_addr %p, seq %d, num %d\n", sec_addr, seq, image_priv.sec_num[seq]); + return IMAGE_INVALID_ADDR; + } else { + image_priv.sec_addr[seq] = sec_addr; + } + } + + image_priv.sec_addr[seq][image_priv.sec_num[seq]].addr = addr; + flash_read(image_priv.flash[seq], addr + offsetof(section_header_t, id), + &(image_priv.sec_addr[seq][image_priv.sec_num[seq]].id), sizeof(id)); + image_priv.sec_num[seq]++; + + if (id == image_priv.sec_addr[seq][image_priv.sec_num[seq] - 1].id) { + return image_priv.sec_addr[seq][image_priv.sec_num[seq] - 1].addr; + } + flash_read(image_priv.flash[seq], addr + offsetof(section_header_t, next_addr), + &next_addr, sizeof(next_addr)); + addr = image_priv.addr[seq] + next_addr; + } + + IMAGE_ERR("seq %d, id %#010x\n", seq, id); + return IMAGE_INVALID_ADDR; +} + +/** + * @brief Get address of the specified section in running image + * @param[in] id ID of the specified section + * @return section address + */ +uint32_t image_get_section_addr(uint32_t id) +{ + IMAGE_DBG("%s(), %d, id %#010x\n", __func__, __LINE__, id); + + return image_get_addr(image_priv.seq, id); +} + +/** + * @brief Read an amount of image data from flash + * @param[in] id Section ID of the image data + * @param[in] seg Section segment of the image data + * @param[in] offset Offset into the segment from where to read data + * @param[in] buf Pointer to the data buffer + * @param[in] size Number of bytes to be read + * @return Number of bytes read + */ +uint32_t image_read(uint32_t id, image_seg_t seg, uint32_t offset, void *buf, uint32_t size) +{ + uint32_t addr; + uint32_t body_len; + + IMAGE_DBG("%s(), %d, id %#010x, seg %d, offset %d, buf %p, size %d\n", + __func__, __LINE__, id, seg, offset, buf, size); + + if (buf == NULL) { + IMAGE_ERR("buf %p\n", buf); + return 0; + } + + addr = image_get_addr(image_priv.seq, id); + if (addr == IMAGE_INVALID_ADDR) { + IMAGE_ERR("seq %d, id %#010x, addr %#010x\n", image_priv.seq, id, addr); + return 0; + } + + switch (seg) { + case IMAGE_SEG_HEADER: + break; + case IMAGE_SEG_BODY: + addr += IMAGE_HEADER_SIZE; + break; + case IMAGE_SEG_TAILER: + flash_read(image_priv.flash[image_priv.seq], addr + offsetof(section_header_t, body_len), + &body_len, sizeof(body_len)); + addr += IMAGE_HEADER_SIZE + body_len; + break; + default : + IMAGE_ERR("seg %d\n", seg); + return 0; + } + + addr += offset; + + return flash_read(image_priv.flash[image_priv.seq], addr, buf, size); + +} + +/** + * @brief Write an amount of image data to flash + * @param[in] id Section ID of the image data + * @param[in] seg Section segment of the image data + * @param[in] offset Offset into the segment from where to write data + * @param[in] buf Pointer to the data buffer + * @param[in] size Number of bytes to be written + * @return Number of bytes written + */ +uint32_t image_write(uint32_t id, image_seg_t seg, uint32_t offset, void *buf, uint32_t size) +{ + uint32_t addr; + uint32_t body_len; + + IMAGE_DBG("%s(), %d, id %#010x, seg %d, offset %d, buf %p, size %d\n", + __func__, __LINE__, id, seg, offset, buf, size); + + if (buf == NULL) { + IMAGE_ERR("buf %p\n", buf); + return 0; + } + + addr = image_get_addr(image_priv.seq, id); + if (addr == IMAGE_INVALID_ADDR) { + IMAGE_ERR("seq %d, id %#010x, addr %#010x\n", image_priv.seq, id, addr); + return 0; + } + + switch (seg) { + case IMAGE_SEG_HEADER: + image_clear_sec_addr(); + break; + case IMAGE_SEG_BODY: + addr += IMAGE_HEADER_SIZE; + break; + case IMAGE_SEG_TAILER: + flash_read(image_priv.flash[image_priv.seq], addr + offsetof(section_header_t, body_len), + &body_len, sizeof(body_len)); + addr += IMAGE_HEADER_SIZE + body_len; + break; + default : + IMAGE_ERR("seg %d\n", seg); + return 0; + } + + addr += offset; + + return flash_write(image_priv.flash[image_priv.seq], addr, buf, size); +} + +static uint16_t image_checksum16(uint8_t *data, uint32_t len) +{ + uint16_t cs = 0; + uint16_t *p = (uint16_t *)data; + + while (len > 1) { + cs += *p++; + len -= 2; + } + if (len) { + cs += *(uint8_t *)p; + } + + return cs; +} + +/** + * @brief Get 16-bit checksum of the data buffer + * @param[in] buf Pointer to the data buffer + * @param[in] len length of the data buffer + * @return 16-bit checksum + */ +uint16_t image_get_checksum(void *buf, uint32_t len) +{ + if (buf == NULL) { + IMAGE_ERR("buf %p\n", buf); + return 0; + } + + return image_checksum16(buf, len); +} + +/** + * @brief Check vadility of the section header + * @param[in] sh Pointer to the section header + * @retval image_val_t, IMAGE_VALID on valid, IMAGE_INVALID on invalid + */ +image_val_t image_check_header(section_header_t *sh) +{ + if (sh == NULL) { + IMAGE_ERR("sh %p\n", sh); + return IMAGE_INVALID; + } + + if ((sh->magic_number != IMAGE_MAGIC_NUMBER) + || (0xFFFF != image_get_checksum(sh, IMAGE_HEADER_SIZE))) { + IMAGE_WARN("%s(), %d, magic number %#010x, checksum %#06x\n", __func__, __LINE__, + sh->magic_number, image_get_checksum(sh, IMAGE_HEADER_SIZE)); + return IMAGE_INVALID; + } + + return IMAGE_VALID; +} + +/** + * @brief Check vadility of the section data(body and tailer) + * @param[in] sh Pointer to the section header + * @param[in] body Pointer to the section body + * @param[in] body_len Length of the section body + * @param[in] tailer Pointer to the section tailer + * @param[in] tailer_len Length of the section tailer + * @retval image_val_t, IMAGE_VALID on valid, IMAGE_INVALID on invalid + */ +image_val_t image_check_data(section_header_t *sh, void *body, uint32_t body_len, + void *tailer, uint32_t tailer_len) +{ + uint8_t joint[2]; + uint16_t data_checksum; + + if ((sh == NULL) || (body == NULL) || (body_len == 0) + || ((tailer == NULL) && (tailer_len != 0))) { + IMAGE_ERR("sh %p, body %p, body len %d, tailer %p, tailer len %d\n", + sh, body, body_len, tailer, tailer_len); + return IMAGE_INVALID; + } + + if (tailer_len == 0) { + data_checksum = sh->data_chksum + + image_checksum16(body, body_len); + } else if ((body_len & 0x1) == 0) { + data_checksum = sh->data_chksum + + image_checksum16(body, body_len) + + image_checksum16(tailer, tailer_len); + } else { + image_memcpy(joint, ((uint8_t *)body) + body_len - 1, 1); + image_memcpy(joint + 1, tailer, 1); + data_checksum = sh->data_chksum + + image_checksum16(body, body_len - 1) + + image_checksum16(joint, 2) + + image_checksum16(((uint8_t *)tailer) + 1, tailer_len - 1); + } + + if (data_checksum != 0xFFFF) { + IMAGE_WARN("%s(), %d, checksum %#06x\n", __func__, __LINE__, data_checksum); + return IMAGE_INVALID; + } + + return IMAGE_VALID; +} + +static image_val_t _image_check_section(uint32_t flash, uint32_t addr) +{ + uint16_t data_chksum; + uint32_t data_size; + uint8_t *buf_check; + section_header_t *sh; + + sh = (section_header_t *)image_malloc(IMAGE_HEADER_SIZE); + if (sh == NULL) { + IMAGE_ERR("sh %p\n", sh); + return IMAGE_INVALID; + } + + flash_read(flash, addr, sh, IMAGE_HEADER_SIZE); + if (image_check_header(sh) == IMAGE_INVALID) { + IMAGE_ERR("flash %d, addr %#010x\n", flash, addr); + image_free(sh); + return IMAGE_INVALID; + } + + data_chksum = sh->data_chksum; + data_size = sh->data_size; + image_free(sh); + + buf_check = (uint8_t *)image_malloc(IMAGE_CHECK_SIZE); + if (buf_check == NULL) { + IMAGE_ERR("buf_check %p\n", buf_check); + return IMAGE_INVALID; + } + + addr += IMAGE_HEADER_SIZE; + while (data_size > 0) { + if (data_size >= IMAGE_CHECK_SIZE) { + flash_read(flash, addr, buf_check, IMAGE_CHECK_SIZE); + data_chksum += image_checksum16(buf_check, IMAGE_CHECK_SIZE); + addr += IMAGE_CHECK_SIZE; + data_size -= IMAGE_CHECK_SIZE; + } else { + flash_read(flash, addr, buf_check, data_size); + data_chksum += image_checksum16(buf_check, data_size); + addr += data_size; + data_size -= data_size; + } + } + image_free(buf_check); + + if (data_chksum != 0xFFFF) { + IMAGE_WARN("%s(), %d, data checksum %#06x\n", __func__, __LINE__, data_chksum); + return IMAGE_INVALID; + } + + return IMAGE_VALID; +} + +/** + * @brief Check vadility of the specified section in specified image + * @param[in] seq Sequence of the specified image + * @param[in] id Section ID of the specified section + * @retval image_val_t, IMAGE_VALID on valid, IMAGE_INVALID on invalid + */ +image_val_t image_check_section(image_seq_t seq, uint32_t id) +{ + uint32_t addr; + + addr = image_get_addr(seq, id); + if (addr == IMAGE_INVALID_ADDR) { + IMAGE_ERR("seq %d, id %#010x, addr %#010x\n", seq, id, addr); + return IMAGE_INVALID; + } + + IMAGE_DBG("%s(), %d, seq %d, flash %d, id %#010x, addr %#010x\n", + __func__, __LINE__, seq, image_priv.flash[seq], id, addr); + + return _image_check_section(image_priv.flash[seq], addr); +} + +/** + * @brief Check vadility of all the sections in specified image + * @param[in] seq Sequence of the specified image + * @retval image_val_t, IMAGE_VALID on valid, IMAGE_INVALID on invalid + */ +image_val_t image_check_sections(image_seq_t seq) +{ + uint32_t addr; + uint32_t next_addr = image_priv.boot_size; + + IMAGE_DBG("%s(), %d, seq %d, next addr %#010x\n", __func__, __LINE__, seq, next_addr); + + while (next_addr != IMAGE_INVALID_ADDR) { + addr = image_priv.addr[seq] + next_addr; + if (_image_check_section(image_priv.flash[seq], addr) == IMAGE_INVALID) { + IMAGE_WARN("%s(), %d, seq %d, flash %d, addr %#010x\n", + __func__, __LINE__, seq, image_priv.flash[seq], addr); + return IMAGE_INVALID; + } + flash_read(image_priv.flash[seq], addr + offsetof(section_header_t, next_addr), + &next_addr, sizeof(next_addr)); + } + + return IMAGE_VALID; +} + diff --git a/platform/mcu/xr871/src/image/image.mk b/platform/mcu/xr871/src/image/image.mk new file mode 100644 index 0000000000..3878ba6359 --- /dev/null +++ b/platform/mcu/xr871/src/image/image.mk @@ -0,0 +1,6 @@ +NAME := image + +$(NAME)_TYPE := kernel +$(NAME)_SOURCES := fdcm.c \ + flash.c \ + image.c \ No newline at end of file diff --git a/platform/mcu/xr871/src/image/image_debug.h b/platform/mcu/xr871/src/image/image_debug.h new file mode 100644 index 0000000000..2d979fd56d --- /dev/null +++ b/platform/mcu/xr871/src/image/image_debug.h @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _IMAGE_DEBUG_H_ +#define _IMAGE_DEBUG_H_ + +#include +#include "sys/xr_util.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define IMAGE_DBG_ON 0 +#define IMAGE_WARN_ON 0 +#define IMAGE_ERR_ON 1 +#define IMAGE_ABORT_ON 0 + +#define FDCM_DBG_ON 0 +#define FDCM_WARN_ON 0 +#define FDCM_ERR_ON 1 +#define FDCM_ABORT_ON 0 + +#define FLASH_DBG_ON 0 +#define FLASH_WARN_ON 0 +#define FLASH_ERR_ON 1 +#define FLASH_ABORT_ON 0 + +#define IMAGE_SYSLOG printf +#define IMAGE_ABORT() sys_abort() + +#define IMAGE_LOG(flags, fmt, arg...) \ + do { \ + if (flags) \ + IMAGE_SYSLOG(fmt, ##arg); \ + } while (0) + +#define IMAGE_DBG(fmt, arg...) IMAGE_LOG(IMAGE_DBG_ON, "[image] "fmt, ##arg) +#define IMAGE_WARN(fmt, arg...) IMAGE_LOG(IMAGE_WARN_ON, "[image WARN] "fmt, ##arg) +#define IMAGE_ERR(fmt, arg...) \ + do { \ + IMAGE_LOG(IMAGE_ERR_ON, "[image ERR] %s():%d, "fmt, \ + __func__, __LINE__, ##arg); \ + if (IMAGE_ABORT_ON) \ + IMAGE_ABORT(); \ + } while (0) + +#define FDCM_SYSLOG printf +#define FDCM_ABORT() sys_abort() + +#define FDCM_LOG(flags, fmt, arg...) \ + do { \ + if (flags) \ + FDCM_SYSLOG(fmt, ##arg); \ + } while (0) + +#define FDCM_DBG(fmt, arg...) FDCM_LOG(FDCM_DBG_ON, "[FDCM] "fmt, ##arg) +#define FDCM_WARN(fmt, arg...) FDCM_LOG(FDCM_WARN_ON, "[FDCM WARN] "fmt, ##arg) +#define FDCM_ERR(fmt, arg...) \ + do { \ + FDCM_LOG(FDCM_ERR_ON, "[FDCM ERR] %s():%d, "fmt, \ + __func__, __LINE__, ##arg); \ + if (FDCM_ABORT_ON) \ + FDCM_ABORT(); \ + } while (0) + +#define FLASH_SYSLOG printf +#define FLASH_ABORT() sys_abort() + +#define FLASH_LOG(flags, fmt, arg...) \ + do { \ + if (flags) \ + FLASH_SYSLOG(fmt, ##arg); \ + } while (0) + +#define FLASH_DBG(fmt, arg...) FLASH_LOG(FLASH_DBG_ON, "[flash] "fmt, ##arg) +#define FLASH_WARN(fmt, arg...) FLASH_LOG(FLASH_WARN_ON, "[flash WARN] "fmt, ##arg) +#define FLASH_ERR(fmt, arg...) \ + do { \ + FLASH_LOG(FLASH_ERR_ON, "[flash ERR] %s():%d, "fmt, \ + __func__, __LINE__, ##arg); \ + if (FLASH_ABORT_ON) \ + FLASH_ABORT(); \ + } while (0) + +#ifdef __cplusplus +} +#endif + +#endif /* _IMAGE_DEBUG_H_ */ diff --git a/platform/mcu/xr871/src/net/lwip/checksum.c b/platform/mcu/xr871/src/net/lwip/checksum.c new file mode 100644 index 0000000000..73ce526438 --- /dev/null +++ b/platform/mcu/xr871/src/net/lwip/checksum.c @@ -0,0 +1,126 @@ +/* Copyright (C) 2013 - Adam Green (https://github.com/adamgreen) + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ +#if defined(__GNUC__) && defined(__thumb2__) + + +/* This is a hand written Thumb-2 assembly language version of the + algorithm 3 version of lwip_standard_chksum in lwIP's inet_chksum.c. It + performs the checksumming 32-bits at a time and even unrolls the loop to + perform two of these 32-bit adds per loop iteration. + + Returns: + 16-bit 1's complement summation (not inversed). + + NOTE: This function does return a uint16_t from the assembly language code + but is marked as void so that GCC doesn't issue warning because it + doesn't know about this low level return. +*/ +__attribute__((naked)) void /*uint16_t*/ thumb2_checksum(const void* pData, int length) +{ + __asm ( + ".syntax unified\n" + ".thumb\n" + + // Push non-volatile registers we use on stack. Push link register too to + // keep stack 8-byte aligned and allow single pop to restore and return. + " push {r4, lr}\n" + // Initialize sum, r2, to 0. + " movs r2, #0\n" + // Remember whether pData was at odd address in r3. This is used later to + // know if it needs to swap the result since the summation will be done at + // an offset of 1, rather than 0. + " ands r3, r0, #1\n" + // Need to 2-byte align? If not skip ahead. + " beq 1$\n" + // We can return if there are no bytes to sum. + " cbz r1, 9$\n" + + // 2-byte align. + // Place the first data byte in odd summation location since it needs to be + // swapped later. It's ok to overwrite r2 here as it only had a value of 0 + // up until now. Advance r0 pointer and decrement r1 length as we go. + " ldrb r2, [r0], #1\n" + " lsls r2, r2, #8\n" + " subs r1, r1, #1\n" + + // Need to 4-byte align? If not skip ahead. + "1$:\n" + " ands r4, r0, #3\n" + " beq 2$\n" + // Have more than 1 byte left to align? If not skip ahead to take care of + // trailing byte. + " cmp r1, #2\n" + " blt 7$\n" + + // 4-byte align. + " ldrh r4, [r0], #2\n" + " adds r2, r2, r4\n" + " subs r1, r1, #2\n" + + // Main summing loop which sums up data 2 words at a time. + // Make sure that we have more than 7 bytes left to sum. + "2$:\n" + " cmp r1, #8\n" + " blt 3$\n" + // Sum next two words. Applying previous upper 16-bit carry to + // lower 16-bits. + " ldr r4, [r0], #4\n" + " adds r2, r4\n" + " adc r2, r2, #0\n" + " ldr r4, [r0], #4\n" + " adds r2, r4\n" + " adc r2, r2, #0\n" + " subs r1, r1, #8\n" + " b 2$\n" + + // Sum up any remaining half-words. + "3$:\n" + // Make sure that we have more than 1 byte left to sum. + " cmp r1, #2\n" + " blt 7$\n" + // Sum up next half word, continue to apply carry. + " ldrh r4, [r0], #2\n" + " adds r2, r4\n" + " adc r2, r2, #0\n" + " subs r1, r1, #2\n" + " b 3$\n" + + // Handle trailing byte, if it exists + "7$:\n" + " cbz r1, 8$\n" + " ldrb r4, [r0]\n" + " adds r2, r4\n" + " adc r2, r2, #0\n" + + // Fold 32-bit checksum into 16-bit checksum. + "8$:\n" + " ubfx r4, r2, #16, #16\n" + " ubfx r2, r2, #0, #16\n" + " adds r2, r4\n" + " ubfx r4, r2, #16, #16\n" + " ubfx r2, r2, #0, #16\n" + " adds r2, r4\n" + + // Swap bytes if started at odd address + " cbz r3, 9$\n" + " rev16 r2, r2\n" + + // Return final sum. + "9$: mov r0, r2\n" + " pop {r4, pc}\n" + ); +} + +#endif diff --git a/platform/mcu/xr871/src/net/lwip/lwip.mk b/platform/mcu/xr871/src/net/lwip/lwip.mk new file mode 100644 index 0000000000..ee51eb8060 --- /dev/null +++ b/platform/mcu/xr871/src/net/lwip/lwip.mk @@ -0,0 +1,5 @@ +NAME := lwip + +$(NAME)_TYPE := kernel +$(NAME)_SOURCES := memcpy.c \ + checksum.c diff --git a/platform/mcu/xr871/src/net/lwip/memcpy.c b/platform/mcu/xr871/src/net/lwip/memcpy.c new file mode 100644 index 0000000000..dc71888048 --- /dev/null +++ b/platform/mcu/xr871/src/net/lwip/memcpy.c @@ -0,0 +1,59 @@ +/* Copyright (C) 2013 - Adam Green (https://github.com/adamgreen) + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ +#if defined(__GNUC__) && defined(__thumb2__) + +#include // for size_t + + +/* This is a hand written Thumb-2 assembly language version of the + standard C memcpy() function that can be used by the lwIP networking + stack to improve its performance. It copies 4 bytes at a time and + unrolls the loop to perform 4 of these copies per loop iteration. +*/ +__attribute__((naked)) void thumb2_memcpy(void* pDest, const void* pSource, size_t length) +{ + __asm ( + ".syntax unified\n" + ".thumb\n" + + // Copy 16 bytes at a time first. + " lsrs r3, r2, #4\n" + " beq.n 2$\n" + "1$: ldr r12, [r1], #4\n" + " str r12, [r0], #4\n" + " ldr r12, [r1], #4\n" + " str r12, [r0], #4\n" + " ldr r12, [r1], #4\n" + " str r12, [r0], #4\n" + " ldr r12, [r1], #4\n" + " str r12, [r0], #4\n" + " subs r3, #1\n" + " bne 1$\n" + + // Copy byte by byte for what is left. + "2$:\n" + " ands r3, r2, #0xf\n" + " beq.n 4$\n" + "3$: ldrb r12, [r1], #1\n" + " strb r12, [r0], #1\n" + " subs r3, #1\n" + " bne 3$\n" + + // Return to caller. + "4$: bx lr\n" + ); +} + +#endif diff --git a/platform/mcu/xr871/src/net/udhcp/AUTHORS b/platform/mcu/xr871/src/net/udhcp/AUTHORS new file mode 100644 index 0000000000..89a6de41df --- /dev/null +++ b/platform/mcu/xr871/src/net/udhcp/AUTHORS @@ -0,0 +1,13 @@ +udhcp server/client package +----------------------- + +Russ Dill +Matthew Ramsay +Chris Trew + +Other Credits: +-------------- +Moreton Bay (http://www.moretonbay.com/) +Lineo (http://opensource.lineo.com) + + diff --git a/platform/mcu/xr871/src/net/udhcp/COPYING b/platform/mcu/xr871/src/net/udhcp/COPYING new file mode 100644 index 0000000000..a43ea2126f --- /dev/null +++ b/platform/mcu/xr871/src/net/udhcp/COPYING @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 675 Mass Ave, Cambridge, MA 02139, USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + Appendix: How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) 19yy + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) 19yy name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/platform/mcu/xr871/src/net/udhcp/ChangeLog b/platform/mcu/xr871/src/net/udhcp/ChangeLog new file mode 100644 index 0000000000..34cfe16f9e --- /dev/null +++ b/platform/mcu/xr871/src/net/udhcp/ChangeLog @@ -0,0 +1,249 @@ +0.9.8 (021031) ++ split up README files (me) ++ use /dev/urandom to seed xid's (instead of time(0)) (me) ++ fixed renew behavior (me) ++ udhcp now fits nicely into busybox + (Glenn McGrath as well as myself) ++ updated client manpage (me) ++ both client and server now use sockets for signal handling, + hopefully, this will be the last needed change in signal + handling, I'm fairly certain all the possible races are now + closed. (me) ++ The server now restarts the auto_time timer when it receives + a SIGUSR1 (write out config file). (me) ++ Improve signal handling (David Poole) ++ Fix to config file parsing (Matt Kraai) ++ Fix load lease logic (me) ++ Fix clear_lease logic (me) ++ -h is now an alias for -H (udhcp bug #1253) ++ Shorter timeout on not receiving offers (me) ++ Improved signal behavior by client (me) ++ Would never assign end address (Keith Smith ) ++ Was improperly reporting yiaddr as siaddr (ben-udhcp@bdlow.net) + udhcp bug#1256 ++ Fixed reading of client id (David Poole ) ++ change sys_errlist[] to strerror() as it aparently doesn't exist + (andersee ) ++ fixed get_raw_packet so it returns -2 on non fatal errors + (Ted Lemon ) ++ Improved (hopefully) NAKing behavior (me) ++ Added -b option (Jouni Malinen) ++ Compute checksums correctly on big endian hosts + (Jouni Malinen ) + +0.9.7 (020526) ++ Use add_lease in read_leases, sanitizes leases more, and clears out exprired + ones if there is no more room (me) ++ Moved udhcpd.leases to /var/lib/misc/udhcpd.leases (Debian bug #147747) ++ Change (obsolete) AF_INET in arping.c to PF_PACKET (Debian bug #127049) ++ Added script hook for DHCPNAK (nak), as well as providing the message option + (me) ++ Generate the paramaters request list by seeing what options in options.c are + ored with OPTION_REQ in options.c ++ Fix dhcp renew forgetfullness on client (bug #1230) ++ Fix dhcp release bug on client (bug #1231) ++ Set option request list for DHCP renew (bug #1233) ++ Set BOOTREQUEST/REPLY properly ++ Change client-identifier field to popularly expected behavior (me) ++ Only reopen port on errors (me) ++ Change fork/close/setsid structures to daemon() (me) ++ Allow user to specify udhcpd config file at run time (Steven, me) ++ Write pidfile after changing it (Steven CTR Carr ) ++ Added env var docs to udhcpc man page (Matt) ++ Standardized lowercase udhcp in documentation (me) ++ Accept packets without a UDP checksum (me) ++ Accept packets with extra garbage (me) ++ Better error handling in files.c (me) ++ Combined read_interface function to reduce COMBINED_BINARY size (me) ++ Drop calc_length(), some servers choke on smaller packets (me) ++ Try to clean some fat out (me) + +0.9.6 (011001) ++ Added bootp paramaters to server (me) ++ Added bootp paramaters to client (me) ++ Added vendor id to client (me) ++ Better pidfile handling in client and server (me) ++ Added man pages (Matt Kraai ) + +0.9.5 (010914) ++ Fixed $HOME and $PATH env passing (me) ++ Fixed client to only listen for raw packets on correct interface (me) ++ added --quit,-q option to quit after a lease is obtained (me) ++ Fixed 100% CPU utilization by client when interface is down (me) + +0.9.4 (010827) ++ Force broadcast to broken clients that request unicast (ie, MSFT 98) ++ Make install rules (Adam J. Richter ) ++ One scripts, instead of many (Adam) ++ Removed script paramater info files (env vars only) (Adam) ++ Controlling of forking behavior in client (Adam) ++ General script.c/dhcpc.c cleanups (Adam) + +0.9.3 (010820) ++ Increased debugging verbosity (me) ++ Cut trailing whitespace when reading config file (me) ++ added hostname option to client (me) ++ fixed a strncpy bug in script.c (me) ++ fixed a leaky socket in dhcpc.c (me) ++ fixed a leaky socket in dhcpd.c (me) + +0.9.2 (010810) ++ Added raw sockets to client (me) ++ alignment fixes (Mark Huang) ++ compiler warning fixes (Mark Huang) ++ client now sends parameter list (Mark Huang/me) ++ added ipttl option ++ Does now not request broadcast packets + +0.9.1 (010806) ++ Added udhcpc client ++ reorganized functions/files ++ listening socket now only binds to one interface + +0.9.0 (010720) Major rewrite, current changes, goals: ++ should not segfault on bogus packets. ++ Options can be read from sname and file fields. ++ supports all DHCP messages (release, decline, inform). ++ IP block is now specified by a range of IP's. ++ Leases file now contains lease time (relative, or absolute). ++ Just about any DHCP option is now supported. ++ DNS entries are no longer read from resolv.conf ++ Lease file can be written periodically when the process receives a SIGUSR1 ++ arpping should be supported on all arches. ++ support for DHCP relays. ++ DHCP messages can be unicast if the client requests it. ++ many, many, many other things. + +0.8.29 (000323) ++ stable(?) release + + +0.8.28 (000323) ++ removed alarm as it was causing server to go down ++ removed debugging ++ break down dhcpd.c into manageable files + + +0.8.27 (000221) ++ OFFER also sends gateway/subnet (for picky dhcp clients) ++ multiple DNS now handled from resolv.conf if available ++ multiple WINS (from dhcpd.conf) + +0.8.25 (000120) ++ now compiles *and* runs on a generic linux system + tested with a windows 98 client and the sample config + files in the samples directory. + +0.8.24 (000117) ++ makeiplist tool has basic functionality in place ++ new sample config files ++ route add -host 255.255.255.255 dev eth0 added for generic linux + +0.8.23 (000117) ++ NETtel specific fix for ignoring dhcp requests on 2nd interface + +0.8.22 (000113) ++ minor changes to compile under a generic linux system ++ minor config file location changes for a generic linux system ++ makeiplist fixes.. still incomplete.. but etting closer + +0.8.21 (000113) ++ now sends the correct server ip instead of hardcoded value ++ minor debugging fixes for critical messages + +0.8.20 (000106) ++ cut out dhcp server checking.. this was causing dialout ppp + sessions with idle time set to never time out. ++ also removed the 10 second pause before launching.. as this + was originally to stop it replying to a dhcp client + on a NETtel which was really a bad way to do it in the + first place :-) + +0.8.19 (000104) ++ fixes for route add -host on a machine that needs to run both + a DHCP client and server (dual eth box) + +0.8.18 (991220) + ++ Race conditions fixed by disabling alarm whilst the server is busy ++ Fixed continous clearing of the offered array so that it is only cleared + when it is dirty - (could change the position of when dirty is set) + +0.8.17 (991212) + +- has problems clearing out the offered array + +0.8.16 (991203) ++ Non blocking error is changes to informational as it is not really + an error + +0.8.15 (991129) ++ Servs the dns field 3 times (Nettel only) so that windows servers + dont time out whilst nettel is booting + +0.8.14 (991126) ++ added owner check for the offered array so clean out time may be + increased ++ added new func to print out chadder/MAC + +0.8.13 (991125) ++ added win95 support (w95 changed xid halfway through conversation) ++ had to change the offered array to use hardware addresses instead of xid ++ fixed re offered bug ++ added more debugging + +0.8.12 (991111) ++ debugging was real bad.. cleaned up a bit.. needs overhaul + + +0.8.11 (991110) ++ fixed up offeredAddr array to actually be used now!! offeredAddr is + used to see if another simultaneous connecting client was offered + an address that we are about to offer another client (multiple + client bug) ++ removed re_offered variable as it breaks multiple client support ++ added lease time to ACK -- doesn't work if in OFFER ++ decreased internal array clear delay to 60 seconds ++ minor findAddr bug (returning -1 instead of 0) ++ if clients xid already in offeredAddr offer the same addr and don't add a + new addr to offered (caused by a client issuing multiple DISCOVERs) + +0.8.10 (991105) ++ \n bug in arpping ++ minor debugging changes (removed printfs etc) ++ started browseiplist (not finished) + +0.8.9 (19991105) ++ fixed options array size bug (options were cut off) + +0.8.8 (19991105) ++ ignores requests from dhcpcd on the same machine + +0.8.7 (19991104) ++ don't die if we can't bind to search for existing DHCP server ++ slightly more verbose syslogging + +0.8.6 (19991103) ++ added makeiplist (not finished -- core dumps) ++ minor debug changes + +0.8.5 (19991029) ++ exits if another DHCP server is already on the network ++ added Linux Makefile + +0.8.4 (19991026) ++ minor bug fix in findaddr preventing an addr being found + +0.8.3 (19991025) ++ fixed up debugging ++ minor hwaddr issues + +0.8.2 (19991022) ++ free leases (new arpping code from dhcpcd) ++ fixed bug where crashes if no leases/iplist file ++ syslogging and debugging switch ++ serve DNS from resolv.conf ++ fixed bug where new lease added if same mac offered ++ now checks the ip is free b4 offering ++ now supports wins server + diff --git a/platform/mcu/xr871/src/net/udhcp/Makefile b/platform/mcu/xr871/src/net/udhcp/Makefile new file mode 100644 index 0000000000..41ab910f5c --- /dev/null +++ b/platform/mcu/xr871/src/net/udhcp/Makefile @@ -0,0 +1,39 @@ +# +# Rules for building library +# + +# ---------------------------------------------------------------------------- +# common rules +# ---------------------------------------------------------------------------- +ROOT_PATH := ../../.. + +include $(ROOT_PATH)/gcc.mk + +# ---------------------------------------------------------------------------- +# library and objects +# ---------------------------------------------------------------------------- +LIBS := libudhcp.a + +OBJS := usr_dhcpd.o \ + arpping.o \ + files.o \ + leases.o \ + serverpacket.o \ + options.o \ + socket.o \ + packet.o \ + dhcp_time.o + +# extra flags +VER := 0.9.8 +CC_FLAGS += -DVERSION='"$(VER)"' -DXR_DHCPD +CC_FLAGS += -DDHCPD_TIMEALT \ + -DDHCPD_LWIP \ + -DDHCPD_HEAP_REPLACE_STACK \ + -DDHCPD_USRCFG \ + -DDHCPD_FREERTOS \ + -DDHCPD_ICMPPING \ + -DDHCPD_LOW_LEVEL + +# library make rules +include $(LIB_MAKE_RULES) diff --git a/platform/mcu/xr871/src/net/udhcp/README b/platform/mcu/xr871/src/net/udhcp/README new file mode 100644 index 0000000000..5f4bb78a84 --- /dev/null +++ b/platform/mcu/xr871/src/net/udhcp/README @@ -0,0 +1,50 @@ +udhcp server/client package readme +------------------------- + +The udhcp server/client package is primarily geared towards embedded +systems. It does however, strive to be fully functional, and RFC +compliant. + + +compile time options +------------------- + +The Makefile contains three of the compile time options: + + DEBUG: If DEBUG is defined, udhcpd will output extra debugging + output, compile with -g, and not fork to the background when run. + SYSLOG: If SYSLOG is defined, udhcpd will log all its messages + syslog, otherwise, it will attempt to log them to stdout. + + COMBINED_BINARY: If COMBINED_BINARY is define, one binary, udhcpd, + is created. If called as udhcpd, the dhcp server will be started. + If called as udhcpc, the dhcp client will be started. + +dhcpd.h contains the other two compile time options: + + LEASE_TIME: The default lease time if not specified in the config + file. + + DHCPD_CONFIG_FILE: The defualt config file to use. + +options.c contains a set of dhcp options for the client: + + name[10]: The name of the option as it will appear in scripts + + flags: The type of option, as well as if it will be requested + by the client (OPTION_REQ) + + code: The DHCP code for this option + + +busybox drop-in +-------------- +udhcp is now a drop-in component for busybox (http://busybox.net). +To update busybox to the latest revision, simply do a: + +cp *.[ch] README AUTHORS COPYING ChangeLog TODO \ + /networking/udhcp + +The only two files udhcp does not provide are config.in and +Makefile.in, so these may need to be updated from time to time. + diff --git a/platform/mcu/xr871/src/net/udhcp/README.dumpleases b/platform/mcu/xr871/src/net/udhcp/README.dumpleases new file mode 100644 index 0000000000..6367710c4f --- /dev/null +++ b/platform/mcu/xr871/src/net/udhcp/README.dumpleases @@ -0,0 +1,17 @@ +udhcp lease dump (dumpleases) +---------------------------- + +dumpleases displays the leases written out by the udhcpd server. Lease +times are stored in the file by time remaining in lease (for systems +without clock that works when there is no power), or by the absolute +time that it expires in seconds from epoch. dumpleases accepts the +following command line options: + +-a, --absolute Interpret lease times as expiration time. +-r, --remaining Interpret lease times as remaining time. +-f, --file=FILE Read lease information from FILE. +-h, --help Display help. + +Note that if udhcpd has not written a leases file recently, the output +of may not be up to date. + diff --git a/platform/mcu/xr871/src/net/udhcp/README.udhcpc b/platform/mcu/xr871/src/net/udhcp/README.udhcpc new file mode 100644 index 0000000000..3591605e3c --- /dev/null +++ b/platform/mcu/xr871/src/net/udhcp/README.udhcpc @@ -0,0 +1,139 @@ +udhcp client (udhcpc) +-------------------- + +The udhcp client negotiates a lease with the DHCP server and notifies +a set of scripts when a leases is obtained or lost. + + +command line options +------------------- + +The command line options for the udhcp client are: + +-c, --clientid=CLIENTID Client identifier +-H, --hostname=HOSTNAME Client hostname +-h, Alias for -H +-f, --foreground Do not fork after getting lease +-b, --background Fork to background if lease cannot be + immediately negotiated. +-i, --interface=INTERFACE Interface to use (default: eth0) +-n, --now Exit with failure if lease cannot be + immediately negotiated. +-p, --pidfile=file Store process ID of daemon in file +-q, --quit Quit after obtaining lease +-r, --request=IP IP address to request (default: none) +-s, --script=file Run file at dhcp events (default: + /usr/share/udhcpc/default.script) +-v, --version Display version + + +If the requested IP address cannot be obtained, the client accepts the +address that the server offers. + + +udhcp client scripts +------------------- + +When an event occurs, udhcpc calls the action script. The script by +default is /usr/share/udhcpc/default.script but this can be changed via +the command line arguments. The three possible arguments to the script +are: + + deconfig: This argument is used when udhcpc starts, and + when a leases is lost. The script should put the interface in an + up, but deconfigured state, ie: ifconfig $interface 0.0.0.0. + + bound: This argument is used when udhcpc moves from an + unbound, to a bound state. All of the paramaters are set in + enviromental variables, The script should configure the interface, + and set any other relavent parameters (default gateway, dns server, + etc). + + renew: This argument is used when a DHCP lease is renewed. All of + the paramaters are set in enviromental variables. This argument is + used when the interface is already configured, so the IP address, + will not change, however, the other DHCP paramaters, such as the + default gateway, subnet mask, and dns server may change. + + nak: This argument is used with udhcpc receives a NAK message. + The script with the deconfig argument will be called directly + afterwards, so no changes to the network interface are neccessary. + This hook is provided for purely informational purposes (the + message option may contain a reason for the NAK). + +The paramaters for enviromental variables are as follows: + + $HOME - The set $HOME env or "/" + $PATH - the set $PATH env or "/bin:/usr/bin:/sbin:/usr/sbin" + $1 - What action the script should perform + interface - The interface this was obtained on + ip - The obtained IP + siaddr - The bootp next server option + sname - The bootp server name option + boot_file - The bootp boot file option + subnet - The assigend subnet mask + timezone - Offset in seconds from UTC + router - A list of routers + timesvr - A list of time servers + namesvr - A list of IEN 116 name servers + dns - A list of DNS server + logsvr - A list of MIT-LCS UDP log servers + cookiesvr - A list of RFC 865 cookie servers + lprsvr - A list of LPR servers + hostname - The assigned hostname + bootsize - The length in 512 octect blocks of the bootfile + domain - The domain name of the network + swapsvr - The IP address of the client's swap server + rootpath - The path name of the client's root disk + ipttl - The TTL to use for this network + mtu - The MTU to use for this network + broadcast - The broadcast address for this network + ntpsrv - A list of NTP servers + wins - A list of WINS servers + lease - The lease time, in seconds + dhcptype - DHCP message type (safely ignored) + serverid - The IP of the server + message - Reason for a DHCPNAK + tftp - The TFTP server name + bootfile - The bootfile name + +additional options are easily added in options.c. + + +note on udhcpc's random seed +--------------------------- + +udhcpc will seed its random number generator (used for generating xid's) +by reading /dev/urandom. If you have a lot of embedded systems on the same +network, with no entropy, you can either seed /dev/urandom by a method of +your own, or doing the following on startup: + +ifconfig eth0 > /dev/urandom + +in order to seed /dev/urandom with some data (mac address) unique to your +system. If reading /dev/urandom fails, udhcpc will fall back to its old +behavior of seeding with time(0). + + +signals accepted by udhcpc +------------------------- + +udhcpc also responds to SIGUSR1 and SIGUSR2. SIGUSR1 will force a renew state, +and SIGUSR2 will force a release of the current lease, and cause udhcpc to +go into an inactive state (until it is killed, or receives a SIGUSR1). You do +not need to sleep between sending signals, as signals received are processed +sequencially in the order they are received. + + +compile time options +------------------- + +options.c contains a set of dhcp options for the client: + + name[10]: The name of the option as it will appear in scripts + + flags: The type of option, as well as if it will be requested + by the client (OPTION_REQ) + + code: The DHCP code for this option + diff --git a/platform/mcu/xr871/src/net/udhcp/README.udhcpd b/platform/mcu/xr871/src/net/udhcp/README.udhcpd new file mode 100644 index 0000000000..bc6137de37 --- /dev/null +++ b/platform/mcu/xr871/src/net/udhcp/README.udhcpd @@ -0,0 +1,59 @@ +udhcp server (udhcpd) +-------------------- + +The only command line argument to udhcpd is an optional specifed +config file. If no config file is specified, udhcpd uses the default +config file, /etc/udhcpd.conf. Ex: + +udhcpd /etc/udhcpd.eth1.conf + +The udhcp server employs a number of simple config files: + +udhcpd.leases +------------ + +The udhcpd.leases behavior is designed for an embedded system. The +file is written either every auto_time seconds, or when a SIGUSR1 +is received (the auto_time timer restarts if a SIGUSR1 is received). +If you send a SIGTERM to udhcpd directly after a SIGUSR1, udhcpd will +finish writing the leases file and wait for the aftermentioned script +to be executed and finish before quiting, so you do not need to sleep +between sending signals. When the file is written, a script can be +optionally called to commit the file to flash. Lease times are stored +in the file by time remaining in lease (for systems without clock +that works when there is no power), or by the absolute time that it +expires in seconds from epoch. In the remaining format, expired leases +are stored as zero. The file is of the format: + +16 byte MAC +4 byte ip address +u32 expire time +16 byte MAC +4 byte ip address +u32 expire time +. +etc. + +example: hexdump udhcpd.leases + +0000000 1000 c95a 27d9 0000 0000 0000 0000 0000 +0000010 a8c0 150a 0d00 2d29 5000 23fc 8566 0000 +0000020 0000 0000 0000 0000 a8c0 140a 0d00 4e29 +0000030 + + +udhcpd.conf +---------- + +The format is fairly simple, there is a sample file with all the +available options and comments describing them in samples/udhcpd.conf + +compile time options +------------------- + +dhcpd.h contains the other two compile time options: + + LEASE_TIME: The default lease time if not specified in the config + file. + + DHCPD_CONFIG_FILE: The defualt config file to use. diff --git a/platform/mcu/xr871/src/net/udhcp/TODO b/platform/mcu/xr871/src/net/udhcp/TODO new file mode 100644 index 0000000000..f88694a866 --- /dev/null +++ b/platform/mcu/xr871/src/net/udhcp/TODO @@ -0,0 +1,14 @@ +TODO +---- ++ Integrade README.*'s with manpages ++ using time(0) breaks if the system clock changes, find a portable solution ++ make failure of reading functions revert to previous value, not the default ++ sanity code for option[OPT_LEN] ++ fix aliasing (ie: eth0:0) ++ better standard linux distro support ++ make sure packet generation works on a wide varitey of arches ++ Interoperability testing ++ Hooks within the DHCP server ++ Additional bootp support in client/server ++ Make serverid option in server configurable ++ Possibly add failure message to DHCP NAK diff --git a/platform/mcu/xr871/src/net/udhcp/arpping.c b/platform/mcu/xr871/src/net/udhcp/arpping.c new file mode 100644 index 0000000000..2fad26c9ba --- /dev/null +++ b/platform/mcu/xr871/src/net/udhcp/arpping.c @@ -0,0 +1,200 @@ +/* + * arpping.c + * + * Mostly stolen from: dhcpcd - DHCP client daemon + * by Yoichi Hariguchi + */ +#ifdef DHCPD_LWIP +#include +#include +#include +#else +#include +#include +#include +#include +#include +#endif + +#ifndef DHCPD_TIMEALT +#include +#else +#include "dhcp_time.h" +#endif + +#include +#include +#include +//#include + +#include "dhcpd.h" +#include "debug.h" +#include "arpping.h" + +/* args: yiaddr - what IP to ping + * ip - our ip + * mac - our arp address + * interface - interface to use + * retn: 1 addr free + * 0 addr used + * -1 error + */ + +#ifdef DHCPD_LWIP +#define ETH_P_ARP ETHTYPE_ARP +#define ARPHRD_ETHER 1 +#define ETH_P_IP ETHTYPE_IP + +#define ARPOP_REQUEST ARP_REQUEST +#endif + +/* FIXME: match response against chaddr */ +int arpping(u_int32_t yiaddr, u_int32_t ip, unsigned char *mac, char *interface) +{ + int timeout = 2; + int optval = 1; + int s; /* socket */ + int rv = 1; /* return value */ + //struct sockaddr addr; /* for interface name */ +#ifdef DHCPD_ICMPPING + char icmp_buf[50]; + struct icmp_echo_hdr *icmp_hdr = (struct icmp_echo_hdr *)icmp_buf; +#else + struct arpMsg arp; +#endif + fd_set fdset; + struct timeval tm; + time_t prevTime; +#ifdef DHCPD_ICMPPING + struct sockaddr_in sl; +#else + struct sockaddr_ll sl; +#endif +#ifdef DHCPD_ICMPPING + if ((s = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)) == -1) { +#else + if ((s = socket (AF_PACKET, SOCK_RAW, htons(ETH_P_ARP))) == -1) { +#endif + DHCPD_LOG(LOG_ERR, "arp:Could not open raw socket"); + return -1; + } + + if (setsockopt(s, SOL_SOCKET, SO_BROADCAST, &optval, sizeof(optval)) == -1) { + DHCPD_LOG(LOG_ERR, "arp:Could not setsocketopt on raw socket"); +#ifdef DHCPD_LWIP + closesocket(s); +#else + close(s); +#endif + return -1; + } + DHCPD_LOG(LOG_INFO, "arp : check ip %s\n", inet_ntoa(yiaddr)); + /* send arp request */ +#ifdef DHCPD_ICMPPING + memset(icmp_buf, 0, sizeof(icmp_buf)); +#else + memset(&arp, 0, sizeof(arp)); +#endif +#ifndef DHCPD_ICMPPING +#ifdef DHCPD_LWIP + memcpy(arp.ethhdr.dest.addr, MAC_BCAST_ADDR, 6); /* MAC DA */ + memcpy(arp.ethhdr.src.addr, mac, 6); /* MAC SA */ + arp.ethhdr.type = htons(ETH_P_ARP); /* protocol type (Ethernet) */ +#else + memcpy(arp.ethhdr.h_dest, MAC_BCAST_ADDR, 6); /* MAC DA */ + memcpy(arp.ethhdr.h_source, mac, 6); /* MAC SA */ + arp.ethhdr.h_proto = htons(ETH_P_ARP); /* protocol type (Ethernet) */ +#endif +#endif +#ifdef DHCPD_ICMPPING + ICMPH_TYPE_SET(icmp_hdr, ICMP_ECHO); + ICMPH_CODE_SET(icmp_hdr, 0); + icmp_hdr->chksum = 0; + icmp_hdr->id = 0; + icmp_hdr->seqno = 0; + icmp_hdr->chksum = inet_chksum(icmp_hdr, sizeof(struct icmp_echo_hdr)); +#else + arp.htype = htons(ARPHRD_ETHER); /* hardware type */ + arp.ptype = htons(ETH_P_IP); /* protocol type (ARP message) */ + arp.hlen = 6; /* hardware address length */ + arp.plen = 4; /* protocol address length */ + arp.operation = htons(ARPOP_REQUEST); /* ARP op code */ +#endif +#ifndef DHCPD_ICMPPING +#ifndef DHCPD_LWIP + *((u_int *) arp.sInaddr) = ip; /* source IP address */ +#else + memcpy(arp.sInaddr,(char *)&ip, 4); +#endif + memcpy(arp.sHaddr, mac, 6); /* source hardware address */ +#ifndef DHCPD_LWIP + *((u_int *) arp.tInaddr) = yiaddr; /* target IP address */ +#else + memcpy(arp.tInaddr,(char *)&yiaddr, 4); +#endif +#endif + //memset(&addr, 0, sizeof(addr)); + //strcpy(addr.sa_data, interface); + //if (sendto(s, &arp, sizeof(arp), 0, &addr, sizeof(addr)) < 0) + memset(&sl, 0, sizeof(sl)); +#ifdef DHCPD_ICMPPING + sl.sin_len = sizeof(sl); + sl.sin_family = AF_INET; + sl.sin_addr.s_addr = yiaddr; +#else + sl.sll_family = AF_PACKET; + //sl.sll_addr = MAC_SOURCE; + //sl.sll_halen = ETH_ALEN; + sl.sll_ifindex = 0x2; +#endif +#ifdef DHCPD_ICMPPING + if (sendto(s, icmp_buf, sizeof(icmp_buf), 0, (struct sockaddr*)&sl, sizeof(sl)) < 0) +#else + if (sendto(s, &arp, sizeof(arp), 0, (struct sockaddr*)&sl, sizeof(sl)) < 0) +#endif + rv = 0; + + /* wait arp reply, and check it */ + tm.tv_usec = 0; + time(&prevTime); + + while (timeout > 0) { + FD_ZERO(&fdset); + FD_SET(s, &fdset); + tm.tv_sec = timeout; + if (select(s + 1, &fdset, (fd_set *) NULL, (fd_set *) NULL, &tm) < 0) { + DEBUG(LOG_ERR, "Error on ARPING request: %s", strerror(errno)); + if (errno != EINTR) rv = 0; + } else if (FD_ISSET(s, &fdset)) { +#ifdef DHCPD_ICMPPING + if (recv(s, icmp_buf, sizeof(icmp_buf), 0) < 0 ) rv = 0; + struct ip_hdr *iphdr = (struct ip_hdr *)icmp_buf; + icmp_hdr = (struct icmp_echo_hdr *)(icmp_buf + IPH_HL(iphdr) * 4); +#else + if (recv(s, &arp, sizeof(arp), 0) < 0 ) rv = 0; + u_int *tsInaddr = (u_int *)arp.sInaddr; +#endif +#ifdef DHCPD_ICMPPING + if (iphdr->src.addr == yiaddr && icmp_hdr->type == ICMP_ER) { +#else + if (arp.operation == htons(ARP_REPLY) && + bcmp(arp.tHaddr, mac, 6) == 0 && + *(/*(u_int *) arp.sInaddr*/tsInaddr) == yiaddr) { +#endif + DEBUG(LOG_INFO, "Valid arp reply receved for this address"); + rv = 0; + break; + } + } + timeout -= time(NULL) - prevTime; + time(&prevTime); + + } +#ifdef DHCPD_LWIP + closesocket(s); +#else + close(s); +#endif + DEBUG(LOG_INFO, "%salid arp replies for this address", rv ? "No v" : "V"); + return rv; +} diff --git a/platform/mcu/xr871/src/net/udhcp/arpping.h b/platform/mcu/xr871/src/net/udhcp/arpping.h new file mode 100644 index 0000000000..5c5a6ec351 --- /dev/null +++ b/platform/mcu/xr871/src/net/udhcp/arpping.h @@ -0,0 +1,41 @@ +/* + * arpping .h + */ + +#ifndef ARPPING_H +#define ARPPING_H + +#ifdef DHCPD_LWIP +#include "netif/etharp.h" +#else +#include +#include +#include +#include +#endif + +#ifdef DHCPD_USRCFG +#include "dhcpd_cfg.h" +#endif +struct arpMsg { +#ifdef DHCPD_LWIP + struct eth_hdr ethhdr; /* Ethernet header */ +#else + struct ethhdr ethhdr; /* Ethernet header */ +#endif + u_short htype; /* hardware type (must be ARPHRD_ETHER) */ + u_short ptype; /* protocol type (must be ETH_P_IP) */ + u_char hlen; /* hardware address length (must be 6) */ + u_char plen; /* protocol address length (must be 4) */ + u_short operation; /* ARP opcode */ + u_char sHaddr[6]; /* sender's hardware address */ + u_char sInaddr[4]; /* sender's IP address */ + u_char tHaddr[6]; /* target's hardware address */ + u_char tInaddr[4]; /* target's IP address */ + u_char pad[18]; /* pad for min. Ethernet payload (60 bytes) */ +}; + +/* function prototypes */ +int arpping(u_int32_t yiaddr, u_int32_t ip, unsigned char *arp, char *interface); + +#endif diff --git a/platform/mcu/xr871/src/net/udhcp/clientpacket.c b/platform/mcu/xr871/src/net/udhcp/clientpacket.c new file mode 100644 index 0000000000..6c32f922ea --- /dev/null +++ b/platform/mcu/xr871/src/net/udhcp/clientpacket.c @@ -0,0 +1,253 @@ +/* clientpacket.c + * + * Packet generation and dispatching functions for the DHCP client. + * + * Russ Dill July 2001 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#if __GLIBC__ >=2 && __GLIBC_MINOR >= 1 +#include +#include +#elif __LINUX +#include +#include +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#include "dhcpd.h" +#include "packet.h" +#include "options.h" +#include "dhcpc.h" +#include "debug.h" + + +/* Create a random xid */ +unsigned long random_xid(void) +{ + static int initialized; + if (!initialized) { + int fd; + unsigned long seed; + + fd = open("/dev/urandom", 0); + if (fd < 0 || read(fd, &seed, sizeof(seed)) < 0) { + LOG(LOG_WARNING, "Could not load seed from /dev/urandom: %s", + strerror(errno)); + seed = time(0); + } + if (fd >= 0) close(fd); + srand(seed); + initialized++; + } + return rand(); +} + + +/* initialize a packet with the proper defaults */ +static void init_packet(struct dhcpMessage *packet, char type) +{ + struct vendor { + char vendor, length; + char str[sizeof("udhcp "VERSION)]; + } vendor_id = { DHCP_VENDOR, sizeof("udhcp "VERSION) - 1, "udhcp "VERSION}; + + init_header(packet, type); + memcpy(packet->chaddr, client_config.arp, 6); + add_option_string(packet->options, client_config.clientid); + if (client_config.hostname) add_option_string(packet->options, client_config.hostname); + add_option_string(packet->options, (unsigned char *) &vendor_id); +} + + +/* Add a paramater request list for stubborn DHCP servers. Pull the data + * from the struct in options.c. Don't do bounds checking here because it + * goes towards the head of the packet. */ +static void add_requests(struct dhcpMessage *packet) +{ + int end = end_option(packet->options); + int i, len = 0; + + packet->options[end + OPT_CODE] = DHCP_PARAM_REQ; + for (i = 0; options[i].code; i++) + if (options[i].flags & OPTION_REQ) + packet->options[end + OPT_DATA + len++] = options[i].code; + packet->options[end + OPT_LEN] = len; + packet->options[end + OPT_DATA + len] = DHCP_END; + +} + + +/* Broadcast a DHCP discover packet to the network, with an optionally requested IP */ +int send_discover(unsigned long xid, unsigned long requested) +{ + struct dhcpMessage packet; + + init_packet(&packet, DHCPDISCOVER); + packet.xid = xid; + if (requested) + add_simple_option(packet.options, DHCP_REQUESTED_IP, requested); + + add_requests(&packet); + LOG(LOG_DEBUG, "Sending discover..."); + return raw_packet(&packet, INADDR_ANY, CLIENT_PORT, INADDR_BROADCAST, + SERVER_PORT, MAC_BCAST_ADDR, client_config.ifindex); +} + + +/* Broadcasts a DHCP request message */ +int send_selecting(unsigned long xid, unsigned long server, unsigned long requested) +{ + struct dhcpMessage packet; + struct in_addr addr; + + init_packet(&packet, DHCPREQUEST); + packet.xid = xid; + + add_simple_option(packet.options, DHCP_REQUESTED_IP, requested); + add_simple_option(packet.options, DHCP_SERVER_ID, server); + + add_requests(&packet); + addr.s_addr = requested; + LOG(LOG_DEBUG, "Sending select for %s...", inet_ntoa(addr)); + return raw_packet(&packet, INADDR_ANY, CLIENT_PORT, INADDR_BROADCAST, + SERVER_PORT, MAC_BCAST_ADDR, client_config.ifindex); +} + + +/* Unicasts or broadcasts a DHCP renew message */ +int send_renew(unsigned long xid, unsigned long server, unsigned long ciaddr) +{ + struct dhcpMessage packet; + int ret = 0; + + init_packet(&packet, DHCPREQUEST); + packet.xid = xid; + packet.ciaddr = ciaddr; + + add_requests(&packet); + LOG(LOG_DEBUG, "Sending renew..."); + if (server) + ret = kernel_packet(&packet, ciaddr, CLIENT_PORT, server, SERVER_PORT); + else ret = raw_packet(&packet, INADDR_ANY, CLIENT_PORT, INADDR_BROADCAST, + SERVER_PORT, MAC_BCAST_ADDR, client_config.ifindex); + return ret; +} + + +/* Unicasts a DHCP release message */ +int send_release(unsigned long server, unsigned long ciaddr) +{ + struct dhcpMessage packet; + + init_packet(&packet, DHCPRELEASE); + packet.xid = random_xid(); + packet.ciaddr = ciaddr; + + add_simple_option(packet.options, DHCP_REQUESTED_IP, ciaddr); + add_simple_option(packet.options, DHCP_SERVER_ID, server); + + LOG(LOG_DEBUG, "Sending release..."); + return kernel_packet(&packet, ciaddr, CLIENT_PORT, server, SERVER_PORT); +} + + +/* return -1 on errors that are fatal for the socket, -2 for those that aren't */ +int get_raw_packet(struct dhcpMessage *payload, int fd) +{ + int bytes; + struct udp_dhcp_packet packet; + u_int32_t source, dest; + u_int16_t check; + + memset(&packet, 0, sizeof(struct udp_dhcp_packet)); + bytes = read(fd, &packet, sizeof(struct udp_dhcp_packet)); + if (bytes < 0) { + DEBUG(LOG_INFO, "couldn't read on raw listening socket -- ignoring"); + usleep(500000); /* possible down interface, looping condition */ + return -1; + } + + if (bytes < (int) (sizeof(struct iphdr) + sizeof(struct udphdr))) { + DEBUG(LOG_INFO, "message too short, ignoring"); + return -2; + } + + if (bytes < ntohs(packet.ip.tot_len)) { + DEBUG(LOG_INFO, "Truncated packet"); + return -2; + } + + /* ignore any extra garbage bytes */ + bytes = ntohs(packet.ip.tot_len); + + /* Make sure its the right packet for us, and that it passes sanity checks */ + if (packet.ip.protocol != IPPROTO_UDP || packet.ip.version != IPVERSION || + packet.ip.ihl != sizeof(packet.ip) >> 2 || packet.udp.dest != htons(CLIENT_PORT) || + bytes > (int) sizeof(struct udp_dhcp_packet) || + ntohs(packet.udp.len) != (short) (bytes - sizeof(packet.ip))) { + DEBUG(LOG_INFO, "unrelated/bogus packet"); + return -2; + } + + /* check IP checksum */ + check = packet.ip.check; + packet.ip.check = 0; + if (check != checksum(&(packet.ip), sizeof(packet.ip))) { + DEBUG(LOG_INFO, "bad IP header checksum, ignoring"); + return -1; + } + + /* verify the UDP checksum by replacing the header with a psuedo header */ + source = packet.ip.saddr; + dest = packet.ip.daddr; + check = packet.udp.check; + packet.udp.check = 0; + memset(&packet.ip, 0, sizeof(packet.ip)); + + packet.ip.protocol = IPPROTO_UDP; + packet.ip.saddr = source; + packet.ip.daddr = dest; + packet.ip.tot_len = packet.udp.len; /* cheat on the psuedo-header */ + if (check && check != checksum(&packet, bytes)) { + DEBUG(LOG_ERR, "packet with bad UDP checksum received, ignoring"); + return -2; + } + + memcpy(payload, &(packet.data), bytes - (sizeof(packet.ip) + sizeof(packet.udp))); + + if (ntohl(payload->cookie) != DHCP_MAGIC) { + LOG(LOG_ERR, "received bogus message (bad magic) -- ignoring"); + return -2; + } + DEBUG(LOG_INFO, "oooooh!!! got some!"); + return bytes - (sizeof(packet.ip) + sizeof(packet.udp)); + +} + diff --git a/platform/mcu/xr871/src/net/udhcp/clientpacket.h b/platform/mcu/xr871/src/net/udhcp/clientpacket.h new file mode 100644 index 0000000000..2a6facbc84 --- /dev/null +++ b/platform/mcu/xr871/src/net/udhcp/clientpacket.h @@ -0,0 +1,12 @@ +#ifndef _CLIENTPACKET_H +#define _CLIENTPACKET_H + +unsigned long random_xid(void); +int send_discover(unsigned long xid, unsigned long requested); +int send_selecting(unsigned long xid, unsigned long server, unsigned long requested); +int send_renew(unsigned long xid, unsigned long server, unsigned long ciaddr); +int send_renew(unsigned long xid, unsigned long server, unsigned long ciaddr); +int send_release(unsigned long server, unsigned long ciaddr); +int get_raw_packet(struct dhcpMessage *payload, int fd); + +#endif diff --git a/platform/mcu/xr871/src/net/udhcp/debug.h b/platform/mcu/xr871/src/net/udhcp/debug.h new file mode 100644 index 0000000000..2650c772e3 --- /dev/null +++ b/platform/mcu/xr871/src/net/udhcp/debug.h @@ -0,0 +1,48 @@ +#ifndef _DEBUG_H +#define _DEBUG_H + +#include "libbb_udhcp.h" + +#include +#ifdef SYSLOG +#include +#endif + +//#define DEBUG +#define DHCPD_LOGD + +#ifdef SYSLOG +# define LOG(level, str, args...) do { printf(str, ## args); \ + printf("\n"); \ + syslog(level, str, ## args); } while(0) +# define OPEN_LOG(name) openlog(name, 0, 0) +#define CLOSE_LOG() closelog() +#else +# define LOG_EMERG "EMERGENCY!" +# define LOG_ALERT "ALERT!" +# define LOG_CRIT "critical!" +# define LOG_WARNING "warning" +# define LOG_ERR "error" +# define LOG_INFO "info" +# define LOG_DEBUG "debug" +#ifdef DHCPD_LOGD +# define DHCPD_LOG(level, str, args...) do { printf("%s, ", level); \ + printf(str, ## args); \ + printf("\n"); } while(0) +#else +# define DHCPD_LOG(level, str, args...) do {; } while(0) +#endif +# define OPEN_LOG(name) do {;} while(0) +#define CLOSE_LOG() do {;} while(0) +#endif + + +#ifdef DEBUG +# undef DEBUG +# define DEBUG(level, str, args...) DHCPD_LOG(level, str, ## args) +# define DEBUGGING +#else +# define DEBUG(level, str, args...) do {;} while(0) +#endif + +#endif diff --git a/platform/mcu/xr871/src/net/udhcp/dhcp_time.c b/platform/mcu/xr871/src/net/udhcp/dhcp_time.c new file mode 100644 index 0000000000..a6ec7483d1 --- /dev/null +++ b/platform/mcu/xr871/src/net/udhcp/dhcp_time.c @@ -0,0 +1,13 @@ +#ifdef DHCPD_TIMEALT +#include "kernel/os/os_time.h" +#include "dhcp_time.h" + +time_t dhcp_time(time_t *timer) +{ + if (!timer) + return (time_t)OS_GetTime(); + *timer = (time_t)OS_GetTime(); + return *timer; +} + +#endif \ No newline at end of file diff --git a/platform/mcu/xr871/src/net/udhcp/dhcp_time.h b/platform/mcu/xr871/src/net/udhcp/dhcp_time.h new file mode 100644 index 0000000000..155a5cceb3 --- /dev/null +++ b/platform/mcu/xr871/src/net/udhcp/dhcp_time.h @@ -0,0 +1,12 @@ +#ifndef DHCP_TMER_H_H +#define DHCP_TMER_H_H + +#ifdef DHCPD_TIMEALT +#include +time_t dhcp_time(time_t *timer); + +#define time dhcp_time + +#endif + +#endif diff --git a/platform/mcu/xr871/src/net/udhcp/dhcpc.c b/platform/mcu/xr871/src/net/udhcp/dhcpc.c new file mode 100644 index 0000000000..7b13f988e1 --- /dev/null +++ b/platform/mcu/xr871/src/net/udhcp/dhcpc.c @@ -0,0 +1,560 @@ +/* dhcpc.c + * + * udhcp DHCP client + * + * Russ Dill July 2001 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dhcpd.h" +#include "dhcpc.h" +#include "options.h" +#include "clientpacket.h" +#include "packet.h" +#include "script.h" +#include "socket.h" +#include "debug.h" +#include "pidfile.h" + +static int state; +static unsigned long requested_ip; /* = 0 */ +static unsigned long server_addr; +static unsigned long timeout; +static int packet_num; /* = 0 */ +static int fd; +static int signal_pipe[2]; + +#define LISTEN_NONE 0 +#define LISTEN_KERNEL 1 +#define LISTEN_RAW 2 +static int listen_mode; + +#define DEFAULT_SCRIPT "/usr/share/udhcpc/default.script" + +struct client_config_t client_config = { + /* Default options. */ + abort_if_no_lease: 0, + foreground: 0, + quit_after_lease: 0, + background_if_no_lease: 0, + interface: "eth0", + pidfile: NULL, + script: DEFAULT_SCRIPT, + clientid: NULL, + hostname: NULL, + ifindex: 0, + arp: "\0\0\0\0\0\0", /* appease gcc-3.0 */ +}; + +#ifndef BB_VER +static void show_usage(void) +{ + printf( +"Usage: udhcpc [OPTIONS]\n\n" +" -c, --clientid=CLIENTID Client identifier\n" +" -H, --hostname=HOSTNAME Client hostname\n" +" -h Alias for -H\n" +" -f, --foreground Do not fork after getting lease\n" +" -b, --background Fork to background if lease cannot be\n" +" immediately negotiated.\n" +" -i, --interface=INTERFACE Interface to use (default: eth0)\n" +" -n, --now Exit with failure if lease cannot be\n" +" immediately negotiated.\n" +" -p, --pidfile=file Store process ID of daemon in file\n" +" -q, --quit Quit after obtaining lease\n" +" -r, --request=IP IP address to request (default: none)\n" +" -s, --script=file Run file at dhcp events (default:\n" +" " DEFAULT_SCRIPT ")\n" +" -v, --version Display version\n" + ); + exit(0); +} +#endif + + +/* just a little helper */ +static void change_mode(int new_mode) +{ + DEBUG(LOG_INFO, "entering %s listen mode", + new_mode ? (new_mode == 1 ? "kernel" : "raw") : "none"); + close(fd); + fd = -1; + listen_mode = new_mode; +} + + +/* perform a renew */ +static void perform_renew(void) +{ + LOG(LOG_INFO, "Performing a DHCP renew"); + switch (state) { + case BOUND: + change_mode(LISTEN_KERNEL); + case RENEWING: + case REBINDING: + state = RENEW_REQUESTED; + break; + case RENEW_REQUESTED: /* impatient are we? fine, square 1 */ + run_script(NULL, "deconfig"); + case REQUESTING: + case RELEASED: + change_mode(LISTEN_RAW); + state = INIT_SELECTING; + break; + case INIT_SELECTING: + } + + /* start things over */ + packet_num = 0; + + /* Kill any timeouts because the user wants this to hurry along */ + timeout = 0; +} + + +/* perform a release */ +static void perform_release(void) +{ + char buffer[16]; + struct in_addr temp_addr; + + /* send release packet */ + if (state == BOUND || state == RENEWING || state == REBINDING) { + temp_addr.s_addr = server_addr; + sprintf(buffer, "%s", inet_ntoa(temp_addr)); + temp_addr.s_addr = requested_ip; + LOG(LOG_INFO, "Unicasting a release of %s to %s", + inet_ntoa(temp_addr), buffer); + send_release(server_addr, requested_ip); /* unicast */ + run_script(NULL, "deconfig"); + } + LOG(LOG_INFO, "Entering released state"); + + change_mode(LISTEN_NONE); + state = RELEASED; + timeout = 0x7fffffff; +} + + +/* Exit and cleanup */ +static void exit_client(int retval) +{ + pidfile_delete(client_config.pidfile); + CLOSE_LOG(); + exit(retval); +} + + +/* Signal handler */ +static void signal_handler(int sig) +{ + if (send(signal_pipe[1], &sig, sizeof(sig), MSG_DONTWAIT) < 0) { + LOG(LOG_ERR, "Could not send signal: %s", + strerror(errno)); + } +} + + +static void background(void) +{ + int pid_fd; + + pid_fd = pidfile_acquire(client_config.pidfile); /* hold lock during fork. */ + while (pid_fd >= 0 && pid_fd < 3) pid_fd = dup(pid_fd); /* don't let daemon close it */ + if (daemon(0, 0) == -1) { + perror("fork"); + exit_client(1); + } + client_config.foreground = 1; /* Do not fork again. */ + pidfile_write_release(pid_fd); +} + + +#ifdef COMBINED_BINARY +int udhcpc_main(int argc, char *argv[]) +#else +int main(int argc, char *argv[]) +#endif +{ + unsigned char *temp, *message; + unsigned long t1 = 0, t2 = 0, xid = 0; + unsigned long start = 0, lease; + fd_set rfds; + int retval; + struct timeval tv; + int c, len; + struct dhcpMessage packet; + struct in_addr temp_addr; + int pid_fd; + time_t now; + int max_fd; + int sig; + + static struct option arg_options[] = { + {"clientid", required_argument, 0, 'c'}, + {"foreground", no_argument, 0, 'f'}, + {"background", no_argument, 0, 'b'}, + {"hostname", required_argument, 0, 'H'}, + {"hostname", required_argument, 0, 'h'}, + {"interface", required_argument, 0, 'i'}, + {"now", no_argument, 0, 'n'}, + {"pidfile", required_argument, 0, 'p'}, + {"quit", no_argument, 0, 'q'}, + {"request", required_argument, 0, 'r'}, + {"script", required_argument, 0, 's'}, + {"version", no_argument, 0, 'v'}, + {"help", no_argument, 0, '?'}, + {0, 0, 0, 0} + }; + + /* get options */ + while (1) { + int option_index = 0; + c = getopt_long(argc, argv, "c:fbH:h:i:np:qr:s:v", arg_options, &option_index); + if (c == -1) break; + + switch (c) { + case 'c': + len = strlen(optarg) > 255 ? 255 : strlen(optarg); + if (client_config.clientid) free(client_config.clientid); + client_config.clientid = xmalloc(len + 2); + client_config.clientid[OPT_CODE] = DHCP_CLIENT_ID; + client_config.clientid[OPT_LEN] = len; + client_config.clientid[OPT_DATA] = '\0'; + strncpy(client_config.clientid + OPT_DATA, optarg, len); + break; + case 'f': + client_config.foreground = 1; + break; + case 'b': + client_config.background_if_no_lease = 1; + break; + case 'h': + case 'H': + len = strlen(optarg) > 255 ? 255 : strlen(optarg); + if (client_config.hostname) free(client_config.hostname); + client_config.hostname = xmalloc(len + 2); + client_config.hostname[OPT_CODE] = DHCP_HOST_NAME; + client_config.hostname[OPT_LEN] = len; + strncpy(client_config.hostname + 2, optarg, len); + break; + case 'i': + client_config.interface = optarg; + break; + case 'n': + client_config.abort_if_no_lease = 1; + break; + case 'p': + client_config.pidfile = optarg; + break; + case 'q': + client_config.quit_after_lease = 1; + break; + case 'r': + requested_ip = inet_addr(optarg); + break; + case 's': + client_config.script = optarg; + break; + case 'v': + printf("udhcpcd, version %s\n\n", VERSION); + exit_client(0); + break; + default: + show_usage(); + } + } + + OPEN_LOG("udhcpc"); + LOG(LOG_INFO, "udhcp client (v%s) started", VERSION); + + pid_fd = pidfile_acquire(client_config.pidfile); + pidfile_write_release(pid_fd); + + if (read_interface(client_config.interface, &client_config.ifindex, + NULL, client_config.arp) < 0) + exit_client(1); + + if (!client_config.clientid) { + client_config.clientid = xmalloc(6 + 3); + client_config.clientid[OPT_CODE] = DHCP_CLIENT_ID; + client_config.clientid[OPT_LEN] = 7; + client_config.clientid[OPT_DATA] = 1; + memcpy(client_config.clientid + 3, client_config.arp, 6); + } + + /* setup signal handlers */ + socketpair(AF_UNIX, SOCK_STREAM, 0, signal_pipe); + signal(SIGUSR1, signal_handler); + signal(SIGUSR2, signal_handler); + signal(SIGTERM, signal_handler); + + state = INIT_SELECTING; + run_script(NULL, "deconfig"); + change_mode(LISTEN_RAW); + + for (;;) { + + tv.tv_sec = timeout - time(0); + tv.tv_usec = 0; + FD_ZERO(&rfds); + + if (listen_mode != LISTEN_NONE && fd < 0) { + if (listen_mode == LISTEN_KERNEL) + fd = listen_socket(INADDR_ANY, CLIENT_PORT, client_config.interface); + else + fd = raw_socket(client_config.ifindex); + if (fd < 0) { + LOG(LOG_ERR, "FATAL: couldn't listen on socket, %s", strerror(errno)); + exit_client(0); + } + } + if (fd >= 0) FD_SET(fd, &rfds); + FD_SET(signal_pipe[0], &rfds); + + if (tv.tv_sec > 0) { + DEBUG(LOG_INFO, "Waiting on select...\n"); + max_fd = signal_pipe[0] > fd ? signal_pipe[0] : fd; + retval = select(max_fd + 1, &rfds, NULL, NULL, &tv); + } else retval = 0; /* If we already timed out, fall through */ + + now = time(0); + if (retval == 0) { + /* timeout dropped to zero */ + switch (state) { + case INIT_SELECTING: + if (packet_num < 3) { + if (packet_num == 0) + xid = random_xid(); + + /* send discover packet */ + send_discover(xid, requested_ip); /* broadcast */ + + timeout = now + ((packet_num == 2) ? 4 : 2); + packet_num++; + } else { + if (client_config.background_if_no_lease) { + LOG(LOG_INFO, "No lease, forking to background."); + background(); + } else if (client_config.abort_if_no_lease) { + LOG(LOG_INFO, "No lease, failing."); + exit_client(1); + } + /* wait to try again */ + packet_num = 0; + timeout = now + 60; + } + break; + case RENEW_REQUESTED: + case REQUESTING: + if (packet_num < 3) { + /* send request packet */ + if (state == RENEW_REQUESTED) + send_renew(xid, server_addr, requested_ip); /* unicast */ + else send_selecting(xid, server_addr, requested_ip); /* broadcast */ + + timeout = now + ((packet_num == 2) ? 10 : 2); + packet_num++; + } else { + /* timed out, go back to init state */ + if (state == RENEW_REQUESTED) run_script(NULL, "deconfig"); + state = INIT_SELECTING; + timeout = now; + packet_num = 0; + change_mode(LISTEN_RAW); + } + break; + case BOUND: + /* Lease is starting to run out, time to enter renewing state */ + state = RENEWING; + change_mode(LISTEN_KERNEL); + DEBUG(LOG_INFO, "Entering renew state"); + /* fall right through */ + case RENEWING: + /* Either set a new T1, or enter REBINDING state */ + if ((t2 - t1) <= (lease / 14400 + 1)) { + /* timed out, enter rebinding state */ + state = REBINDING; + timeout = now + (t2 - t1); + DEBUG(LOG_INFO, "Entering rebinding state"); + } else { + /* send a request packet */ + send_renew(xid, server_addr, requested_ip); /* unicast */ + + t1 = (t2 - t1) / 2 + t1; + timeout = t1 + start; + } + break; + case REBINDING: + /* Either set a new T2, or enter INIT state */ + if ((lease - t2) <= (lease / 14400 + 1)) { + /* timed out, enter init state */ + state = INIT_SELECTING; + LOG(LOG_INFO, "Lease lost, entering init state"); + run_script(NULL, "deconfig"); + timeout = now; + packet_num = 0; + change_mode(LISTEN_RAW); + } else { + /* send a request packet */ + send_renew(xid, 0, requested_ip); /* broadcast */ + + t2 = (lease - t2) / 2 + t2; + timeout = t2 + start; + } + break; + case RELEASED: + /* yah, I know, *you* say it would never happen */ + timeout = 0x7fffffff; + break; + } + } else if (retval > 0 && listen_mode != LISTEN_NONE && FD_ISSET(fd, &rfds)) { + /* a packet is ready, read it */ + + if (listen_mode == LISTEN_KERNEL) + len = get_packet(&packet, fd); + else len = get_raw_packet(&packet, fd); + + if (len == -1 && errno != EINTR) { + DEBUG(LOG_INFO, "error on read, %s, reopening socket", strerror(errno)); + change_mode(listen_mode); /* just close and reopen */ + } + if (len < 0) continue; + + if (packet.xid != xid) { + DEBUG(LOG_INFO, "Ignoring XID %lx (our xid is %lx)", + (unsigned long) packet.xid, xid); + continue; + } + + if ((message = get_option(&packet, DHCP_MESSAGE_TYPE)) == NULL) { + DEBUG(LOG_ERR, "couldnt get option from packet -- ignoring"); + continue; + } + + switch (state) { + case INIT_SELECTING: + /* Must be a DHCPOFFER to one of our xid's */ + if (*message == DHCPOFFER) { + if ((temp = get_option(&packet, DHCP_SERVER_ID))) { + memcpy(&server_addr, temp, 4); + xid = packet.xid; + requested_ip = packet.yiaddr; + + /* enter requesting state */ + state = REQUESTING; + timeout = now; + packet_num = 0; + } else { + DEBUG(LOG_ERR, "No server ID in message"); + } + } + break; + case RENEW_REQUESTED: + case REQUESTING: + case RENEWING: + case REBINDING: + if (*message == DHCPACK) { + if (!(temp = get_option(&packet, DHCP_LEASE_TIME))) { + LOG(LOG_ERR, "No lease time with ACK, using 1 hour lease"); + lease = 60 * 60; + } else { + memcpy(&lease, temp, 4); + lease = ntohl(lease); + } + + /* enter bound state */ + t1 = lease / 2; + + /* little fixed point for n * .875 */ + t2 = (lease * 0x7) >> 3; + temp_addr.s_addr = packet.yiaddr; + LOG(LOG_INFO, "Lease of %s obtained, lease time %ld", + inet_ntoa(temp_addr), lease); + start = now; + timeout = t1 + start; + requested_ip = packet.yiaddr; + run_script(&packet, + ((state == RENEWING || state == REBINDING) ? "renew" : "bound")); + + state = BOUND; + change_mode(LISTEN_NONE); + if (client_config.quit_after_lease) + exit_client(0); + if (!client_config.foreground) + background(); + + } else if (*message == DHCPNAK) { + /* return to init state */ + LOG(LOG_INFO, "Received DHCP NAK"); + run_script(&packet, "nak"); + if (state != REQUESTING) + run_script(NULL, "deconfig"); + state = INIT_SELECTING; + timeout = now; + requested_ip = 0; + packet_num = 0; + change_mode(LISTEN_RAW); + sleep(3); /* avoid excessive network traffic */ + } + break; + /* case BOUND, RELEASED: - ignore all packets */ + } + } else if (retval > 0 && FD_ISSET(signal_pipe[0], &rfds)) { + if (read(signal_pipe[0], &sig, sizeof(signal)) < 0) { + DEBUG(LOG_ERR, "Could not read signal: %s", + strerror(errno)); + continue; /* probably just EINTR */ + } + switch (sig) { + case SIGUSR1: + perform_renew(); + break; + case SIGUSR2: + perform_release(); + break; + case SIGTERM: + LOG(LOG_INFO, "Received SIGTERM"); + exit_client(0); + } + } else if (retval == -1 && errno == EINTR) { + /* a signal was caught */ + } else { + /* An error occured */ + DEBUG(LOG_ERR, "Error on select"); + } + + } + return 0; +} + diff --git a/platform/mcu/xr871/src/net/udhcp/dhcpc.h b/platform/mcu/xr871/src/net/udhcp/dhcpc.h new file mode 100644 index 0000000000..52157f5c81 --- /dev/null +++ b/platform/mcu/xr871/src/net/udhcp/dhcpc.h @@ -0,0 +1,34 @@ +/* dhcpc.h */ +#ifndef _DHCPC_H +#define _DHCPC_H + +#include "libbb_udhcp.h" + +#define INIT_SELECTING 0 +#define REQUESTING 1 +#define BOUND 2 +#define RENEWING 3 +#define REBINDING 4 +#define INIT_REBOOT 5 +#define RENEW_REQUESTED 6 +#define RELEASED 7 + + +struct client_config_t { + char foreground; /* Do not fork */ + char quit_after_lease; /* Quit after obtaining lease */ + char abort_if_no_lease; /* Abort if no lease */ + char background_if_no_lease; /* Fork to background if no lease */ + char *interface; /* The name of the interface to use */ + char *pidfile; /* Optionally store the process ID */ + char *script; /* User script to run at dhcp events */ + unsigned char *clientid; /* Optional client id to use */ + unsigned char *hostname; /* Optional hostname to use */ + int ifindex; /* Index number of the interface to use */ + unsigned char arp[6]; /* Our arp address */ +}; + +extern struct client_config_t client_config; + + +#endif diff --git a/platform/mcu/xr871/src/net/udhcp/dhcpd.c b/platform/mcu/xr871/src/net/udhcp/dhcpd.c new file mode 100644 index 0000000000..a6968faede --- /dev/null +++ b/platform/mcu/xr871/src/net/udhcp/dhcpd.c @@ -0,0 +1,289 @@ +/* dhcpd.c + * + * udhcp Server + * Copyright (C) 1999 Matthew Ramsay + * Chris Trew + * + * Rewrite by Russ Dill July 2001 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "debug.h" +#include "dhcpd.h" +#include "arpping.h" +#include "socket.h" +#include "options.h" +#include "files.h" +#include "leases.h" +#include "packet.h" +#include "serverpacket.h" +#include "pidfile.h" + + +/* globals */ +struct dhcpOfferedAddr *leases; +struct server_config_t server_config; +static int signal_pipe[2]; + +/* Exit and cleanup */ +static void exit_server(int retval) +{ + pidfile_delete(server_config.pidfile); + CLOSE_LOG(); + exit(retval); +} + + +/* Signal handler */ +static void signal_handler(int sig) +{ + if (send(signal_pipe[1], &sig, sizeof(sig), MSG_DONTWAIT) < 0) { + LOG(LOG_ERR, "Could not send signal: %s", + strerror(errno)); + } +} + + +#ifdef COMBINED_BINARY +int udhcpd_main(int argc, char *argv[]) +#else +int main(int argc, char *argv[]) +#endif +{ + fd_set rfds; + struct timeval tv; + int server_socket = -1; + int bytes, retval; + struct dhcpMessage packet; + unsigned char *state; + unsigned char *server_id, *requested; + u_int32_t server_id_align, requested_align; + unsigned long timeout_end; + struct option_set *option; + struct dhcpOfferedAddr *lease; + int pid_fd; + int max_sock; + int sig; + + OPEN_LOG("udhcpd"); + LOG(LOG_INFO, "udhcp server (v%s) started", VERSION); + + memset(&server_config, 0, sizeof(struct server_config_t)); + + if (argc < 2) + read_config(DHCPD_CONF_FILE); + else read_config(argv[1]); + + pid_fd = pidfile_acquire(server_config.pidfile); + pidfile_write_release(pid_fd); + + if ((option = find_option(server_config.options, DHCP_LEASE_TIME))) { + memcpy(&server_config.lease, option->data + 2, 4); + server_config.lease = ntohl(server_config.lease); + } + else server_config.lease = LEASE_TIME; + + leases = malloc(sizeof(struct dhcpOfferedAddr) * server_config.max_leases); + memset(leases, 0, sizeof(struct dhcpOfferedAddr) * server_config.max_leases); + read_leases(server_config.lease_file); + + if (read_interface(server_config.interface, &server_config.ifindex, + &server_config.server, server_config.arp) < 0) + exit_server(1); + +#ifndef DEBUGGING + pid_fd = pidfile_acquire(server_config.pidfile); /* hold lock during fork. */ + if (daemon(0, 0) == -1) { + perror("fork"); + exit_server(1); + } + pidfile_write_release(pid_fd); +#endif + + + socketpair(AF_UNIX, SOCK_STREAM, 0, signal_pipe); + signal(SIGUSR1, signal_handler); + signal(SIGTERM, signal_handler); + + timeout_end = time(0) + server_config.auto_time; + while(1) { /* loop until universe collapses */ + + if (server_socket < 0) + if ((server_socket = listen_socket(INADDR_ANY, SERVER_PORT, server_config.interface)) < 0) { + LOG(LOG_ERR, "FATAL: couldn't create server socket, %s", strerror(errno)); + exit_server(0); + } + + FD_ZERO(&rfds); + FD_SET(server_socket, &rfds); + FD_SET(signal_pipe[0], &rfds); + if (server_config.auto_time) { + tv.tv_sec = timeout_end - time(0); + tv.tv_usec = 0; + } + if (!server_config.auto_time || tv.tv_sec > 0) { + max_sock = server_socket > signal_pipe[0] ? server_socket : signal_pipe[0]; + retval = select(max_sock + 1, &rfds, NULL, NULL, + server_config.auto_time ? &tv : NULL); + } else retval = 0; /* If we already timed out, fall through */ + + if (retval == 0) { + write_leases(); + timeout_end = time(0) + server_config.auto_time; + continue; + } else if (retval < 0 && errno != EINTR) { + DEBUG(LOG_INFO, "error on select"); + continue; + } + + if (FD_ISSET(signal_pipe[0], &rfds)) { + if (read(signal_pipe[0], &sig, sizeof(sig)) < 0) + continue; /* probably just EINTR */ + switch (sig) { + case SIGUSR1: + LOG(LOG_INFO, "Received a SIGUSR1"); + write_leases(); + /* why not just reset the timeout, eh */ + timeout_end = time(0) + server_config.auto_time; + continue; + case SIGTERM: + LOG(LOG_INFO, "Received a SIGTERM"); + exit_server(0); + } + } + + if ((bytes = get_packet(&packet, server_socket)) < 0) { /* this waits for a packet - idle */ + if (bytes == -1 && errno != EINTR) { + DEBUG(LOG_INFO, "error on read, %s, reopening socket", strerror(errno)); + close(server_socket); + server_socket = -1; + } + continue; + } + + if ((state = get_option(&packet, DHCP_MESSAGE_TYPE)) == NULL) { + DEBUG(LOG_ERR, "couldn't get option from packet, ignoring"); + continue; + } + + /* ADDME: look for a static lease */ + lease = find_lease_by_chaddr(packet.chaddr); + switch (state[0]) { + case DHCPDISCOVER: + DEBUG(LOG_INFO,"received DISCOVER"); + + if (sendOffer(&packet) < 0) { + LOG(LOG_ERR, "send OFFER failed"); + } + break; + case DHCPREQUEST: + DEBUG(LOG_INFO, "received REQUEST"); + + requested = get_option(&packet, DHCP_REQUESTED_IP); + server_id = get_option(&packet, DHCP_SERVER_ID); + + if (requested) memcpy(&requested_align, requested, 4); + if (server_id) memcpy(&server_id_align, server_id, 4); + + if (lease) { /*ADDME: or static lease */ + if (server_id) { + /* SELECTING State */ + DEBUG(LOG_INFO, "server_id = %08x", ntohl(server_id_align)); + if (server_id_align == server_config.server && requested && + requested_align == lease->yiaddr) { + sendACK(&packet, lease->yiaddr); + } + } else { + if (requested) { + /* INIT-REBOOT State */ + if (lease->yiaddr == requested_align) + sendACK(&packet, lease->yiaddr); + else sendNAK(&packet); + } else { + /* RENEWING or REBINDING State */ + if (lease->yiaddr == packet.ciaddr) + sendACK(&packet, lease->yiaddr); + else { + /* don't know what to do!!!! */ + sendNAK(&packet); + } + } + } + + /* what to do if we have no record of the client */ + } else if (server_id) { + /* SELECTING State */ + + } else if (requested) { + /* INIT-REBOOT State */ + if ((lease = find_lease_by_yiaddr(requested_align))) { + if (lease_expired(lease)) { + /* probably best if we drop this lease */ + memset(lease->chaddr, 0, 16); + /* make some contention for this address */ + } else sendNAK(&packet); + } else if (requested_align < server_config.start || + requested_align > server_config.end) { + sendNAK(&packet); + } /* else remain silent */ + + } else { + /* RENEWING or REBINDING State */ + } + break; + case DHCPDECLINE: + DEBUG(LOG_INFO,"received DECLINE"); + if (lease) { + memset(lease->chaddr, 0, 16); + lease->expires = time(0) + server_config.decline_time; + printf("%s,line:%d,lease->expires:%lu\n",__func__,__LINE__,lease->expires); + } + break; + case DHCPRELEASE: + DEBUG(LOG_INFO,"received RELEASE"); + if (lease) lease->expires = time(0); + printf("%s,line:%d,lease->expires:%lu\n",__func__,__LINE__,lease->expires); + break; + case DHCPINFORM: + DEBUG(LOG_INFO,"received INFORM"); + send_inform(&packet); + break; + default: + LOG(LOG_WARNING, "unsupported DHCP message (%02x) -- ignoring", state[0]); + } + } + + return 0; +} + diff --git a/platform/mcu/xr871/src/net/udhcp/dhcpd.h b/platform/mcu/xr871/src/net/udhcp/dhcpd.h new file mode 100644 index 0000000000..18f876d3c3 --- /dev/null +++ b/platform/mcu/xr871/src/net/udhcp/dhcpd.h @@ -0,0 +1,139 @@ +/* dhcpd.h */ +#ifndef _DHCPD_H +#define _DHCPD_H + +#ifndef DHCPD_LWIP +#include +#include +#include "libbb_udhcp.h" +#endif +#ifdef DHCPD_FREERTOS +#include "kernel/os/os_thread.h" +#endif +#include + +#include "leases.h" + + +/************************************/ +/* Defaults _you_ may want to tweak */ +/************************************/ + +/* the period of time the client is allowed to use that address */ +#define LEASE_TIME (60*60*24*10) /* 10 days of seconds */ + +#ifdef DHCPD_FS +/* where to find the DHCP server configuration file */ +#define DHCPD_CONF_FILE "/etc/udhcpd.conf" +#endif +/*****************************************************************/ +/* Do not modify below here unless you know what you are doing!! */ +/*****************************************************************/ + +/* DHCP protocol -- see RFC 2131 */ +#define SERVER_PORT 67 +#define CLIENT_PORT 68 + +#define DHCP_MAGIC 0x63825363 + +/* DHCP option codes (partial list) */ +#define DHCP_PADDING 0x00 +#define DHCP_SUBNET 0x01 +#define DHCP_TIME_OFFSET 0x02 +#define DHCP_ROUTER 0x03 +#define DHCP_TIME_SERVER 0x04 +#define DHCP_NAME_SERVER 0x05 +#define DHCP_DNS_SERVER 0x06 +#define DHCP_LOG_SERVER 0x07 +#define DHCP_COOKIE_SERVER 0x08 +#define DHCP_LPR_SERVER 0x09 +#define DHCP_HOST_NAME 0x0c +#define DHCP_BOOT_SIZE 0x0d +#define DHCP_DOMAIN_NAME 0x0f +#define DHCP_SWAP_SERVER 0x10 +#define DHCP_ROOT_PATH 0x11 +#define DHCP_IP_TTL 0x17 +#define DHCP_MTU 0x1a +#define DHCP_BROADCAST 0x1c +#define DHCP_NTP_SERVER 0x2a +#define DHCP_WINS_SERVER 0x2c +#define DHCP_REQUESTED_IP 0x32 +#define DHCP_LEASE_TIME 0x33 +#define DHCP_OPTION_OVER 0x34 +#define DHCP_MESSAGE_TYPE 0x35 +#define DHCP_SERVER_ID 0x36 +#define DHCP_PARAM_REQ 0x37 +#define DHCP_MESSAGE 0x38 +#define DHCP_MAX_SIZE 0x39 +#define DHCP_T1 0x3a +#define DHCP_T2 0x3b +#define DHCP_VENDOR 0x3c +#define DHCP_CLIENT_ID 0x3d + +#define DHCP_END 0xFF + + +#define BOOTREQUEST 1 +#define BOOTREPLY 2 + +#define ETH_10MB 1 +#define ETH_10MB_LEN 6 + +#define DHCPDISCOVER 1 +#define DHCPOFFER 2 +#define DHCPREQUEST 3 +#define DHCPDECLINE 4 +#define DHCPACK 5 +#define DHCPNAK 6 +#define DHCPRELEASE 7 +#define DHCPINFORM 8 + +#define BROADCAST_FLAG 0x8000 + +#define OPTION_FIELD 0 +#define FILE_FIELD 1 +#define SNAME_FIELD 2 + +/* miscellaneous defines */ +#define MAC_BCAST_ADDR (unsigned char *) "\xff\xff\xff\xff\xff\xff" +#define OPT_CODE 0 +#define OPT_LEN 1 +#define OPT_DATA 2 + +struct option_set { + unsigned char *data; + struct option_set *next; +}; + +struct server_config_t { + u_int32_t server; /* Our IP, in network order */ + u_int32_t start; /* Start address of leases, network order */ + u_int32_t end; /* End of leases, network order */ + struct option_set *options; /* List of DHCP options loaded from the config file */ + char *interface; /* The name of the interface to use */ + int ifindex; /* Index number of the interface to use */ + unsigned char arp[6]; /* Our arp address */ + unsigned long lease; /* lease time in seconds (host order) */ + unsigned long max_leases; /* maximum number of leases (including reserved address) */ + char remaining; /* should the lease file be interpreted as lease time remaining, or + * as the time the lease expires */ + unsigned long auto_time; /* how long should udhcpd wait before writing a config file. + * if this is zero, it will only write one on SIGUSR1 */ + unsigned long decline_time; /* how long an address is reserved if a client returns a + * decline message */ + unsigned long conflict_time; /* how long an arp conflict offender is leased for */ + unsigned long offer_time; /* how long an offered address is reserved */ + unsigned long min_lease; /* minimum lease a client can request*/ + char *lease_file; + char *pidfile; + char *notify_file; /* What to run whenever leases are written */ + u_int32_t siaddr; /* next server bootp option */ + char *sname; /* bootp server name */ + char *boot_file; /* bootp boot file option */ +}; + +extern struct server_config_t server_config; +extern struct dhcpOfferedAddr *leases; + + +#endif diff --git a/platform/mcu/xr871/src/net/udhcp/dhcpd_cfg.h b/platform/mcu/xr871/src/net/udhcp/dhcpd_cfg.h new file mode 100644 index 0000000000..a01703d95d --- /dev/null +++ b/platform/mcu/xr871/src/net/udhcp/dhcpd_cfg.h @@ -0,0 +1,27 @@ +#ifndef DHCPD_CFG_H_H +#define DHCPD_CFG_H_H + +#define DHCPD_ADDR_START "192.168.51.100" +#define DHCPD_ADDR_END "192.168.51.104" +#define DHCPD_INTERFACE "en1" +#define DHCPD_OPTION "" +#define DHCPD_OPT "" +#define DHCPD_REMAIN "yes" +#define DHCPD_MAX_LEASES "5" +#define DHCPD_AUTO_TIME "7200" +#define DHCPD_DECLINE_TIME "3600" +#define DHCPD_CONFLICT_TIME "3600" +#define DHCPD_OFFER_TIME "60" +#define DHCPD_MIN_LEASE "60" +#define DHCPD_SIADDR "0.0.0.0" +#define DHCPD_SNAME "IOT" + +#define u_int8_t uint8_t +#define u_int16_t uint16_t +#define u_int32_t uint32_t + +#define u_int uint32_t +#define u_char uint8_t +#define u_short uint16_t + +#endif /* DHCPD_CFG_H_H */ diff --git a/platform/mcu/xr871/src/net/udhcp/dns.h b/platform/mcu/xr871/src/net/udhcp/dns.h new file mode 100644 index 0000000000..a8cd5079ba --- /dev/null +++ b/platform/mcu/xr871/src/net/udhcp/dns.h @@ -0,0 +1,82 @@ +/*** + * dns-server.c + * Provided by: Paul Krzyzanowski + * Group: Aditya Geria, Monisha Jain, Jeevana Lagisetty + * Header file for dns-server.c + * Provides the header structs for a DNS query exchange + **/ + +#ifndef __DHCPD_DNS_H__ +#define __DHCPD_DNS_H__ + +#include +#include +#include "lwip/sockets.h" +#include +#include + +/* + DNS structures +*/ + +/* dns header - see rfc1035 */ +/* this is the main header of a DNS message */ +/* it is followed by zero or more questions, answers, authorities, and additional sections */ +/* the last four count fields tell you how many of each to expect */ + +typedef struct { + unsigned short id; + unsigned char rd:1; + unsigned char tc:1; + unsigned char aa:1; + unsigned char opcode:4; + unsigned char qr:1; + unsigned char rcode:4; + unsigned char cd:1; + unsigned char ad:1; + unsigned char z:1; + unsigned char ra:1; + unsigned short qd_count; + unsigned short an_count; + unsigned short ns_count; + unsigned short ar_count; +} dns_header; + +/* dns question section format. This is prepended with a name */ +/* check the specs for the format of a name. Instead of components */ +/* separated by dots, each component is prefixed with a byte containing */ +/* the length of that component */ + +typedef struct { + unsigned short qtype; + unsigned short qclass; +} dns_question; + +/* DNS resource record format */ +/* The answer, authority, and additional sections all share this format. */ +/* It is prepended with a name and suffixed with additional data */ + +typedef struct __attribute__ ((__packed__)) { + unsigned short type; + unsigned short class; + unsigned int ttl; + unsigned short data_len; + //unsigned int rdata; +} dns_rrhdr; + +#define DNS_BUF_SIZE 256 + +//#define DNS_SERVER_DBG + +#ifdef DNS_SERVER_DBG +#define DNS_DBG printf +#define DNS_ERR(x...) do {printf("[DNS ERR]"); printf(x); } while (0) +#else +#define DNS_DBG(x...) NULL +#define DNS_ERR(x...) NULL +#endif /* DEBUG */ + +int dns_listen_socket(void); +int dns_server(int fd, char *recvbuf, int buflen); + +#endif \ No newline at end of file diff --git a/platform/mcu/xr871/src/net/udhcp/dns_server.c b/platform/mcu/xr871/src/net/udhcp/dns_server.c new file mode 100644 index 0000000000..28b5f0f5fa --- /dev/null +++ b/platform/mcu/xr871/src/net/udhcp/dns_server.c @@ -0,0 +1,120 @@ +/*** + * dns-server.c + * Authors: Aditya Geria, Monisha Jain, Jeevana Lagisetty + * Version 1.0.4 (April 1, 2016) + * Fixed debug information, cleaned up messages shown + * Host a simple DNS server which will resolve hosts from a given + * hosts.txt file, and provide an IP address. + **/ + +#include "dns.h" +#include "lwip/netdb.h" +#include +#include + +int process_request (int fd, void* recvbuf, int recvlen, struct sockaddr *to) +{ + int ret; + dns_header* header = (dns_header*) recvbuf; + socklen_t addrlen = sizeof(*to); + + if(header->qr == 0) { + DNS_DBG("Correct query field - identified question\n"); + DNS_DBG("Query count: %d\n", htons(header->qd_count)); + + //char* query = (char*)recvbuf + sizeof(dns_header); //gets the query name + //DNS_DBG("Query length: %d\n", strlen(query)); + + header->qr = 1; + header->aa = 0; + header->tc = 0; + header->ra = 1; + header->rcode = 5; //refuse; + //header->qd_count = htons(1); + //header->an_count = 0; + //header->ns_count = 0; + //header->ar_count = 0; + + //no need to add answer record, or query + if((ret = sendto(fd, recvbuf, recvlen, 0, to, addrlen)) < 0) { + DNS_ERR("Failed to send, line %d\n", __LINE__); + return -1; + } + return ret; + }else { + return -1; + } +} + +/** + * @Method dump + * @param buf - thing to view/dump + * @param len - length of buf + * Shows contents of (buf) in bitwise manner + */ +void dump (char* buf, int len) { + int i = 0; + + for(i=0; i < len; ++i) + DNS_DBG("%02x ", buf[i]); // dump hex values + DNS_DBG("\n"); + + for(i=0; i < len; ++i) { + if((u8_t)buf[i] >= 0x20 && (u8_t)buf[i] <= 0x7f) + DNS_DBG("'%c' ", buf[i]); // dump a character if it’s printable + else + DNS_DBG("0x%02x ", buf[i]); // else dump a hex value + } + DNS_DBG("\n"); + return; +} + +int dns_listen_socket(void) { + struct sockaddr_in myaddr; // our address + int fd = -1; // our socket + int port = 53; + int n = 1; + + DNS_DBG("Opening listen socket on port %d for dns server\n", port); + /* create a UDP socket */ + if ((fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) { + DNS_ERR("cannot create socket\n"); + return -1; + } + + if(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *) &n, sizeof(n)) < 0) { + closesocket(fd); + return -1; + } + + /* bind the socket to any valid IP address and a specific port */ + memset((char *)&myaddr, 0, sizeof(myaddr)); + myaddr.sin_family = AF_INET; + myaddr.sin_addr.s_addr = htonl(INADDR_ANY); + myaddr.sin_port = htons(port); + + if (bind(fd, (struct sockaddr *)&myaddr, sizeof(myaddr)) < 0) { + closesocket(fd); + return -1; + } + return fd; +} + +int dns_server(int fd, char *recvbuf, int buflen) +{ + struct sockaddr_in remaddr; // remote address + socklen_t addrlen = sizeof(remaddr); // length of addresses + int ret; //bytes received + + ret = recvfrom(fd, recvbuf, buflen, 0, (struct sockaddr *)&remaddr, &addrlen); + if (ret < 0 || ret < sizeof(dns_header)) { + DNS_ERR("dns packet recv err\n"); + return -1; + } + DNS_DBG("dns server received %d bytes\n", ret); + //dump(recvbuf, ret); //debugging dump + ret = process_request(fd, recvbuf, ret, (struct sockaddr *)&remaddr); + return ret; +} + + diff --git a/platform/mcu/xr871/src/net/udhcp/dumpleases.1 b/platform/mcu/xr871/src/net/udhcp/dumpleases.1 new file mode 100644 index 0000000000..04a04da155 --- /dev/null +++ b/platform/mcu/xr871/src/net/udhcp/dumpleases.1 @@ -0,0 +1,30 @@ +.TH DUMPLEASES 1 2001-09-27 GNU/Linux "GNU/Linux Administrator's Manual" +.SH NAME +dumpleases \- display leases granted by udhcp server +.SH SYNOPSIS +.B dumpleases +.RI [ OPTION ]... +.SH DESCRIPTION +Display the DHCP leases granted by +.BR udhcpd (8). +.SH OPTIONS +.TP +.BR \-a ,\ \-\-absolute +Interpret lease times as expiration time. +.TP +.BI \-f\ FILE,\ \-\-file= FILE +Read lease information from +.IR FILE . +.TP +.BR \-h ,\ \-\-help +Display help. +.TP +.BR \-r ,\ \-\-remaining +Interpret lease times as remaining time. +.SH FILES +.TP +.I /var/lib/misc/udhcpd.leases +Lease information file. +.SH SEE ALSO +.BR udhcpd (8), +.BR udhcpd.conf (5). diff --git a/platform/mcu/xr871/src/net/udhcp/dumpleases.c b/platform/mcu/xr871/src/net/udhcp/dumpleases.c new file mode 100644 index 0000000000..78d4322c91 --- /dev/null +++ b/platform/mcu/xr871/src/net/udhcp/dumpleases.c @@ -0,0 +1,112 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "libbb_udhcp.h" + +#define REMAINING 0 +#define ABSOLUTE 1 + +struct lease_t { + unsigned char chaddr[16]; + u_int32_t yiaddr; + u_int32_t expires; +}; + +#ifdef BB_VER +int dumpleases_main(int argc, char *argv[]) +#else +int main(int argc, char *argv[]) +#endif +{ + FILE *fp; + int i, c, mode = REMAINING; + long expires; + char file[255] = "/var/lib/misc/udhcpd.leases"; + struct lease_t lease; + struct in_addr addr; + + static struct option options[] = { + {"absolute", 0, 0, 'a'}, + {"remaining", 0, 0, 'r'}, + {"file", 1, 0, 'f'}, + {"help", 0, 0, 'h'}, + {0, 0, 0, 0} + }; + + while (1) { + int option_index = 0; + c = getopt_long(argc, argv, "arf:h", options, &option_index); + if (c == -1) break; + + switch (c) { + case 'a': mode = ABSOLUTE; break; + case 'r': mode = REMAINING; break; + case 'f': + strncpy(file, optarg, 255); + file[254] = '\0'; + break; + case 'h': + printf("Usage: dumpleases -f -[r|a]\n\n"); + printf(" -f, --file=FILENAME Leases file to load\n"); + printf(" -r, --remaining Interepret lease times as time remaing\n"); + printf(" -a, --absolute Interepret lease times as expire time\n"); + break; + } + } + + if (!(fp = fopen(file, "r"))) { + perror("could not open input file"); + return 0; + } + + printf("Mac Address IP-Address Expires %s\n", mode == REMAINING ? "in" : "at"); + /* "00:00:00:00:00:00 255.255.255.255 Wed Jun 30 21:49:08 1993" */ + while (fread(&lease, sizeof(lease), 1, fp)) { + + for (i = 0; i < 6; i++) { + printf("%02x", lease.chaddr[i]); + if (i != 5) printf(":"); + } + addr.s_addr = lease.yiaddr; + printf(" %-15s", inet_ntoa(addr)); + expires = ntohl(lease.expires); + printf(" "); + if (mode == REMAINING) { + if (!expires) printf("expired\n"); + else { + if (expires > 60*60*24) { + printf("%ld days, ", expires / (60*60*24)); + expires %= 60*60*24; + } + if (expires > 60*60) { + printf("%ld hours, ", expires / (60*60)); + expires %= 60*60; + } + if (expires > 60) { + printf("%ld minutes, ", expires / 60); + expires %= 60; + } + printf("%ld seconds\n", expires); + } + } else printf("%s", ctime(&expires)); + } + fclose(fp); + + return 0; +} diff --git a/platform/mcu/xr871/src/net/udhcp/files.c b/platform/mcu/xr871/src/net/udhcp/files.c new file mode 100644 index 0000000000..a609741d7f --- /dev/null +++ b/platform/mcu/xr871/src/net/udhcp/files.c @@ -0,0 +1,322 @@ +/* + * files.c -- DHCP server file manipulation * + * Rewrite by Russ Dill July 2001 + */ + +#include +#include +#include +#include + +#ifdef DHCPD_LWIP +#include "lwip/netdb.h" +#include "lwip/sockets.h" +#else +#include +#include +#include +#endif + +#ifndef DHCPD_TIMEALT +#include +#endif + +#include "debug.h" +#include "dhcpd.h" +#include "files.h" +#include "options.h" +#include "leases.h" + +/* on these functions, make sure you datatype matches */ +static int read_ip(char *line, void *arg) +{ + struct in_addr *addr = arg; + struct hostent *host; + + int retval = 1; + if (!inet_aton(line, addr)) { + if ((host = gethostbyname(line))) { + addr->s_addr = *((unsigned long *) host->h_addr_list[0]); + } + else retval = 0; + + } + return retval; +} + +static int read_str(char *line, void *arg) +{ + char **dest = arg; + + if (*dest) free(*dest); + *dest = strdup(line); + + return 1; +} + +static int read_u32(char *line, void *arg) +{ + u_int32_t *dest = arg; + char *endptr; + *dest = strtoul(line, &endptr, 0); + return endptr[0] == '\0'; +} + +static int read_yn(char *line, void *arg) +{ + char *dest = arg; + int retval = 1; + + if (!strcasecmp("yes", line)) + *dest = 1; + else if (!strcasecmp("no", line)) + *dest = 0; + else retval = 0; + + return retval; +} + +/* read a dhcp option and add it to opt_list */ +static int read_opt(char *line, void *arg) +{ + struct option_set **opt_list = arg; + char *opt, *val, *endptr; + struct dhcp_option *option = NULL; + int retval = 0, length = 0; + char buffer[255]; + u_int16_t result_u16; + u_int32_t result_u32; + int i; + + if (!(opt = strtok(line, " \t="))) return 0; + + for (i = 0; options[i].code; i++) + if (!strcmp(options[i].name, opt)) + option = &(options[i]); + + if (!option) return 0; + + do { + val = strtok(NULL, ", \t"); + if (val) { + length = option_lengths[option->flags & TYPE_MASK]; + retval = 0; + switch (option->flags & TYPE_MASK) { + case OPTION_IP: + retval = read_ip(val, buffer); + break; + case OPTION_IP_PAIR: + retval = read_ip(val, buffer); + if (!(val = strtok(NULL, ", \t/-"))) retval = 0; + if (retval) retval = read_ip(val, buffer + 4); + break; + case OPTION_STRING: + length = strlen(val); + if (length > 0) { + if (length > 254) length = 254; + memcpy(buffer, val, length); + retval = 1; + } + break; + case OPTION_BOOLEAN: + retval = read_yn(val, buffer); + break; + case OPTION_U8: + buffer[0] = strtoul(val, &endptr, 0); + retval = (endptr[0] == '\0'); + break; + case OPTION_U16: + result_u16 = htons(strtoul(val, &endptr, 0)); + memcpy(buffer, &result_u16, 2); + retval = (endptr[0] == '\0'); + break; + case OPTION_S16: + result_u16 = htons(strtol(val, &endptr, 0)); + memcpy(buffer, &result_u16, 2); + retval = (endptr[0] == '\0'); + break; + case OPTION_U32: + result_u32 = htonl(strtoul(val, &endptr, 0)); + memcpy(buffer, &result_u32, 4); + retval = (endptr[0] == '\0'); + break; + case OPTION_S32: + result_u32 = htonl(strtol(val, &endptr, 0)); + memcpy(buffer, &result_u32, 4); + retval = (endptr[0] == '\0'); + break; + default: + break; + } + if (retval) + attach_option(opt_list, option, buffer, length); + }; + } while (val && retval && option->flags & OPTION_LIST); + return retval; +} + +#ifndef DHCPD_USRCFG + +static struct config_keyword keywords[] = { + /* keyword[14] handler variable address default[20] */ + {"start", read_ip, &(server_config.start), "192.168.0.20"}, + {"end", read_ip, &(server_config.end), "192.168.0.254"}, + {"interface", read_str, &(server_config.interface), "eth0"}, + {"option", read_opt, &(server_config.options), ""}, + {"opt", read_opt, &(server_config.options), ""}, + {"max_leases", read_u32, &(server_config.max_leases), "254"}, + {"remaining", read_yn, &(server_config.remaining), "yes"}, + {"auto_time", read_u32, &(server_config.auto_time), "7200"}, + {"decline_time",read_u32, &(server_config.decline_time),"3600"}, + {"conflict_time",read_u32,&(server_config.conflict_time),"3600"}, + {"offer_time", read_u32, &(server_config.offer_time), "60"}, + {"min_lease", read_u32, &(server_config.min_lease), "60"}, + {"lease_file", read_str, &(server_config.lease_file), "/var/lib/misc/udhcpd.leases"}, + {"pidfile", read_str, &(server_config.pidfile), "/var/run/udhcpd.pid"}, + {"notify_file", read_str, &(server_config.notify_file), ""}, + {"siaddr", read_ip, &(server_config.siaddr), "0.0.0.0"}, + {"sname", read_str, &(server_config.sname), ""}, + {"boot_file", read_str, &(server_config.boot_file), ""}, + /*ADDME: static lease */ + {"", NULL, NULL, ""} +}; +#else + +#include "dhcpd_cfg.h" + +static struct config_keyword keywords[] = { + /* keyword[14] handler variable address default[20] */ + {"start", read_ip, &(server_config.start), DHCPD_ADDR_START}, /* default start addr*/ + {"end", read_ip, &(server_config.end), DHCPD_ADDR_END}, /* default end addr*/ + {"interface", read_str, &(server_config.interface), DHCPD_INTERFACE}, + {"option", read_opt, &(server_config.options), DHCPD_OPTION}, + {"opt", read_opt, &(server_config.options), DHCPD_OPT}, + {"max_leases", read_u32, &(server_config.max_leases), DHCPD_MAX_LEASES}, + {"remaining", read_yn, &(server_config.remaining), DHCPD_REMAIN}, + {"auto_time", read_u32, &(server_config.auto_time), DHCPD_AUTO_TIME}, + {"decline_time",read_u32, &(server_config.decline_time),DHCPD_DECLINE_TIME}, + {"conflict_time",read_u32,&(server_config.conflict_time),DHCPD_CONFLICT_TIME}, + {"offer_time", read_u32, &(server_config.offer_time), DHCPD_OFFER_TIME}, + {"min_lease", read_u32, &(server_config.min_lease), DHCPD_MIN_LEASE}, + {"siaddr", read_ip, &(server_config.siaddr), DHCPD_SIADDR}, + {"sname", read_str, &(server_config.sname), DHCPD_SNAME}, + {"", NULL, NULL, ""} +}; + +int init_config() +{ + u_int32_t i = 0; + for (i = 0; strlen(keywords[i].keyword); i++) + if (strlen(keywords[i].def)) + keywords[i].handler(keywords[i].def, keywords[i].var); + return 0; +} +#endif + +#ifdef DHCPD_FS +int read_config(char *file) +{ + FILE *in; + char buffer[80], orig[80], *token, *line; + int i; + + for (i = 0; strlen(keywords[i].keyword); i++) + if (strlen(keywords[i].def)) + keywords[i].handler(keywords[i].def, keywords[i].var); + + if (!(in = fopen(file, "r"))) { + DHCPD_LOG(LOG_ERR, "unable to open config file: %s", file); + return 0; + } + + while (fgets(buffer, 80, in)) { + if (strchr(buffer, '\n')) *(strchr(buffer, '\n')) = '\0'; + strncpy(orig, buffer, 80); + if (strchr(buffer, '#')) *(strchr(buffer, '#')) = '\0'; + token = buffer + strspn(buffer, " \t"); + if (*token == '\0') continue; + line = token + strcspn(token, " \t="); + if (*line == '\0') continue; + *line = '\0'; + line++; + + /* eat leading whitespace */ + line = line + strspn(line, " \t="); + /* eat trailing whitespace */ + for (i = strlen(line); i > 0 && isspace(line[i - 1]); i--); + line[i] = '\0'; + + for (i = 0; strlen(keywords[i].keyword); i++) + if (!strcasecmp(token, keywords[i].keyword)) + if (!keywords[i].handler(line, keywords[i].var)) { + DHCPD_LOG(LOG_ERR, "unable to parse '%s'", orig); + /* reset back to the default value */ + keywords[i].handler(keywords[i].def, keywords[i].var); + } + } + fclose(in); + return 1; +} + +void write_leases(void) +{ + FILE *fp; + unsigned int i; + char buf[255]; + time_t curr = time(0); + unsigned long lease_time; + + if (!(fp = fopen(server_config.lease_file, "w"))) { + DHCPD_LOG(LOG_ERR, "Unable to open %s for writing", server_config.lease_file); + return; + } + + for (i = 0; i < server_config.max_leases; i++) { + if (leases[i].yiaddr != 0) { + if (server_config.remaining) { + if (lease_expired(&(leases[i]))) + lease_time = 0; + else lease_time = leases[i].expires - curr; + } else lease_time = leases[i].expires; + lease_time = htonl(lease_time); + fwrite(leases[i].chaddr, 16, 1, fp); + fwrite(&(leases[i].yiaddr), 4, 1, fp); + fwrite(&lease_time, 4, 1, fp); + } + } + fclose(fp); + + if (server_config.notify_file) { + sprintf(buf, "%s %s", server_config.notify_file, server_config.lease_file); + system(buf); + } +} + +void read_leases(char *file) +{ + FILE *fp; + unsigned int i = 0; + struct dhcpOfferedAddr lease; + + if (!(fp = fopen(file, "r"))) { + DHCPD_LOG(LOG_ERR, "Unable to open %s for reading", file); + return; + } + + while (i < server_config.max_leases && (fread(&lease, sizeof lease, 1, fp) == 1)) { + /* ADDME: is it a static lease */ + if (lease.yiaddr >= server_config.start && lease.yiaddr <= server_config.end) { + lease.expires = ntohl(lease.expires); + if (!server_config.remaining) lease.expires -= time(0); + if (!(add_lease(lease.chaddr, lease.yiaddr, lease.expires))) { + DHCPD_LOG(LOG_WARNING, "Too many leases while loading %s\n", file); + break; + } + i++; + } + } + DEBUG(LOG_INFO, "Read %d leases", i); + fclose(fp); +} + +#endif diff --git a/platform/mcu/xr871/src/net/udhcp/files.h b/platform/mcu/xr871/src/net/udhcp/files.h new file mode 100644 index 0000000000..695b7c73b5 --- /dev/null +++ b/platform/mcu/xr871/src/net/udhcp/files.h @@ -0,0 +1,19 @@ +/* files.h */ +#ifndef _FILES_H +#define _FILES_H + +struct config_keyword { + char keyword[14]; + int (*handler)(char *line, void *var); + void *var; + char def[30]; +}; + +#ifdef DHCPD_FS +int read_config(char *file); +void write_leases(void); +void read_leases(char *file); +#else +int init_config(); +#endif +#endif diff --git a/platform/mcu/xr871/src/net/udhcp/frontend.c b/platform/mcu/xr871/src/net/udhcp/frontend.c new file mode 100644 index 0000000000..de57795084 --- /dev/null +++ b/platform/mcu/xr871/src/net/udhcp/frontend.c @@ -0,0 +1,16 @@ +#include + +extern int udhcpd_main(int argc, char *argv[]); +extern int udhcpc_main(int argc, char *argv[]); + +int main(int argc, char *argv[]) +{ + int ret = 0; + char *base = strrchr(argv[0], '/'); + + if (strstr(base ? (base + 1) : argv[0], "dhcpd")) + ret = udhcpd_main(argc, argv); + else ret = udhcpc_main(argc, argv); + + return ret; +} diff --git a/platform/mcu/xr871/src/net/udhcp/leases.c b/platform/mcu/xr871/src/net/udhcp/leases.c new file mode 100644 index 0000000000..0550bd691b --- /dev/null +++ b/platform/mcu/xr871/src/net/udhcp/leases.c @@ -0,0 +1,161 @@ +/* + * leases.c -- tools to manage DHCP leases + * Russ Dill July 2001 + */ +#include + +#ifdef DHCPD_LWIP +#include +#else +#include +#include +#include +#endif + +#ifdef DHCPD_TIMEALT +#include "dhcp_time.h" +#else +#include +#endif + +#include "debug.h" +#include "dhcpd.h" +#include "files.h" +#include "options.h" +#include "leases.h" +#include "arpping.h" + +unsigned char blank_chaddr[] = {[0 ... 15] = 0}; + +/* clear every lease out that chaddr OR yiaddr matches and is nonzero */ +void clear_lease(u_int8_t *chaddr, u_int32_t yiaddr) +{ + unsigned int i, j; + + for (j = 0; j < 16 && !chaddr[j]; j++); + + for (i = 0; i < server_config.max_leases; i++) + if ((j != 16 && !memcmp(leases[i].chaddr, chaddr, 16)) || + (yiaddr && leases[i].yiaddr == yiaddr)) { + memset(&(leases[i]), 0, sizeof(struct dhcpOfferedAddr)); + } +} + + +/* add a lease into the table, clearing out any old ones */ +struct dhcpOfferedAddr *add_lease(u_int8_t *chaddr, u_int32_t yiaddr, unsigned long lease) +{ + struct dhcpOfferedAddr *oldest; + + /* clean out any old ones */ + clear_lease(chaddr, yiaddr); + + oldest = oldest_expired_lease(); + + if (oldest) { + memcpy(oldest->chaddr, chaddr, 16); + oldest->yiaddr = yiaddr; + oldest->expires = time(0) + lease; + } + + return oldest; +} + + +/* true if a lease has expired */ +int lease_expired(struct dhcpOfferedAddr *lease) +{ + return (lease->expires < (unsigned long) time(0)); +} + + +/* Find the oldest expired lease, NULL if there are no expired leases */ +struct dhcpOfferedAddr *oldest_expired_lease(void) +{ + struct dhcpOfferedAddr *oldest = NULL; + unsigned long oldest_lease = time(0); + unsigned int i; + + + for (i = 0; i < server_config.max_leases; i++) + if (oldest_lease > leases[i].expires) { + oldest_lease = leases[i].expires; + oldest = &(leases[i]); + } + return oldest; + +} + + +/* Find the first lease that matches chaddr, NULL if no match */ +struct dhcpOfferedAddr *find_lease_by_chaddr(u_int8_t *chaddr) +{ + unsigned int i; + + for (i = 0; i < server_config.max_leases; i++) + if (!memcmp(leases[i].chaddr, chaddr, 16)) return &(leases[i]); + + return NULL; +} + + +/* Find the first lease that matches yiaddr, NULL is no match */ +struct dhcpOfferedAddr *find_lease_by_yiaddr(u_int32_t yiaddr) +{ + unsigned int i; + + for (i = 0; i < server_config.max_leases; i++) + if (leases[i].yiaddr == yiaddr) return &(leases[i]); + + return NULL; +} + + +/* find an assignable address, it check_expired is true, we check all the expired leases as well. + * Maybe this should try expired leases by age... */ +u_int32_t find_address(int check_expired) +{ + u_int32_t addr, ret; + struct dhcpOfferedAddr *lease = NULL; + + addr = ntohl(server_config.start); /* addr is in host order here */ + for (;addr <= ntohl(server_config.end); addr++) { + + /* ie, 192.168.55.0 */ + if (!(addr & 0xFF)) continue; + + /* ie, 192.168.55.255 */ + if ((addr & 0xFF) == 0xFF) continue; + + /* lease is not taken */ + ret = htonl(addr); + if ((!(lease = find_lease_by_yiaddr(ret)) || + + /* or it expired and we are checking for expired leases */ + (check_expired && lease_expired(lease))) && + + /* and it isn't on the network */ + !check_ip(ret)) { + return ret; + break; + } + } + return 0; +} + + +/* check is an IP is taken, if it is, add it to the lease table */ +int check_ip(u_int32_t addr) +{ + struct in_addr temp; + memset(&temp, 0, sizeof(temp)); + DHCPD_LOG(LOG_INFO, "check ip"); + if (arpping(addr, server_config.server, server_config.arp, server_config.interface) == 0) { + temp.s_addr = addr; + DHCPD_LOG(LOG_INFO, "%s belongs to someone, reserving it for %ld seconds", + inet_ntoa(temp), server_config.conflict_time); + add_lease(blank_chaddr, addr, server_config.conflict_time); + return 1; + } else return 0; +} + diff --git a/platform/mcu/xr871/src/net/udhcp/leases.h b/platform/mcu/xr871/src/net/udhcp/leases.h new file mode 100644 index 0000000000..4c8f4ee187 --- /dev/null +++ b/platform/mcu/xr871/src/net/udhcp/leases.h @@ -0,0 +1,27 @@ +/* leases.h */ +#ifndef _LEASES_H +#define _LEASES_H + +#ifdef DHCPD_USRCFG +#include "dhcpd_cfg.h" +#endif + +struct dhcpOfferedAddr { + u_int8_t chaddr[16]; + u_int32_t yiaddr; /* network order */ + u_int32_t expires; /* host order */ +}; + +extern unsigned char blank_chaddr[]; + +void clear_lease(u_int8_t *chaddr, u_int32_t yiaddr); +struct dhcpOfferedAddr *add_lease(u_int8_t *chaddr, u_int32_t yiaddr, unsigned long lease); +int lease_expired(struct dhcpOfferedAddr *lease); +struct dhcpOfferedAddr *oldest_expired_lease(void); +struct dhcpOfferedAddr *find_lease_by_chaddr(u_int8_t *chaddr); +struct dhcpOfferedAddr *find_lease_by_yiaddr(u_int32_t yiaddr); +u_int32_t find_address(int check_expired); +int check_ip(u_int32_t addr); + + +#endif diff --git a/platform/mcu/xr871/src/net/udhcp/libbb_udhcp.h b/platform/mcu/xr871/src/net/udhcp/libbb_udhcp.h new file mode 100644 index 0000000000..d3f72c65b1 --- /dev/null +++ b/platform/mcu/xr871/src/net/udhcp/libbb_udhcp.h @@ -0,0 +1,29 @@ +/* libbb_udhcp.h - busybox compatability wrapper */ + +#ifndef _LIBBB_UDHCP_H +#define _LIBBB_UDHCP_H + +#ifdef BB_VER +#include "libbb.h" + +#ifdef CONFIG_FEATURE_UDHCP_SYSLOG +#define SYSLOG +#endif + +#ifdef CONFIG_FEATURE_UDHCP_DEBUG +#define DEBUG +#endif + +#define COMBINED_BINARY +#define VERSION "0.9.8-pre" + +#else /* ! BB_VER */ + +#define TRUE 1 +#define FALSE 0 + +#define xmalloc malloc + +#endif /* BB_VER */ + +#endif /* _LIBBB_UDHCP_H */ diff --git a/platform/mcu/xr871/src/net/udhcp/log b/platform/mcu/xr871/src/net/udhcp/log new file mode 100644 index 0000000000..86a4f652d9 --- /dev/null +++ b/platform/mcu/xr871/src/net/udhcp/log @@ -0,0 +1,9 @@ +../../../../../tools/toolchain/bin/arm-none-eabi-gcc -mcpu=cortex-m3 -mthumb -c -gdwarf-2 -fno-common -fmessage-length=0 -Wall -fno-exceptions -ffunction-sections -fdata-sections -fomit-frame-pointer -MMD -MP -DNDEBUG -Os -Werror -DBB_VER -Wno-address -DTARGET_M3 -DTARGET_CORTEX_M -DTARGET_XR1705_F103RB -DTARGET_AW -DTARGET_AW1705 -DTARGET_AW1705F103RB -DTOOLCHAIN_GCC_ARM -DTOOLCHAIN_GCC -D__CORTEX_M3 -DARM_MATH_CM3 -D__FPU_PRESENT=0 -DMBED_BUILD_TIMESTAMP=1437480207.16 -D__MBED__=1 -DTARGET_FF_ARDUINO -DTARGET_FF_MORPHO -D__XRADIO_HEADERS__ -D__XRADIO_REDEFINE_GCC_INT32_TYPE__ -D__XRADIO_DUAL_CORE__ -D__XRADIO_APP_CORE__ -D__XRADIO_SIMULATE_HW_MBOX__ -D__XRADIO_WLAN_CMD_TEST__ -std=gnu99 -I../../../../inc -I../../../../inc/rtos -I../../../../inc/net -I../../../../inc/net/lwip -I../../../../inc/net/lwip/ipv4 -o xradio_dhcpd.o xradio_dhcpd.c +../../../../../tools/toolchain/bin/arm-none-eabi-gcc -mcpu=cortex-m3 -mthumb -c -gdwarf-2 -fno-common -fmessage-length=0 -Wall -fno-exceptions -ffunction-sections -fdata-sections -fomit-frame-pointer -MMD -MP -DNDEBUG -Os -Werror -DBB_VER -Wno-address -DTARGET_M3 -DTARGET_CORTEX_M -DTARGET_XR1705_F103RB -DTARGET_AW -DTARGET_AW1705 -DTARGET_AW1705F103RB -DTOOLCHAIN_GCC_ARM -DTOOLCHAIN_GCC -D__CORTEX_M3 -DARM_MATH_CM3 -D__FPU_PRESENT=0 -DMBED_BUILD_TIMESTAMP=1437480207.16 -D__MBED__=1 -DTARGET_FF_ARDUINO -DTARGET_FF_MORPHO -D__XRADIO_HEADERS__ -D__XRADIO_REDEFINE_GCC_INT32_TYPE__ -D__XRADIO_DUAL_CORE__ -D__XRADIO_APP_CORE__ -D__XRADIO_SIMULATE_HW_MBOX__ -D__XRADIO_WLAN_CMD_TEST__ -std=gnu99 -I../../../../inc -I../../../../inc/rtos -I../../../../inc/net -I../../../../inc/net/lwip -I../../../../inc/net/lwip/ipv4 -o arpping.o arpping.c +../../../../../tools/toolchain/bin/arm-none-eabi-gcc -mcpu=cortex-m3 -mthumb -c -gdwarf-2 -fno-common -fmessage-length=0 -Wall -fno-exceptions -ffunction-sections -fdata-sections -fomit-frame-pointer -MMD -MP -DNDEBUG -Os -Werror -DBB_VER -Wno-address -DTARGET_M3 -DTARGET_CORTEX_M -DTARGET_XR1705_F103RB -DTARGET_AW -DTARGET_AW1705 -DTARGET_AW1705F103RB -DTOOLCHAIN_GCC_ARM -DTOOLCHAIN_GCC -D__CORTEX_M3 -DARM_MATH_CM3 -D__FPU_PRESENT=0 -DMBED_BUILD_TIMESTAMP=1437480207.16 -D__MBED__=1 -DTARGET_FF_ARDUINO -DTARGET_FF_MORPHO -D__XRADIO_HEADERS__ -D__XRADIO_REDEFINE_GCC_INT32_TYPE__ -D__XRADIO_DUAL_CORE__ -D__XRADIO_APP_CORE__ -D__XRADIO_SIMULATE_HW_MBOX__ -D__XRADIO_WLAN_CMD_TEST__ -std=gnu99 -I../../../../inc -I../../../../inc/rtos -I../../../../inc/net -I../../../../inc/net/lwip -I../../../../inc/net/lwip/ipv4 -o files.o files.c +../../../../../tools/toolchain/bin/arm-none-eabi-gcc -mcpu=cortex-m3 -mthumb -c -gdwarf-2 -fno-common -fmessage-length=0 -Wall -fno-exceptions -ffunction-sections -fdata-sections -fomit-frame-pointer -MMD -MP -DNDEBUG -Os -Werror -DBB_VER -Wno-address -DTARGET_M3 -DTARGET_CORTEX_M -DTARGET_XR1705_F103RB -DTARGET_AW -DTARGET_AW1705 -DTARGET_AW1705F103RB -DTOOLCHAIN_GCC_ARM -DTOOLCHAIN_GCC -D__CORTEX_M3 -DARM_MATH_CM3 -D__FPU_PRESENT=0 -DMBED_BUILD_TIMESTAMP=1437480207.16 -D__MBED__=1 -DTARGET_FF_ARDUINO -DTARGET_FF_MORPHO -D__XRADIO_HEADERS__ -D__XRADIO_REDEFINE_GCC_INT32_TYPE__ -D__XRADIO_DUAL_CORE__ -D__XRADIO_APP_CORE__ -D__XRADIO_SIMULATE_HW_MBOX__ -D__XRADIO_WLAN_CMD_TEST__ -std=gnu99 -I../../../../inc -I../../../../inc/rtos -I../../../../inc/net -I../../../../inc/net/lwip -I../../../../inc/net/lwip/ipv4 -o leases.o leases.c +../../../../../tools/toolchain/bin/arm-none-eabi-gcc -mcpu=cortex-m3 -mthumb -c -gdwarf-2 -fno-common -fmessage-length=0 -Wall -fno-exceptions -ffunction-sections -fdata-sections -fomit-frame-pointer -MMD -MP -DNDEBUG -Os -Werror -DBB_VER -Wno-address -DTARGET_M3 -DTARGET_CORTEX_M -DTARGET_XR1705_F103RB -DTARGET_AW -DTARGET_AW1705 -DTARGET_AW1705F103RB -DTOOLCHAIN_GCC_ARM -DTOOLCHAIN_GCC -D__CORTEX_M3 -DARM_MATH_CM3 -D__FPU_PRESENT=0 -DMBED_BUILD_TIMESTAMP=1437480207.16 -D__MBED__=1 -DTARGET_FF_ARDUINO -DTARGET_FF_MORPHO -D__XRADIO_HEADERS__ -D__XRADIO_REDEFINE_GCC_INT32_TYPE__ -D__XRADIO_DUAL_CORE__ -D__XRADIO_APP_CORE__ -D__XRADIO_SIMULATE_HW_MBOX__ -D__XRADIO_WLAN_CMD_TEST__ -std=gnu99 -I../../../../inc -I../../../../inc/rtos -I../../../../inc/net -I../../../../inc/net/lwip -I../../../../inc/net/lwip/ipv4 -o serverpacket.o serverpacket.c +../../../../../tools/toolchain/bin/arm-none-eabi-gcc -mcpu=cortex-m3 -mthumb -c -gdwarf-2 -fno-common -fmessage-length=0 -Wall -fno-exceptions -ffunction-sections -fdata-sections -fomit-frame-pointer -MMD -MP -DNDEBUG -Os -Werror -DBB_VER -Wno-address -DTARGET_M3 -DTARGET_CORTEX_M -DTARGET_XR1705_F103RB -DTARGET_AW -DTARGET_AW1705 -DTARGET_AW1705F103RB -DTOOLCHAIN_GCC_ARM -DTOOLCHAIN_GCC -D__CORTEX_M3 -DARM_MATH_CM3 -D__FPU_PRESENT=0 -DMBED_BUILD_TIMESTAMP=1437480207.16 -D__MBED__=1 -DTARGET_FF_ARDUINO -DTARGET_FF_MORPHO -D__XRADIO_HEADERS__ -D__XRADIO_REDEFINE_GCC_INT32_TYPE__ -D__XRADIO_DUAL_CORE__ -D__XRADIO_APP_CORE__ -D__XRADIO_SIMULATE_HW_MBOX__ -D__XRADIO_WLAN_CMD_TEST__ -std=gnu99 -I../../../../inc -I../../../../inc/rtos -I../../../../inc/net -I../../../../inc/net/lwip -I../../../../inc/net/lwip/ipv4 -o options.o options.c +../../../../../tools/toolchain/bin/arm-none-eabi-gcc -mcpu=cortex-m3 -mthumb -c -gdwarf-2 -fno-common -fmessage-length=0 -Wall -fno-exceptions -ffunction-sections -fdata-sections -fomit-frame-pointer -MMD -MP -DNDEBUG -Os -Werror -DBB_VER -Wno-address -DTARGET_M3 -DTARGET_CORTEX_M -DTARGET_XR1705_F103RB -DTARGET_AW -DTARGET_AW1705 -DTARGET_AW1705F103RB -DTOOLCHAIN_GCC_ARM -DTOOLCHAIN_GCC -D__CORTEX_M3 -DARM_MATH_CM3 -D__FPU_PRESENT=0 -DMBED_BUILD_TIMESTAMP=1437480207.16 -D__MBED__=1 -DTARGET_FF_ARDUINO -DTARGET_FF_MORPHO -D__XRADIO_HEADERS__ -D__XRADIO_REDEFINE_GCC_INT32_TYPE__ -D__XRADIO_DUAL_CORE__ -D__XRADIO_APP_CORE__ -D__XRADIO_SIMULATE_HW_MBOX__ -D__XRADIO_WLAN_CMD_TEST__ -std=gnu99 -I../../../../inc -I../../../../inc/rtos -I../../../../inc/net -I../../../../inc/net/lwip -I../../../../inc/net/lwip/ipv4 -o socket.o socket.c +../../../../../tools/toolchain/bin/arm-none-eabi-gcc -mcpu=cortex-m3 -mthumb -c -gdwarf-2 -fno-common -fmessage-length=0 -Wall -fno-exceptions -ffunction-sections -fdata-sections -fomit-frame-pointer -MMD -MP -DNDEBUG -Os -Werror -DBB_VER -Wno-address -DTARGET_M3 -DTARGET_CORTEX_M -DTARGET_XR1705_F103RB -DTARGET_AW -DTARGET_AW1705 -DTARGET_AW1705F103RB -DTOOLCHAIN_GCC_ARM -DTOOLCHAIN_GCC -D__CORTEX_M3 -DARM_MATH_CM3 -D__FPU_PRESENT=0 -DMBED_BUILD_TIMESTAMP=1437480207.16 -D__MBED__=1 -DTARGET_FF_ARDUINO -DTARGET_FF_MORPHO -D__XRADIO_HEADERS__ -D__XRADIO_REDEFINE_GCC_INT32_TYPE__ -D__XRADIO_DUAL_CORE__ -D__XRADIO_APP_CORE__ -D__XRADIO_SIMULATE_HW_MBOX__ -D__XRADIO_WLAN_CMD_TEST__ -std=gnu99 -I../../../../inc -I../../../../inc/rtos -I../../../../inc/net -I../../../../inc/net/lwip -I../../../../inc/net/lwip/ipv4 -o packet.o packet.c +../../../../../tools/toolchain/bin/arm-none-eabi-ar -crs libudhcpd.a xradio_dhcpd.o arpping.o files.o leases.o serverpacket.o options.o socket.o packet.o diff --git a/platform/mcu/xr871/src/net/udhcp/options.c b/platform/mcu/xr871/src/net/udhcp/options.c new file mode 100644 index 0000000000..2c4128f55f --- /dev/null +++ b/platform/mcu/xr871/src/net/udhcp/options.c @@ -0,0 +1,230 @@ +/* + * options.c -- DHCP server option packet tools + * Rewrite by Russ Dill July 2001 + */ + +#include +#include +#include + +#include "debug.h" +#include "dhcpd.h" +#include "files.h" +#include "options.h" +#include "leases.h" + + +/* supported options are easily added here */ +struct dhcp_option options[] = { + /* name[10] flags code */ + {"subnet", OPTION_IP | OPTION_REQ, 0x01}, + {"timezone", OPTION_S32, 0x02}, + {"router", OPTION_IP | OPTION_LIST | OPTION_REQ, 0x03}, + {"timesvr", OPTION_IP | OPTION_LIST, 0x04}, + {"namesvr", OPTION_IP | OPTION_LIST, 0x05}, + {"dns", OPTION_IP | OPTION_LIST | OPTION_REQ, 0x06}, + {"logsvr", OPTION_IP | OPTION_LIST, 0x07}, + {"cookiesvr", OPTION_IP | OPTION_LIST, 0x08}, + {"lprsvr", OPTION_IP | OPTION_LIST, 0x09}, + {"hostname", OPTION_STRING | OPTION_REQ, 0x0c}, + {"bootsize", OPTION_U16, 0x0d}, + {"domain", OPTION_STRING | OPTION_REQ, 0x0f}, + {"swapsvr", OPTION_IP, 0x10}, + {"rootpath", OPTION_STRING, 0x11}, + {"ipttl", OPTION_U8, 0x17}, + {"mtu", OPTION_U16, 0x1a}, + {"broadcast", OPTION_IP | OPTION_REQ, 0x1c}, + {"ntpsrv", OPTION_IP | OPTION_LIST, 0x2a}, + {"wins", OPTION_IP | OPTION_LIST, 0x2c}, + {"requestip", OPTION_IP, 0x32}, + {"lease", OPTION_U32, 0x33}, + {"dhcptype", OPTION_U8, 0x35}, + {"serverid", OPTION_IP, 0x36}, + {"message", OPTION_STRING, 0x38}, + {"tftp", OPTION_STRING, 0x42}, + {"bootfile", OPTION_STRING, 0x43}, + {"", 0x00, 0x00} +}; + +/* Lengths of the different option types */ +int option_lengths[] = { + [OPTION_IP] = 4, + [OPTION_IP_PAIR] = 8, + [OPTION_BOOLEAN] = 1, + [OPTION_STRING] = 1, + [OPTION_U8] = 1, + [OPTION_U16] = 2, + [OPTION_S16] = 2, + [OPTION_U32] = 4, + [OPTION_S32] = 4 +}; + + +/* get an option with bounds checking (warning, not aligned). */ +unsigned char *get_option(struct dhcpMessage *packet, int code) +{ + int i, length; + unsigned char *optionptr; + int over = 0, done = 0, curr = OPTION_FIELD; + + optionptr = packet->options; + i = 0; + length = 308; + while (!done) { + if (i >= length) { + DHCPD_LOG(LOG_WARNING, "bogus packet, option fields too long."); + return NULL; + } + if (optionptr[i + OPT_CODE] == code) { + if (i + 1 + optionptr[i + OPT_LEN] >= length) { + DHCPD_LOG(LOG_WARNING, "bogus packet, option fields too long."); + return NULL; + } + return optionptr + i + 2; + } + switch (optionptr[i + OPT_CODE]) { + case DHCP_PADDING: + i++; + break; + case DHCP_OPTION_OVER: + if (i + 1 + optionptr[i + OPT_LEN] >= length) { + DHCPD_LOG(LOG_WARNING, "bogus packet, option fields too long."); + return NULL; + } + over = optionptr[i + 3]; + i += optionptr[OPT_LEN] + 2; + break; + case DHCP_END: + if (curr == OPTION_FIELD && over & FILE_FIELD) { + optionptr = packet->file; + i = 0; + length = 128; + curr = FILE_FIELD; + } else if (curr == FILE_FIELD && over & SNAME_FIELD) { + optionptr = packet->sname; + i = 0; + length = 64; + curr = SNAME_FIELD; + } else done = 1; + break; + default: + i += optionptr[OPT_LEN + i] + 2; + } + } + return NULL; +} + + +/* return the position of the 'end' option (no bounds checking) */ +int end_option(unsigned char *optionptr) +{ + int i = 0; + + while (optionptr[i] != DHCP_END) { + if (optionptr[i] == DHCP_PADDING) i++; + else i += optionptr[i + OPT_LEN] + 2; + } + return i; +} + + +/* add an option string to the options (an option string contains an option code, + * length, then data) */ +int add_option_string(unsigned char *optionptr, unsigned char *string) +{ + int end = end_option(optionptr); + + /* end position + string length + option code/length + end option */ + if (end + string[OPT_LEN] + 2 + 1 >= 308) { + DHCPD_LOG(LOG_ERR, "Option 0x%02x did not fit into the packet!", string[OPT_CODE]); + return 0; + } + DEBUG(LOG_INFO, "adding option 0x%02x", string[OPT_CODE]); + memcpy(optionptr + end, string, string[OPT_LEN] + 2); + optionptr[end + string[OPT_LEN] + 2] = DHCP_END; + return string[OPT_LEN] + 2; +} + + +/* add a one to four byte option to a packet */ +int add_simple_option(unsigned char *optionptr, unsigned char code, u_int32_t data) +{ + char length = 0; + int i; + unsigned char option[2 + 4]; + unsigned char *u8; + u_int16_t *u16; + u_int32_t *u32; + u_int32_t aligned; + u8 = (unsigned char *) &aligned; + u16 = (u_int16_t *) &aligned; + u32 = &aligned; + + for (i = 0; options[i].code; i++) + if (options[i].code == code) { + length = option_lengths[options[i].flags & TYPE_MASK]; + } + + if (!length) { + DEBUG(LOG_ERR, "Could not add option 0x%02x", code); + return 0; + } + + option[OPT_CODE] = code; + option[OPT_LEN] = length; + + switch (length) { + case 1: *u8 = data; break; + case 2: *u16 = data; break; + case 4: *u32 = data; break; + } + memcpy(option + 2, &aligned, length); + return add_option_string(optionptr, option); +} + + +/* find option 'code' in opt_list */ +struct option_set *find_option(struct option_set *opt_list, char code) +{ + while (opt_list && opt_list->data[OPT_CODE] < code) + opt_list = opt_list->next; + + if (opt_list && opt_list->data[OPT_CODE] == code) return opt_list; + else return NULL; +} + + +/* add an option to the opt_list */ +void attach_option(struct option_set **opt_list, struct dhcp_option *option, char *buffer, int length) +{ + struct option_set *existing, *new, **curr; + + /* add it to an existing option */ + if ((existing = find_option(*opt_list, option->code))) { + DEBUG(LOG_INFO, "Attaching option %s to existing member of list", option->name); + if (option->flags & OPTION_LIST) { + if (existing->data[OPT_LEN] + length <= 255) { + existing->data = realloc(existing->data, + existing->data[OPT_LEN] + length + 2); + memcpy(existing->data + existing->data[OPT_LEN] + 2, buffer, length); + existing->data[OPT_LEN] += length; + } /* else, ignore the data, we could put this in a second option in the future */ + } /* else, ignore the new data */ + } else { + DEBUG(LOG_INFO, "Attaching option %s to list", option->name); + + /* make a new option */ + new = malloc(sizeof(struct option_set)); + new->data = malloc(length + 2); + new->data[OPT_CODE] = option->code; + new->data[OPT_LEN] = length; + memcpy(new->data + 2, buffer, length); + + curr = opt_list; + while (*curr && (*curr)->data[OPT_CODE] < option->code) + curr = &(*curr)->next; + + new->next = *curr; + *curr = new; + } +} diff --git a/platform/mcu/xr871/src/net/udhcp/options.h b/platform/mcu/xr871/src/net/udhcp/options.h new file mode 100644 index 0000000000..1fded2ef4d --- /dev/null +++ b/platform/mcu/xr871/src/net/udhcp/options.h @@ -0,0 +1,40 @@ +/* options.h */ +#ifndef _OPTIONS_H +#define _OPTIONS_H + +#include "packet.h" + +#define TYPE_MASK 0x0F + +enum { + OPTION_IP=1, + OPTION_IP_PAIR, + OPTION_STRING, + OPTION_BOOLEAN, + OPTION_U8, + OPTION_U16, + OPTION_S16, + OPTION_U32, + OPTION_S32 +}; + +#define OPTION_REQ 0x10 /* have the client request this option */ +#define OPTION_LIST 0x20 /* There can be a list of 1 or more of these */ + +struct dhcp_option { + char name[10]; + char flags; + unsigned char code; +}; + +extern struct dhcp_option options[]; +extern int option_lengths[]; + +unsigned char *get_option(struct dhcpMessage *packet, int code); +int end_option(unsigned char *optionptr); +int add_option_string(unsigned char *optionptr, unsigned char *string); +int add_simple_option(unsigned char *optionptr, unsigned char code, u_int32_t data); +struct option_set *find_option(struct option_set *opt_list, char code); +void attach_option(struct option_set **opt_list, struct dhcp_option *option, char *buffer, int length); + +#endif diff --git a/platform/mcu/xr871/src/net/udhcp/packet.c b/platform/mcu/xr871/src/net/udhcp/packet.c new file mode 100644 index 0000000000..fd44e0d98d --- /dev/null +++ b/platform/mcu/xr871/src/net/udhcp/packet.c @@ -0,0 +1,357 @@ +#include +#include + +#ifndef DHCPD_LWIP +#include +#include +#include +#endif +#include + + +#if __GLIBC__ >=2 && __GLIBC_MINOR >= 1 +#include +#include +#elif __LINUX +#include +#include +#include +#endif + +#ifdef DHCPD_LWIP +#include "netif/etharp.h" +#include "lwip/sockets.h" +#include "lwip/inet_chksum.h" +#include "lwip/ip_addr.h" +#include "lwipopts.h" +#include "stdlib.h" +#endif + +#include "packet.h" +#include "debug.h" +#include "dhcpd.h" +#include "options.h" + + +void init_header(struct dhcpMessage *packet, char type) +{ + memset(packet, 0, sizeof(struct dhcpMessage)); + switch (type) { + case DHCPDISCOVER: + case DHCPREQUEST: + case DHCPRELEASE: + case DHCPINFORM: + packet->op = BOOTREQUEST; + break; + case DHCPOFFER: + case DHCPACK: + case DHCPNAK: + packet->op = BOOTREPLY; + } + packet->htype = ETH_10MB; + packet->hlen = ETH_10MB_LEN; + packet->cookie = htonl(DHCP_MAGIC); + packet->options[0] = DHCP_END; + add_simple_option(packet->options, DHCP_MESSAGE_TYPE, type); +} + + +/* read a packet from socket fd, return -1 on read error, -2 on packet error */ +int get_packet(struct dhcpMessage *packet, int fd) +{ + int bytes; + int i; + const char broken_vendors[][8] = { + "MSFT 98", + "" + }; + char unsigned *vendor; + + memset(packet, 0, sizeof(struct dhcpMessage)); +#ifndef DHCPD_LWIP + bytes = read(fd, packet, sizeof(struct dhcpMessage)); +#else + struct sockaddr_in from; + int from_len = sizeof(from); + bytes = recvfrom(fd, packet, sizeof(struct dhcpMessage), 0, (struct sockaddr*)&from, (socklen_t *)&from_len); +#endif + if (bytes < 0) { + DEBUG(LOG_INFO, "couldn't read on listening socket, ignoring"); + return -1; + } + if (ntohl(packet->cookie) != DHCP_MAGIC) { + if ((strncmp((char *)packet,"", strlen("")) == 0) && + (from.sin_addr.s_addr == htonl(INADDR_LOOPBACK))) { + DHCPD_LOG(LOG_ALERT, "received stop server message"); + return -3; + } + DHCPD_LOG(LOG_ERR, "received bogus message, ignoring"); + return -2; + } + DEBUG(LOG_INFO, "Received a packet"); + + if (packet->op == BOOTREQUEST && (vendor = get_option(packet, DHCP_VENDOR))) { + for (i = 0; broken_vendors[i][0]; i++) { + if (vendor[OPT_LEN - 2] == (unsigned char) strlen(broken_vendors[i]) && + !strncmp((char *)vendor, broken_vendors[i], vendor[OPT_LEN - 2])) { + DEBUG(LOG_INFO, "broken client (%s), forcing broadcast", + broken_vendors[i]); + packet->flags |= htons(BROADCAST_FLAG); + } + } + } + + + return bytes; +} + + +u_int16_t checksum(void *addr, int count) +{ + /* Compute Internet Checksum for "count" bytes + * beginning at location "addr". + */ + register int32_t sum = 0; + u_int16_t *source = (u_int16_t *) addr; + + while (count > 1) { + /* This is the inner loop */ + sum += *source++; + count -= 2; + } + + /* Add left-over byte, if any */ + if (count > 0) { + /* Make sure that the left-over byte is added correctly both + * with little and big endian hosts */ + u_int16_t tmp = 0; + *(unsigned char *) (&tmp) = * (unsigned char *) source; + sum += tmp; + } + /* Fold 32-bit sum to 16 bits */ + while (sum >> 16) + sum = (sum & 0xffff) + (sum >> 16); + + return ~sum; +} +#define ETH_P_IP ETHTYPE_IP + +int _low_level_dhcp_send(struct dhcpMessage *payload, u_int32_t source_ip, int source_port, + u_int32_t dest_ip, int dest_port, unsigned char *dest_arp, int ifindex) +{ + struct netif *netif = netif_find(server_config.interface); + struct pbuf *p; + struct eth_hdr *ethhdr; + struct ip_hdr *iphdr; + struct udp_hdr *udphdr; + + p = pbuf_alloc(PBUF_LINK, + SIZEOF_ETH_HDR + sizeof(struct ip_hdr) + + sizeof(struct udp_hdr) + sizeof(struct dhcpMessage), + PBUF_RAM); + if (p == NULL) { + DHCPD_LOG(LOG_ERR, "Calloc failed...%s %d", __FUNCTION__, __LINE__); + return -1; + } + + ethhdr = (struct eth_hdr *)p->payload; + iphdr = (struct ip_hdr *)((char *)ethhdr + SIZEOF_ETH_HDR); + udphdr = (struct udp_hdr *)((char *)iphdr + sizeof(struct ip_hdr)); + + ETHADDR32_COPY(ðhdr->dest, dest_arp); + ETHADDR16_COPY(ðhdr->src, server_config.arp); + ethhdr->type = PP_HTONS(ETHTYPE_IP); + + iphdr->src.addr = source_ip; + iphdr->dest.addr = dest_ip; + + IPH_VHL_SET(iphdr, 4, IP_HLEN / 4); + IPH_TOS_SET(iphdr, 0x00); + IPH_LEN_SET(iphdr, htons(IP_HLEN + sizeof(struct udp_hdr) + sizeof(struct dhcpMessage))); + IPH_ID_SET(iphdr, htons(2)); + IPH_OFFSET_SET(iphdr, 0); + IPH_TTL_SET(iphdr, 255); + IPH_PROTO_SET(iphdr, IP_PROTO_UDP); + IPH_CHKSUM_SET(iphdr, 0); + IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, IP_HLEN)); + + udphdr->src = htons(source_port); + udphdr->dest = htons(dest_port); + udphdr->len = htons(sizeof(struct udp_hdr) + sizeof(struct dhcpMessage)); + udphdr->chksum = 0; + + memcpy((char *)udphdr + sizeof(struct udp_hdr), + payload, sizeof(struct dhcpMessage)); + + netif->linkoutput(netif, p); + pbuf_free(p); + + return 0; +} + + +/* Constuct a ip/udp header for a packet, and specify the source and dest hardware address */ +int raw_packet(struct dhcpMessage *payload, u_int32_t source_ip, int source_port, + u_int32_t dest_ip, int dest_port, unsigned char *dest_arp, int ifindex) +{ +#ifndef DHCPD_LOW_LEVEL + int fd; + int result; + struct sockaddr_ll dest; +#ifdef DHCPD_HEAP_REPLACE_STACK + uint32_t interval; + struct udp_dhcp_packet *packet; + packet = malloc(sizeof(*packet)); + if (!packet) + DHCPD_LOG(LOG_ERR, "Calloc failed...%d",__LINE__); +#else + struct udp_dhcp_packet packet; +#endif + + if ((fd = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IP))) < 0) { + DEBUG(LOG_ERR, "socket call failed: %s", strerror(errno)); + return -1; + } + + memset(&dest, 0, sizeof(dest)); +#ifndef DHCPD_HEAP_REPLACE_STACK + memset(&packet, 0, sizeof(packet)); +#else + memset(packet, 0, sizeof(*packet)); +#endif + + dest.sll_family = AF_PACKET; + dest.sll_protocol = htons(ETH_P_IP); + dest.sll_ifindex = ifindex; + dest.sll_halen = 6; + memcpy(dest.sll_addr, dest_arp, 6); + if (bind(fd, (struct sockaddr *)&dest, sizeof(struct sockaddr_ll)) < 0) { + DEBUG(LOG_ERR, "bind call failed: %s", strerror(errno)); +#ifndef DHCPD_LWIP + close(fd); +#else + closesocket(fd); +#ifdef DHCPD_HEAP_REPLACE_STACK + if (packet) { + free(packet); + packet = NULL; + } +#endif +#endif + return -1; + } +#ifdef DHCPD_LWIP + packet->ip._proto = IPPROTO_UDP; + interval = source_ip; + memcpy(&(packet->ip.src), &interval, 4); + + interval = dest_ip; + memcpy(&(packet->ip.dest), &interval, 4); +#else + packet.ip.protocol = IPPROTO_UDP; + packet.ip.saddr = source_ip; + packet.ip.daddr = dest_ip; +#endif +#ifdef DHCPD_LWIP + packet->udp.src = htons(source_port); + packet->udp.dest = htons(dest_port); + packet->udp.len = htons(sizeof(packet->udp) + sizeof(struct dhcpMessage)); /* cheat on the psuedo-header */ +#else + packet.udp.source = htons(source_port); + packet.udp.dest = htons(dest_port); + packet.udp.len = htons(sizeof(packet.udp) + sizeof(struct dhcpMessage)); /* cheat on the psuedo-header */ +#endif + +#ifdef DHCPD_LWIP + packet->ip._len = packet->udp.len; + memcpy(&(packet->data), payload, sizeof(struct dhcpMessage)); + packet->udp.chksum = checksum(packet, sizeof(struct udp_dhcp_packet)); +#else + packet.ip.tot_len = packet.udp.len; + memcpy(&(packet.data), payload, sizeof(struct dhcpMessage)); + packet.udp.check = checksum(&packet, sizeof(struct udp_dhcp_packet)); +#endif + + +#ifdef DHCPD_LWIP + packet->ip._len = htons(sizeof(struct udp_dhcp_packet)); + #define IPVERSION 4 + packet->ip._v_hl = (IPVERSION << 4) |(sizeof(packet->ip) >> 2); + //packet.ip._v_hl = sizeof(packet.ip) >> 2; + //IPH_V(packet.ip) = IPVERSION; + ///**********/packet.ip.version = IPVERSION; + packet->ip._ttl = IP_DEFAULT_TTL; + packet->ip._chksum = checksum(&(packet->ip), sizeof(packet->ip)); +#else + packet.ip.tot_len = htons(sizeof(struct udp_dhcp_packet)); + packet.ip.ihl = sizeof(packet.ip) >> 2; + packet.ip.version = IPVERSION; + packet.ip.ttl = IPDEFTTL; + packet.ip.check = checksum(&(packet.ip), sizeof(packet.ip)); +#endif +#ifdef DHCPD_HEAP_REPLACE_STACK + result = sendto(fd, packet, sizeof(struct udp_dhcp_packet), 0, (struct sockaddr *) &dest, sizeof(dest)); +#else + result = sendto(fd, &packet, sizeof(struct udp_dhcp_packet), 0, (struct sockaddr *) &dest, sizeof(dest)); +#endif + if (result <= 0) { + DEBUG(LOG_ERR, "write on socket failed: %s", strerror(errno)); + } +#ifdef DHCPD_HEAP_REPLACE_STACK + if (packet) + free(packet); +#endif +#ifdef DHCPD_LWIP + closesocket(fd); +#else + close(fd); +#endif + return result; +#else + + return _low_level_dhcp_send(payload, source_ip, source_port, + dest_ip, dest_port, dest_arp, ifindex); + +#endif +} + + +/* Let the kernel do all the work for packet generation */ +int kernel_packet(struct dhcpMessage *payload, u_int32_t source_ip, int source_port, + u_int32_t dest_ip, int dest_port) +{ + int n = 1; + int fd, result; + struct sockaddr_in client; + + if ((fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) + return -1; + + if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *) &n, sizeof(n)) == -1) + return -1; + + memset(&client, 0, sizeof(client)); + client.sin_family = AF_INET; + client.sin_port = htons(source_port); + client.sin_addr.s_addr = source_ip; + + if (bind(fd, (struct sockaddr *)&client, sizeof(struct sockaddr)) == -1) + return -1; + + memset(&client, 0, sizeof(client)); + client.sin_family = AF_INET; + client.sin_port = htons(dest_port); + client.sin_addr.s_addr = dest_ip; + + if (connect(fd, (struct sockaddr *)&client, sizeof(struct sockaddr)) == -1) + return -1; + + result = write(fd, payload, sizeof(struct dhcpMessage)); +#ifdef DHCPD_LWIP + closesocket(fd); +#else + close(fd); +#endif + return result; +} + diff --git a/platform/mcu/xr871/src/net/udhcp/packet.h b/platform/mcu/xr871/src/net/udhcp/packet.h new file mode 100644 index 0000000000..b6739ccaaa --- /dev/null +++ b/platform/mcu/xr871/src/net/udhcp/packet.h @@ -0,0 +1,54 @@ +#ifndef _PACKET_H +#define _PACKET_H + +#ifdef DHCPD_LWIP +#include +#include +#else +#include +#include +#endif +#ifdef DHCPD_USRCFG +#include "dhcpd_cfg.h" +#endif + +struct dhcpMessage { + u_int8_t op; + u_int8_t htype; + u_int8_t hlen; + u_int8_t hops; + u_int32_t xid; + u_int16_t secs; + u_int16_t flags; + u_int32_t ciaddr; + u_int32_t yiaddr; + u_int32_t siaddr; + u_int32_t giaddr; + u_int8_t chaddr[16]; + u_int8_t sname[64]; + u_int8_t file[128]; + u_int32_t cookie; + u_int8_t options[308]; /* 312 - cookie */ +}; + +struct udp_dhcp_packet { +#ifdef DHCPD_LWIP + struct ip_hdr ip; + struct udp_hdr udp; +#else + struct iphdr ip; + struct udphdr udp; +#endif + struct dhcpMessage data; +}; + +void init_header(struct dhcpMessage *packet, char type); +int get_packet(struct dhcpMessage *packet, int fd); +u_int16_t checksum(void *addr, int count); +int raw_packet(struct dhcpMessage *payload, u_int32_t source_ip, int source_port, + u_int32_t dest_ip, int dest_port, unsigned char *dest_arp, int ifindex); +int kernel_packet(struct dhcpMessage *payload, u_int32_t source_ip, int source_port, + u_int32_t dest_ip, int dest_port); + + +#endif diff --git a/platform/mcu/xr871/src/net/udhcp/pidfile.c b/platform/mcu/xr871/src/net/udhcp/pidfile.c new file mode 100644 index 0000000000..837fcaeb17 --- /dev/null +++ b/platform/mcu/xr871/src/net/udhcp/pidfile.c @@ -0,0 +1,69 @@ +/* pidfile.c + * + * Functions to assist in the writing and removing of pidfiles. + * + * Russ Dill Soptember 2001 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "debug.h" + +int pidfile_acquire(char *pidfile) +{ + int pid_fd; + if (pidfile == NULL) return -1; + + pid_fd = open(pidfile, O_CREAT | O_WRONLY, 0644); + if (pid_fd < 0) { + DHCPD_LOG(LOG_ERR, "Unable to open pidfile %s: %s\n", + pidfile, strerror(errno)); + } else { + lockf(pid_fd, F_LOCK, 0); + } + + return pid_fd; +} + + +void pidfile_write_release(int pid_fd) +{ + FILE *out; + + if (pid_fd < 0) return; + + if ((out = fdopen(pid_fd, "w")) != NULL) { + fprintf(out, "%d\n", getpid()); + fclose(out); + } + lockf(pid_fd, F_UNLCK, 0); + close(pid_fd); +} + + +void pidfile_delete(char *pidfile) +{ + if (pidfile) unlink(pidfile); +} + + diff --git a/platform/mcu/xr871/src/net/udhcp/pidfile.h b/platform/mcu/xr871/src/net/udhcp/pidfile.h new file mode 100644 index 0000000000..0e2b148a93 --- /dev/null +++ b/platform/mcu/xr871/src/net/udhcp/pidfile.h @@ -0,0 +1,26 @@ +/* pidfile.h + * + * Functions to assist in the writing and removing of pidfiles. + * + * Russ Dill Soptember 2001 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + + +int pidfile_acquire(char *pidfile); +void pidfile_write_release(int pid_fd); +void pidfile_delete(char *pidfile); + diff --git a/platform/mcu/xr871/src/net/udhcp/samples/README b/platform/mcu/xr871/src/net/udhcp/samples/README new file mode 100644 index 0000000000..51721ea3cd --- /dev/null +++ b/platform/mcu/xr871/src/net/udhcp/samples/README @@ -0,0 +1,11 @@ +The files in this dir are: + + udhcpd.conf: A sample udhpcd configuration file. + + sample.script + sample.nak + sample.deconfig + sample.bound + sample.renew : sample scripts for the udhcpc client. + + simple.script : a simple sample script for the client. diff --git a/platform/mcu/xr871/src/net/udhcp/samples/sample.bound b/platform/mcu/xr871/src/net/udhcp/samples/sample.bound new file mode 100644 index 0000000000..200352672d --- /dev/null +++ b/platform/mcu/xr871/src/net/udhcp/samples/sample.bound @@ -0,0 +1,30 @@ +#!/bin/sh +# Sample udhcpc renew script + +RESOLV_CONF="/etc/udhcpc/resolv.conf" + +[ -n "$broadcast" ] && BROADCAST="broadcast $broadcast" +[ -n "$subnet" ] && NETMASK="netmask $subnet" + +/sbin/ifconfig $interface $ip $BROADCAST $NETMASK + +if [ -n "$router" ] +then + echo "deleting routers" + while /sbin/route del default gw 0.0.0.0 dev $interface + do : + done + + for i in $router + do + /sbin/route add default gw $i dev $interface + done +fi + +echo -n > $RESOLV_CONF +[ -n "$domain" ] && echo domain $domain >> $RESOLV_CONF +for i in $dns +do + echo adding dns $i + echo nameserver $i >> $RESOLV_CONF +done \ No newline at end of file diff --git a/platform/mcu/xr871/src/net/udhcp/samples/sample.deconfig b/platform/mcu/xr871/src/net/udhcp/samples/sample.deconfig new file mode 100644 index 0000000000..b221bcf12b --- /dev/null +++ b/platform/mcu/xr871/src/net/udhcp/samples/sample.deconfig @@ -0,0 +1,4 @@ +#!/bin/sh +# Sample udhcpc deconfig script + +/sbin/ifconfig $interface 0.0.0.0 diff --git a/platform/mcu/xr871/src/net/udhcp/samples/sample.nak b/platform/mcu/xr871/src/net/udhcp/samples/sample.nak new file mode 100644 index 0000000000..f4d08e6691 --- /dev/null +++ b/platform/mcu/xr871/src/net/udhcp/samples/sample.nak @@ -0,0 +1,4 @@ +#!/bin/sh +# Sample udhcpc nak script + +echo Received a NAK: $message diff --git a/platform/mcu/xr871/src/net/udhcp/samples/sample.renew b/platform/mcu/xr871/src/net/udhcp/samples/sample.renew new file mode 100644 index 0000000000..c953e97588 --- /dev/null +++ b/platform/mcu/xr871/src/net/udhcp/samples/sample.renew @@ -0,0 +1,30 @@ +#!/bin/sh +# Sample udhcpc bound script + +RESOLV_CONF="/etc/udhcpc/resolv.conf" + +[ -n "$broadcast" ] && BROADCAST="broadcast $broadcast" +[ -n "$subnet" ] && NETMASK="netmask $subnet" + +/sbin/ifconfig $interface $ip $BROADCAST $NETMASK + +if [ -n "$router" ] +then + echo "deleting routers" + while /sbin/route del default gw 0.0.0.0 dev $interface + do : + done + + for i in $router + do + /sbin/route add default gw $i dev $interface + done +fi + +echo -n > $RESOLV_CONF +[ -n "$domain" ] && echo domain $domain >> $RESOLV_CONF +for i in $dns +do + echo adding dns $i + echo nameserver $i >> $RESOLV_CONF +done \ No newline at end of file diff --git a/platform/mcu/xr871/src/net/udhcp/samples/sample.script b/platform/mcu/xr871/src/net/udhcp/samples/sample.script new file mode 100644 index 0000000000..9b717ac3c5 --- /dev/null +++ b/platform/mcu/xr871/src/net/udhcp/samples/sample.script @@ -0,0 +1,7 @@ +#!/bin/sh +# Currently, we only dispatch according to command. However, a more +# elaborate system might dispatch by command and interface or do some +# common initialization first, especially if more dhcp event notifications +# are added. + +exec /usr/share/udhcpc/sample.$1 diff --git a/platform/mcu/xr871/src/net/udhcp/samples/simple.script b/platform/mcu/xr871/src/net/udhcp/samples/simple.script new file mode 100644 index 0000000000..a52a7f8122 --- /dev/null +++ b/platform/mcu/xr871/src/net/udhcp/samples/simple.script @@ -0,0 +1,39 @@ +#!/bin/sh + +# udhcpc script edited by Tim Riker + +[ -z "$1" ] && echo "Error: should be called from udhcpc" && exit 1 + +RESOLV_CONF="/etc/resolv.conf" +[ -n "$broadcast" ] && BROADCAST="broadcast $broadcast" +[ -n "$subnet" ] && NETMASK="netmask $subnet" + +case "$1" in + deconfig) + /sbin/ifconfig $interface 0.0.0.0 + ;; + + renew|bound) + /sbin/ifconfig $interface $ip $BROADCAST $NETMASK + + if [ -n "$router" ] ; then + echo "deleting routers" + while route del default gw 0.0.0.0 dev $interface ; do + : + done + + for i in $router ; do + route add default gw $i dev $interface + done + fi + + echo -n > $RESOLV_CONF + [ -n "$domain" ] && echo search $domain >> $RESOLV_CONF + for i in $dns ; do + echo adding dns $i + echo nameserver $i >> $RESOLV_CONF + done + ;; +esac + +exit 0 diff --git a/platform/mcu/xr871/src/net/udhcp/samples/udhcpd.conf b/platform/mcu/xr871/src/net/udhcp/samples/udhcpd.conf new file mode 100644 index 0000000000..36cb58c3f8 --- /dev/null +++ b/platform/mcu/xr871/src/net/udhcp/samples/udhcpd.conf @@ -0,0 +1,116 @@ +# Sample udhcpd configuration file (/etc/udhcpd.conf) + +# The start and end of the IP lease block + +start 192.168.0.20 #default: 192.168.0.20 +end 192.168.0.254 #default: 192.168.0.254 + + +# The interface that udhcpd will use + +interface eth0 #default: eth0 + + +# The maximim number of leases (includes addressesd reserved +# by OFFER's, DECLINE's, and ARP conficts + +#max_leases 254 #default: 254 + + +# If remaining is true (default), udhcpd will store the time +# remaining for each lease in the udhcpd leases file. This is +# for embedded systems that cannot keep time between reboots. +# If you set remaining to no, the absolute time that the lease +# expires at will be stored in the dhcpd.leases file. + +#remaining yes #default: yes + + +# The time period at which udhcpd will write out a dhcpd.leases +# file. If this is 0, udhcpd will never automatically write a +# lease file. (specified in seconds) + +#auto_time 7200 #default: 7200 (2 hours) + + +# The amount of time that an IP will be reserved (leased) for if a +# DHCP decline message is received (seconds). + +#decline_time 3600 #default: 3600 (1 hour) + + +# The amount of time that an IP will be reserved (leased) for if an +# ARP conflct occurs. (seconds + +#conflict_time 3600 #default: 3600 (1 hour) + + +# How long an offered address is reserved (leased) in seconds + +#offer_time 60 #default: 60 (1 minute) + +# If a lease to be given is below this value, the full lease time is +# instead used (seconds). + +#min_lease 60 #defult: 60 + + +# The location of the leases file + +#lease_file /var/lib/misc/udhcpd.leases #defualt: /var/lib/misc/udhcpd.leases + +# The location of the pid file +#pidfile /var/run/udhcpd.pid #default: /var/run/udhcpd.pid + +# Everytime udhcpd writes a leases file, the below script will be called. +# Useful for writing the lease file to flash every few hours. + +#notify_file #default: (no script) + +#notify_file dumpleases # <--- usefull for debugging + +# The following are bootp specific options, setable by udhcpd. + +#siaddr 192.168.0.22 #default: 0.0.0.0 + +#sname zorak #default: (none) + +#boot_file /var/nfs_root #default: (none) + +# The remainer of options are DHCP options and can be specifed with the +# keyword 'opt' or 'option'. If an option can take multiple items, such +# as the dns option, they can be listed on the same line, or multiple +# lines. The only option with a default is 'lease'. + +#Examles +opt dns 192.168.10.2 192.168.10.10 +option subnet 255.255.255.0 +opt router 192.168.10.2 +opt wins 192.168.10.10 +option dns 129.219.13.81 # appened to above DNS servers for a total of 3 +option domain local +option lease 864000 # 10 days of seconds + + +# Currently supported options, for more info, see options.c +#subnet +#timezone +#router +#timesvr +#namesvr +#dns +#logsvr +#cookiesvr +#lprsvr +#bootsize +#domain +#swapsvr +#rootpath +#ipttl +#mtu +#broadcast +#wins +#lease +#ntpsrv +#tftp +#bootfile \ No newline at end of file diff --git a/platform/mcu/xr871/src/net/udhcp/script.c b/platform/mcu/xr871/src/net/udhcp/script.c new file mode 100644 index 0000000000..c2889a06ea --- /dev/null +++ b/platform/mcu/xr871/src/net/udhcp/script.c @@ -0,0 +1,228 @@ +/* script.c + * + * Functions to call the DHCP client notification scripts + * + * Russ Dill July 2001 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "options.h" +#include "dhcpd.h" +#include "dhcpc.h" +#include "packet.h" +#include "options.h" +#include "debug.h" + +/* get a rough idea of how long an option will be (rounding up...) */ +static int max_option_length[] = { + [OPTION_IP] = sizeof("255.255.255.255 "), + [OPTION_IP_PAIR] = sizeof("255.255.255.255 ") * 2, + [OPTION_STRING] = 1, + [OPTION_BOOLEAN] = sizeof("yes "), + [OPTION_U8] = sizeof("255 "), + [OPTION_U16] = sizeof("65535 "), + [OPTION_S16] = sizeof("-32768 "), + [OPTION_U32] = sizeof("4294967295 "), + [OPTION_S32] = sizeof("-2147483684 "), +}; + + +static int upper_length(int length, struct dhcp_option *option) +{ + return max_option_length[option->flags & TYPE_MASK] * + (length / option_lengths[option->flags & TYPE_MASK]); +} + + +static int sprintip(char *dest, char *pre, unsigned char *ip) { + return sprintf(dest, "%s%d.%d.%d.%d ", pre, ip[0], ip[1], ip[2], ip[3]); +} + + +/* Fill dest with the text of option 'option'. */ +static void fill_options(char *dest, unsigned char *option, struct dhcp_option *type_p) +{ + int type, optlen; + u_int16_t val_u16; + int16_t val_s16; + u_int32_t val_u32; + int32_t val_s32; + int len = option[OPT_LEN - 2]; + + dest += sprintf(dest, "%s=", type_p->name); + + type = type_p->flags & TYPE_MASK; + optlen = option_lengths[type]; + for(;;) { + switch (type) { + case OPTION_IP_PAIR: + dest += sprintip(dest, "", option); + *(dest++) = '/'; + option += 4; + optlen = 4; + case OPTION_IP: /* Works regardless of host byte order. */ + dest += sprintip(dest, "", option); + break; + case OPTION_BOOLEAN: + dest += sprintf(dest, *option ? "yes " : "no "); + break; + case OPTION_U8: + dest += sprintf(dest, "%u ", *option); + break; + case OPTION_U16: + memcpy(&val_u16, option, 2); + dest += sprintf(dest, "%u ", ntohs(val_u16)); + break; + case OPTION_S16: + memcpy(&val_s16, option, 2); + dest += sprintf(dest, "%d ", ntohs(val_s16)); + break; + case OPTION_U32: + memcpy(&val_u32, option, 4); + dest += sprintf(dest, "%lu ", (unsigned long) ntohl(val_u32)); + break; + case OPTION_S32: + memcpy(&val_s32, option, 4); + dest += sprintf(dest, "%ld ", (long) ntohl(val_s32)); + break; + case OPTION_STRING: + memcpy(dest, option, len); + dest[len] = '\0'; + return; /* Short circuit this case */ + } + option += optlen; + len -= optlen; + if (len <= 0) break; + } +} + + +static char *find_env(const char *prefix, char *defaultstr) +{ + extern char **environ; + char **ptr; + const int len = strlen(prefix); + + for (ptr = environ; *ptr != NULL; ptr++) { + if (strncmp(prefix, *ptr, len) == 0) + return *ptr; + } + return defaultstr; +} + + +/* put all the paramaters into an environment */ +static char **fill_envp(struct dhcpMessage *packet) +{ + int num_options = 0; + int i, j; + char **envp; + unsigned char *temp; + char over = 0; + + if (packet == NULL) + num_options = 0; + else { + for (i = 0; options[i].code; i++) + if (get_option(packet, options[i].code)) + num_options++; + if (packet->siaddr) num_options++; + if ((temp = get_option(packet, DHCP_OPTION_OVER))) + over = *temp; + if (!(over & FILE_FIELD) && packet->file[0]) num_options++; + if (!(over & SNAME_FIELD) && packet->sname[0]) num_options++; + } + + envp = xmalloc((num_options + 5) * sizeof(char *)); + envp[0] = xmalloc(sizeof("interface=") + strlen(client_config.interface)); + sprintf(envp[0], "interface=%s", client_config.interface); + envp[1] = find_env("PATH", "PATH=/bin:/usr/bin:/sbin:/usr/sbin"); + envp[2] = find_env("HOME", "HOME=/"); + + if (packet == NULL) { + envp[3] = NULL; + return envp; + } + + envp[3] = xmalloc(sizeof("ip=255.255.255.255")); + sprintip(envp[3], "ip=", (unsigned char *) &packet->yiaddr); + for (i = 0, j = 4; options[i].code; i++) { + if ((temp = get_option(packet, options[i].code))) { + envp[j] = xmalloc(upper_length(temp[OPT_LEN - 2], &options[i]) + strlen(options[i].name) + 2); + fill_options(envp[j], temp, &options[i]); + j++; + } + } + if (packet->siaddr) { + envp[j] = xmalloc(sizeof("siaddr=255.255.255.255")); + sprintip(envp[j++], "siaddr=", (unsigned char *) &packet->siaddr); + } + if (!(over & FILE_FIELD) && packet->file[0]) { + /* watch out for invalid packets */ + packet->file[sizeof(packet->file) - 1] = '\0'; + envp[j] = xmalloc(sizeof("boot_file=") + strlen(packet->file)); + sprintf(envp[j++], "boot_file=%s", packet->file); + } + if (!(over & SNAME_FIELD) && packet->sname[0]) { + /* watch out for invalid packets */ + packet->sname[sizeof(packet->sname) - 1] = '\0'; + envp[j] = xmalloc(sizeof("sname=") + strlen(packet->sname)); + sprintf(envp[j++], "sname=%s", packet->sname); + } + envp[j] = NULL; + return envp; +} + + +/* Call a script with a par file and env vars */ +void run_script(struct dhcpMessage *packet, const char *name) +{ + int pid; + char **envp; + + if (client_config.script == NULL) + return; + + /* call script */ + pid = fork(); + if (pid) { + waitpid(pid, NULL, 0); + return; + } else if (pid == 0) { + envp = fill_envp(packet); + + /* close fd's? */ + + /* exec script */ + DEBUG(LOG_INFO, "execle'ing %s", client_config.script); + execle(client_config.script, client_config.script, + name, NULL, envp); + DHCPD_LOG(LOG_ERR, "script %s failed: %s", + client_config.script, strerror(errno)); + exit(1); + } +} diff --git a/platform/mcu/xr871/src/net/udhcp/script.h b/platform/mcu/xr871/src/net/udhcp/script.h new file mode 100644 index 0000000000..87a20cc170 --- /dev/null +++ b/platform/mcu/xr871/src/net/udhcp/script.h @@ -0,0 +1,6 @@ +#ifndef _SCRIPT_H +#define _SCRIPT_H + +void run_script(struct dhcpMessage *packet, const char *name); + +#endif diff --git a/platform/mcu/xr871/src/net/udhcp/serverpacket.c b/platform/mcu/xr871/src/net/udhcp/serverpacket.c new file mode 100644 index 0000000000..df20cfe3b8 --- /dev/null +++ b/platform/mcu/xr871/src/net/udhcp/serverpacket.c @@ -0,0 +1,428 @@ +/* serverpacket.c + * + * Constuct and send DHCP server packets + * + * Russ Dill July 2001 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#ifdef DHCPD_LWIP +#include +#else +#include +#include +#include +#endif +#include +#include +#include "packet.h" +#include "debug.h" +#include "dhcpd.h" +#include "options.h" +#include "leases.h" +#ifdef DHCPD_TIMEALT +#include "dhcp_time.h" +#else +#include +#endif +/* send a packet to giaddr using the kernel ip stack */ +static int send_packet_to_relay(struct dhcpMessage *payload) +{ + DEBUG(LOG_INFO, "Forwarding packet to relay"); + + return kernel_packet(payload, server_config.server, SERVER_PORT, + payload->giaddr, SERVER_PORT); +} + + +/* send a packet to a specific arp address and ip address by creating our own ip packet */ +static int send_packet_to_client(struct dhcpMessage *payload, int force_broadcast) +{ + unsigned char *chaddr; + u_int32_t ciaddr; + + if (force_broadcast) { + DEBUG(LOG_INFO, "broadcasting packet to client (NAK)"); + ciaddr = INADDR_BROADCAST; + chaddr = MAC_BCAST_ADDR; + } else if (payload->ciaddr) { + DEBUG(LOG_INFO, "unicasting packet to client ciaddr"); + ciaddr = payload->ciaddr; + chaddr = payload->chaddr; + } else if (ntohs(payload->flags) & BROADCAST_FLAG) { + DEBUG(LOG_INFO, "broadcasting packet to client (requested)"); + ciaddr = INADDR_BROADCAST; + chaddr = MAC_BCAST_ADDR; + } else { + DEBUG(LOG_INFO, "unicasting packet to client yiaddr"); + ciaddr = payload->yiaddr; + chaddr = payload->chaddr; + } + return raw_packet(payload, server_config.server, SERVER_PORT, + ciaddr, CLIENT_PORT, chaddr, server_config.ifindex); +} + + +/* send a dhcp packet, if force broadcast is set, the packet will be broadcast to the client */ +static int send_packet(struct dhcpMessage *payload, int force_broadcast) +{ + int ret; + + if (payload->giaddr) + ret = send_packet_to_relay(payload); + else ret = send_packet_to_client(payload, force_broadcast); + return ret; +} + + +static void init_packet(struct dhcpMessage *packet, struct dhcpMessage *oldpacket, char type) +{ + init_header(packet, type); + packet->xid = oldpacket->xid; + memcpy(packet->chaddr, oldpacket->chaddr, 16); + packet->flags = oldpacket->flags; + packet->giaddr = oldpacket->giaddr; + packet->ciaddr = oldpacket->ciaddr; + add_simple_option(packet->options, DHCP_SERVER_ID, server_config.server); +} + + +/* add in the bootp options */ +static void add_bootp_options(struct dhcpMessage *packet) +{ + packet->siaddr = server_config.siaddr; + if (server_config.sname) + strncpy((char *)packet->sname, server_config.sname, sizeof(packet->sname) - 1); + if (server_config.boot_file) + strncpy((char *)packet->file, server_config.boot_file, sizeof(packet->file) - 1); +} + + +/* send a DHCP OFFER to a DHCP DISCOVER */ +int sendOffer(struct dhcpMessage *oldpacket) +{ +#ifdef DHCPD_HEAP_REPLACE_STACK + struct dhcpMessage *packet; +#else + struct dhcpMessage packet; +#endif + struct dhcpOfferedAddr *lease = NULL; + u_int32_t req_align, lease_time_align = server_config.lease; + unsigned char *req, *lease_time; + struct option_set *curr; + //struct in_addr addr; + +#ifdef DHCPD_HEAP_REPLACE_STACK + packet = calloc(1,sizeof(struct dhcpMessage)); + if (!packet) { + DHCPD_LOG(LOG_ERR, "Calloc failed...%d",__LINE__); + return -1; + } + init_packet(packet, oldpacket, DHCPOFFER); +#else + init_packet(&packet, oldpacket, DHCPOFFER); +#endif + + /* ADDME: if static, short circuit */ + /* the client is in our lease/offered table */ + if ((lease = find_lease_by_chaddr(oldpacket->chaddr))) { + if (!lease_expired(lease)) + lease_time_align = lease->expires - time(0); +#ifdef DHCPD_HEAP_REPLACE_STACK + packet->yiaddr = lease->yiaddr; +#else + packet.yiaddr = lease->yiaddr; +#endif + /* Or the client has a requested ip */ + } else if ((req = get_option(oldpacket, DHCP_REQUESTED_IP)) && + + /* Don't look here (ugly hackish thing to do) */ + memcpy(&req_align, req, 4) && + + /* and the ip is in the lease range */ + ntohl(req_align) >= ntohl(server_config.start) && + ntohl(req_align) <= ntohl(server_config.end) && + + /* and its not already taken/offered */ /* ADDME: check that its not a static lease */ + ((!(lease = find_lease_by_yiaddr(req_align)) || + + /* or its taken, but expired */ /* ADDME: or maybe in here */ + lease_expired(lease)))) { +#ifdef DHCPD_HEAP_REPLACE_STACK + packet->yiaddr = req_align; /* FIXME: oh my, is there a host using this IP? */ +#else + packet.yiaddr = req_align; /* FIXME: oh my, is there a host using this IP? */ +#endif + + /* otherwise, find a free IP */ /*ADDME: is it a static lease? */ + } else { +#ifdef DHCPD_HEAP_REPLACE_STACK + packet->yiaddr = find_address(0); + /* try for an expired lease */ + if (!packet->yiaddr) packet->yiaddr = find_address(1); +#else + packet.yiaddr = find_address(0); + + /* try for an expired lease */ + if (!packet.yiaddr) packet.yiaddr = find_address(1); +#endif + } +#ifdef DHCPD_HEAP_REPLACE_STACK + if(!(packet->yiaddr)) { + if (packet != NULL) + free(packet); + +#else + if(!packet.yiaddr) { +#endif + DHCPD_LOG(LOG_WARNING, "no IP addresses to give -- OFFER abandoned"); + return -1; + } +#ifdef DHCPD_HEAP_REPLACE_STACK + if (!add_lease(packet->chaddr, packet->yiaddr, server_config.offer_time)) { + if (packet != NULL) { + free(packet); + packet = NULL; + } +#else + if (!add_lease(packet.chaddr, packet.yiaddr, server_config.offer_time)) { +#endif + DHCPD_LOG(LOG_WARNING, "lease pool is full -- OFFER abandoned"); + return -1; + } + + if ((lease_time = get_option(oldpacket, DHCP_LEASE_TIME))) { + memcpy(&lease_time_align, lease_time, 4); + lease_time_align = ntohl(lease_time_align); + if (lease_time_align > server_config.lease) + lease_time_align = server_config.lease; + } + + /* Make sure we aren't just using the lease time from the previous offer */ + if (lease_time_align < server_config.min_lease) + lease_time_align = server_config.lease; + /* ADDME: end of short circuit */ +#ifdef DHCPD_HEAP_REPLACE_STACK + struct netif *netif = netif_find(server_config.interface); + + add_simple_option(packet->options, DHCP_LEASE_TIME, htonl(lease_time_align)); +#ifdef __CONFIG_LWIP_V1 + add_simple_option(packet->options, DHCP_SUBNET, ip4_addr_get_u32(&netif->netmask)); + add_simple_option(packet->options, DHCP_ROUTER, ip4_addr_get_u32(&netif->gw)); + add_simple_option(packet->options, DHCP_DNS_SERVER, ip4_addr_get_u32(&netif->ip_addr)); +#elif LWIP_IPV4 /* now only for IPv4 */ + add_simple_option(packet->options, DHCP_SUBNET, ip4_addr_get_u32(ip_2_ip4(&netif->netmask))); + add_simple_option(packet->options, DHCP_ROUTER, ip4_addr_get_u32(ip_2_ip4(&netif->gw))); + add_simple_option(packet->options, DHCP_DNS_SERVER, ip4_addr_get_u32(ip_2_ip4(&netif->ip_addr))); +#else + #error "IPv4 not support!" +#endif + +#else + add_simple_option(packet.options, DHCP_LEASE_TIME, htonl(lease_time_align)); +#endif + + curr = server_config.options; + while (curr) { + if (curr->data[OPT_CODE] != DHCP_LEASE_TIME) +#ifdef DHCPD_HEAP_REPLACE_STACK + add_option_string(packet->options, curr->data); +#else + add_option_string(packet.options, curr->data); +#endif + curr = curr->next; + } +#ifdef DHCPD_HEAP_REPLACE_STACK + add_bootp_options(packet); + + //addr.s_addr = packet->yiaddr; + DHCPD_LOG(LOG_INFO, "sending OFFER of %s", inet_ntoa(packet->yiaddr)); + int ret = send_packet(packet, 0); + if (packet != NULL) { + free(packet); + packet = NULL; + } + return ret; +#else + add_bootp_options(&packet); + + addr.s_addr = packet.yiaddr; + DHCPD_LOG(LOG_INFO, "sending OFFER of %s", inet_ntoa(addr)); + return send_packet(&packet, 0); +#endif +} + + +int sendNAK(struct dhcpMessage *oldpacket) +{ +#ifdef DHCPD_HEAP_REPLACE_STACK + struct dhcpMessage *packet; +#else + struct dhcpMessage packet; +#endif +#ifdef DHCPD_HEAP_REPLACE_STACK + packet = calloc(1,sizeof(struct dhcpMessage)); + if (!packet) + DHCPD_LOG(LOG_ERR, "Calloc failed...%d",__LINE__); + init_packet(packet, oldpacket, DHCPNAK); +#else + init_packet(&packet, oldpacket, DHCPNAK); +#endif + + DEBUG(LOG_INFO, "sending NAK"); +#ifdef DHCPD_HEAP_REPLACE_STACK + int ret = send_packet(packet, 1); + if (packet != NULL) + free(packet); + return ret; +#else + return send_packet(&packet, 1); +#endif +} + + +int sendACK(struct dhcpMessage *oldpacket, u_int32_t yiaddr) +{ +#ifdef DHCPD_HEAP_REPLACE_STACK + struct dhcpMessage *packet; +#else + struct dhcpMessage packet; +#endif + struct option_set *curr; + unsigned char *lease_time; + u_int32_t lease_time_align = server_config.lease; + //struct in_addr addr; + +#ifdef DHCPD_HEAP_REPLACE_STACK + packet = calloc(1,sizeof(struct dhcpMessage)); + if (!packet) + DHCPD_LOG(LOG_ERR, "Calloc failed...%d",__LINE__); + init_packet(packet, oldpacket, DHCPACK); + packet->yiaddr = yiaddr; +#else + init_packet(&packet, oldpacket, DHCPACK); + + packet.yiaddr = yiaddr; +#endif + + if ((lease_time = get_option(oldpacket, DHCP_LEASE_TIME))) { + memcpy(&lease_time_align, lease_time, 4); + lease_time_align = ntohl(lease_time_align); + if (lease_time_align > server_config.lease) + lease_time_align = server_config.lease; + else if (lease_time_align < server_config.min_lease) + lease_time_align = server_config.lease; + } +#ifdef DHCPD_HEAP_REPLACE_STACK + struct netif *netif = netif_find(server_config.interface); + + add_simple_option(packet->options, DHCP_LEASE_TIME, htonl(lease_time_align)); +#ifdef __CONFIG_LWIP_V1 + add_simple_option(packet->options, DHCP_SUBNET, ip4_addr_get_u32(&netif->netmask)); + add_simple_option(packet->options, DHCP_ROUTER, ip4_addr_get_u32(&netif->gw)); + add_simple_option(packet->options, DHCP_DNS_SERVER, ip4_addr_get_u32(&netif->ip_addr)); +#elif LWIP_IPV4 /* now only for IPv4 */ + add_simple_option(packet->options, DHCP_SUBNET, ip4_addr_get_u32(ip_2_ip4(&netif->netmask))); + add_simple_option(packet->options, DHCP_ROUTER, ip4_addr_get_u32(ip_2_ip4(&netif->gw))); + add_simple_option(packet->options, DHCP_DNS_SERVER, ip4_addr_get_u32(ip_2_ip4(&netif->ip_addr))); +#else + #error "IPv4 not support!" +#endif + +#else + add_simple_option(packet.options, DHCP_LEASE_TIME, htonl(lease_time_align)); +#endif + curr = server_config.options; + while (curr) { + if (curr->data[OPT_CODE] != DHCP_LEASE_TIME) +#ifdef DHCPD_HEAP_REPLACE_STACK + add_option_string(packet->options, curr->data); +#else + add_option_string(packet.options, curr->data); +#endif + curr = curr->next; + } +#ifdef DHCPD_HEAP_REPLACE_STACK + add_bootp_options(packet); + + //addr.s_addr = packet->yiaddr; + DHCPD_LOG(LOG_INFO, "sending ACK to %s", inet_ntoa(packet->yiaddr)); + int ret = 0; + if (send_packet(packet, 0) < 0) + ret = -1; + + add_lease(packet->chaddr, packet->yiaddr, lease_time_align); + + if (packet) + free(packet); + return ret; +#else + add_bootp_options(&packet); + + //addr.s_addr = packet.yiaddr; + DHCPD_LOG(LOG_INFO, "sending ACK to %s", inet_ntoa(packet->yiaddr)); + + if (send_packet(&packet, 0) < 0) + return -1; + + add_lease(packet.chaddr, packet.yiaddr, lease_time_align); + return 0; +#endif +} + + +int send_inform(struct dhcpMessage *oldpacket) +{ +#ifdef DHCPD_HEAP_REPLACE_STACK + struct dhcpMessage *packet; +#else + struct dhcpMessage packet; +#endif + struct option_set *curr; +#ifdef DHCPD_HEAP_REPLACE_STACK + packet = calloc(1,sizeof(struct dhcpMessage)); + if (!packet) + DHCPD_LOG(LOG_ERR, "Calloc failed...%d",__LINE__); + init_packet(packet, oldpacket, DHCPACK); +#else + init_packet(&packet, oldpacket, DHCPACK); +#endif + + curr = server_config.options; + while (curr) { + if (curr->data[OPT_CODE] != DHCP_LEASE_TIME) +#ifdef DHCPD_HEAP_REPLACE_STACK + add_option_string(packet->options, curr->data); +#else + add_option_string(packet.options, curr->data); +#endif + curr = curr->next; + } +#ifdef DHCPD_HEAP_REPLACE_STACK + add_bootp_options(packet); + + int ret = send_packet(packet, 0); + if (packet) + free(packet); + return ret; +#else + add_bootp_options(&packet); + + return send_packet(&packet, 0); +#endif +} + + + diff --git a/platform/mcu/xr871/src/net/udhcp/serverpacket.h b/platform/mcu/xr871/src/net/udhcp/serverpacket.h new file mode 100644 index 0000000000..5a4fb27684 --- /dev/null +++ b/platform/mcu/xr871/src/net/udhcp/serverpacket.h @@ -0,0 +1,11 @@ +#ifndef _SERVERPACKET_H +#define _SERVERPACKET_H + + +int sendOffer(struct dhcpMessage *oldpacket); +int sendNAK(struct dhcpMessage *oldpacket); +int sendACK(struct dhcpMessage *oldpacket, u_int32_t yiaddr); +int send_inform(struct dhcpMessage *oldpacket); + + +#endif diff --git a/platform/mcu/xr871/src/net/udhcp/socket.c b/platform/mcu/xr871/src/net/udhcp/socket.c new file mode 100644 index 0000000000..5a517d43eb --- /dev/null +++ b/platform/mcu/xr871/src/net/udhcp/socket.c @@ -0,0 +1,211 @@ +/* + * socket.c -- DHCP server client/server socket creation + * + * udhcp client/server + * Copyright (C) 1999 Matthew Ramsay + * Chris Trew + * + * Rewrite by Russ Dill July 2001 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include + +#ifdef DHCPD_LWIP +#include +#else +#include +#include +#include +#include +#include +#include +#endif + +#ifndef DHCPD_FREERTOS +#include +#endif + +#include +#include + +#if __GLIBC__ >=2 && __GLIBC_MINOR >= 1 +#include +#include +#elif __LINUX +#include +#include +#include +#endif + +#ifdef DHCPD_LWIP +#include "netif/etharp.h" +#include "lwip/sockets.h" +#include +#include "dhcpd.h" +#endif +#include "debug.h" + +#ifdef DHCPD_USRCFG +#include "dhcpd_cfg.h" +#endif + +int read_interface(char *interface, int *ifindex, u_int32_t *addr, unsigned char *arp) +{ +#ifndef DHCPD_LWIP + int fd; + struct ifreq ifr; + struct sockaddr_in *our_ip; + + memset(&ifr, 0, sizeof(struct ifreq)); + if((fd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) >= 0) { + ifr.ifr_addr.sa_family = AF_INET; + strcpy(ifr.ifr_name, interface); + + if (addr) { + if (ioctl(fd, SIOCGIFADDR, &ifr) == 0) { + our_ip = (struct sockaddr_in *) &ifr.ifr_addr; + *addr = our_ip->sin_addr.s_addr; + DEBUG(LOG_INFO, "%s (our ip) = %s", ifr.ifr_name, inet_ntoa(our_ip->sin_addr)); + } else { + DHCPD_LOG(LOG_ERR, "SIOCGIFADDR failed, is the interface up and configured?: %s", + strerror(errno)); + return -1; + } + } + + if (ioctl(fd, SIOCGIFINDEX, &ifr) == 0) { + DEBUG(LOG_INFO, "adapter index %d", ifr.ifr_ifindex); + *ifindex = ifr.ifr_ifindex; + } else { + DHCPD_LOG(LOG_ERR, "SIOCGIFINDEX failed!: %s", strerror(errno)); + return -1; + } + if (ioctl(fd, SIOCGIFHWADDR, &ifr) == 0) { + memcpy(arp, ifr.ifr_hwaddr.sa_data, 6); + DEBUG(LOG_INFO, "adapter hardware address %02x:%02x:%02x:%02x:%02x:%02x", + arp[0], arp[1], arp[2], arp[3], arp[4], arp[5]); + } else { + DHCPD_LOG(LOG_ERR, "SIOCGIFHWADDR failed!: %s", strerror(errno)); + return -1; + } + } else { + DHCPD_LOG(LOG_ERR, "socket failed!: %s", strerror(errno)); + return -1; + } + close(fd); +#else + struct netif *netif = netif_find(interface); + if (netif != NULL) + *ifindex = netif->num; + else + return -1; + + memcpy(addr, &(netif->ip_addr), 4); + memcpy(arp, netif->hwaddr, 6); + DHCPD_LOG(LOG_INFO, "Obtain addr :%s,hwaddr:[%02x:%02x:%02x:%02x:%02x:%02x]",inet_ntoa(*addr),arp[0],arp[1],arp[2],arp[3],arp[4],arp[5]); +#endif + return 0; +} + + +int listen_socket(unsigned int ip, int port, char *inf) +{ +#ifndef DHCPD_LWIP + struct ifreq interface; +#endif + int fd; + struct sockaddr_in addr; + int n = 1; + + DEBUG(LOG_INFO, "Opening listen socket on 0x%08x:%d %s\n", ip, port, inf); + if ((fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) { + DEBUG(LOG_ERR, "socket call failed: %s", strerror(errno)); + return -1; + } + + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = htons(port); + addr.sin_addr.s_addr = ip; + + if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *) &n, sizeof(n)) == -1) { +#ifdef DHCPD_LWIP + closesocket(fd); +#else + close(fd); +#endif + return -1; + } + if (setsockopt(fd, SOL_SOCKET, SO_BROADCAST, (char *) &n, sizeof(n)) == -1) { +#ifdef DHCPD_LWIP + closesocket(fd); +#else + close(fd); +#endif + return -1; + } +#ifndef DHCPD_LWIP + strncpy(interface.ifr_ifrn.ifrn_name, inf, IFNAMSIZ); + if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE,(char *)&interface, sizeof(interface)) < 0) { + close(fd); + return -1; + } +#endif + if (bind(fd, (struct sockaddr *)&addr, sizeof(struct sockaddr)) == -1) { +#ifdef DHCPD_LWIP + closesocket(fd); +#else + close(fd); +#endif + return -1; + } + + return fd; +} + +#define ETH_P_IP ETHTYPE_IP +int raw_socket(int ifindex) +{ +#if 0 + int fd; + struct sockaddr_ll sock; + + DEBUG(LOG_INFO, "Opening raw socket on ifindex %d\n", ifindex); + if ((fd = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IP))) < 0) { + DEBUG(LOG_ERR, "socket call failed: %s", strerror(errno)); + return -1; + } + + sock.sll_family = AF_PACKET; + sock.sll_protocol = htons(ETH_P_IP); + sock.sll_ifindex = ifindex; + if (bind(fd, (struct sockaddr *) &sock, sizeof(sock)) < 0) { + DEBUG(LOG_ERR, "bind call failed: %s", strerror(errno)); +#ifdef DHCPD_LWIP + closesocket(fd); +#else + close(fd); +#endif + return -1; + } + + return fd; + +#endif + return 0; +} + diff --git a/platform/mcu/xr871/src/net/udhcp/socket.h b/platform/mcu/xr871/src/net/udhcp/socket.h new file mode 100644 index 0000000000..333994b8f7 --- /dev/null +++ b/platform/mcu/xr871/src/net/udhcp/socket.h @@ -0,0 +1,9 @@ +/* socket.h */ +#ifndef _SOCKET_H +#define _SOCKET_H + +int read_interface(char *interface, int *ifindex, u_int32_t *addr, unsigned char *arp); +int listen_socket(unsigned int ip, int port, char *inf); +int raw_socket(int ifindex); + +#endif diff --git a/platform/mcu/xr871/src/net/udhcp/udhcp.mk b/platform/mcu/xr871/src/net/udhcp/udhcp.mk new file mode 100644 index 0000000000..5a720e5d1b --- /dev/null +++ b/platform/mcu/xr871/src/net/udhcp/udhcp.mk @@ -0,0 +1,25 @@ +NAME := udhcp + +$(NAME)_TYPE := kernel + +$(NAME)_SOURCES := usr_dhcpd.c \ + arpping.c \ + files.c \ + leases.c \ + serverpacket.c \ + options.c \ + socket.c \ + packet.c \ + dhcp_time.c \ + dns_server.c + +VER := 0.9.8 +$(NAME)_CFLAGS += -DVERSION='"$(VER)"' -DXR_DHCPD +$(NAME)_CFLAGS += -DDHCPD_TIMEALT \ + -DDHCPD_LWIP \ + -DDHCPD_HEAP_REPLACE_STACK \ + -DDHCPD_USRCFG \ + -DDHCPD_FREERTOS \ + -DDHCPD_ICMPPING \ + -DDHCPD_LOW_LEVEL \ + -DDHCPD_DNS diff --git a/platform/mcu/xr871/src/net/udhcp/udhcpc.8 b/platform/mcu/xr871/src/net/udhcp/udhcpc.8 new file mode 100644 index 0000000000..752a736c0d --- /dev/null +++ b/platform/mcu/xr871/src/net/udhcp/udhcpc.8 @@ -0,0 +1,208 @@ +.TH UDHCPC 8 2001-09-26 GNU/Linux "GNU/Linux Administrator's Manual" +.SH NAME +udhcpc \- very small DHCP client +.SH SYNOPSIS +.B udhcpc +.RI [ OPTION ]... +.SH DESCRIPTION +The udhcp client negotiates a lease with the DHCP server and +executes a script when it is obtained or lost. +.SH OPTIONS +.TP +.BI \-c\ CLIENTID ,\ \-\-clientid= CLIENTID +Send the client identifier +.IR CLIENTID . +.TP +.BR -f ,\ \-\-foreground +Do not fork after obtaining a lease. +.TP +.BI \-H\ HOSTNAME ,\ \-\-hostname= HOSTNAME +Send the client hostname +.IR HOSTNAME . +.TP +.BI \-h\ HOSTNAME +Alias for -H +.IR HOSTNAME . +.TP +.BI \-i\ INTERFACE ,\ \-\-interface= INTERFACE +Configure +.IR INTERFACE . +.TP +.BR -n ,\ \-\-now +Exit with failure if a lease cannot be obtained. +.TP +.BI \-p\ FILE ,\ \-\-pidfile= FILE +Write the process ID of the daemon to +.IR FILE . +.TP +.BR -q ,\ \-\-quit +Exit after obtaining a lease. +.TP +.BI \-r\ ADDRESS ,\ \-\-request= ADDRESS +Request IP address +.IR ADDRESS . +.TP +.BI \-s\ FILE ,\ \-\-script= FILE +Use script +.IR FILE . +.TP +.BR -v ,\ \-\-version +Display version. +.SH USAGE +When an event occurs, +.B udhcpc +executes a script. There are four possible arguments to this +script: +.TP +.B deconfig +.B deconfig +is used when +.B udhcpc +starts, and when a lease is lost. The script should put the +interface in an up, but deconfigured, state. +.TP +.B bound +.B bound +is used when +.B udhcpc +moves from an unbound to a bound state. The script should +configure the interface and set any other relevant parameters +(e.g., default gateway, dns server, etc.). +.TP +.B renew +.B renew +is used when +.B udhcpc +when a lease is renewed. The interface is already +configured, so the IP address will not change. Other parameters +(e.g., default gateway, subnet mask, dns server) may. +.TP +.B nak +.B nak +is used when +.B udhcpc +receieves a NAK packet from the server. The +enviromental variable +.B $message +will contain the reason for the +NAK message if the server included one. Processing this message +is optional, as the script will also be called with deconfig if +need be. +.PP +Parameters are passed to the script via the following environment +variables: +.TP +.B HOME +The inherited HOME, or "/" if it is unset. +.TP +.B PATH +The inherited PATH, or "/bin:/usr/bin:/sbin:/usr/sbin" if it is +unset. +.TP +.B interface +The interface. +.TP +.B ip +The client IP address. +.TP +.B siaddr +The BOOTP next server option. +.TP +.B sname +The BOOTP server name option. +.TP +.B boot_file +The BOOTP boot file option. +.TP +.B subnet +The subnet mask. +.TP +.B timezone +The timezone offset from UTC in seconds. +.TP +.B router +The list of routers. +.TP +.B timesvr +The list of time servers. +.TP +.B namesvr +The list of IEN 116 name servers. +.TP +.B dns +The list of DNS servers. +.TP +.B logsvr +The list of MIT-LCS UDP log servers. +.TP +.B cookiesvr +The list of RFC 865 cookie servers. +.TP +.B lprsvr +The list of LPR servers. +.TP +.B hostname +The host name. +.TP +.B bootsize +The length in 512-octet blocks of the bootfile. +.TP +.B domain +The domain name of the network. +.TP +.B swapsvr +The client's swap server. +.TP +.B rootpath +The path of the client's root dist. +.TP +.B ipttl +The TTL. +.TP +.B mtu +The MTU. +.TP +.B broadcast +The broadcast address. +.TP +.B ntpsrv +The list of NTP servers. +.TP +.B wins +The list of WINS servers. +.TP +.B lease +The lease time in seconds. +.TP +.B dhcptype +The DHCP message type (safely ignored). +.TP +.B serverid +The server IP address. +.TP +.B message +Reason for a DHCPNAK. +.TP +.B tftp +The TFTP server name. +.TP +.B bootfile +The bootfile name. +.SH FILES +.TP +.I /usr/share/udhcpc/default.script +Script run when leases are obtained or lost. +.SH NOTES +.B udhcpc +responds to the following signals: +.TP +.B SIGUSR1 +This signal causes +.B udhcpc +to renew the current lease or, if it does not have one, obtain a +new lease. +.TP +.B SIGUSR2 +This signal caused +.B udhcpc +to release the current lease. diff --git a/platform/mcu/xr871/src/net/udhcp/udhcpd.8 b/platform/mcu/xr871/src/net/udhcp/udhcpd.8 new file mode 100644 index 0000000000..be8fac888c --- /dev/null +++ b/platform/mcu/xr871/src/net/udhcp/udhcpd.8 @@ -0,0 +1,17 @@ +.TH UDHCPD 8 2001-09-27 GNU/Linux "GNU/Linux Administrator's Manual" +.SH NAME +udhcpd \- very small DHCP server +.SH SYNOPSIS +.B udhcpd +.SH DESCRIPTION +The udhcp server negotiates leases with DHCP clients. +.SH FILES +.TP +.I /etc/udhcpd.conf +Configuration file. +.TP +.I /var/lib/misc/udhcpd.leases +Lease information file. +.SH SEE ALSO +.BR dumpleases (1), +.BR udhcpd.conf (8). diff --git a/platform/mcu/xr871/src/net/udhcp/udhcpd.conf.5 b/platform/mcu/xr871/src/net/udhcp/udhcpd.conf.5 new file mode 100644 index 0000000000..b86a9f51f0 --- /dev/null +++ b/platform/mcu/xr871/src/net/udhcp/udhcpd.conf.5 @@ -0,0 +1,164 @@ +.TH UDHCPD.CONF 5 2001-09-26 GNU/Linux "GNU/Linux Administrator's Manual" +.SH NAME +udhcpd.conf \- udhcp server configuration file +.SH DESCRIPTION +The file +.I /etc/udhcpd.conf +contains configuration information specific to the udhcp server. +It should contain one configuration keyword per line, followed by +appropriate configuration information. +.SH OPTIONS +.TP +.BI start\ ADDRESS +The starting address of the IP lease block is +.IR ADDRESS . +The default is +.BR 192.168.0.20 . +.TP +.BI end\ ADDRESS +The ending address of the IP lease block is +.IR ADDRESS . +The default is +.BR 192.168.0.254 . +.TP +.BI interface\ INTERFACE +The udhcp server should listen on +.IR INTERFACE . +The default is +.BR eth0 . +.TP +.BI max_leases\ LEASES +Offer at most +.I LEASES +leases (including those reserved by OFFERs, DECLINEs, and ARP +conflicts). The default is +.BR 254 . +.TP +.BI remaining\ REMAINING +If +.I REMAINING +is +.BR yes , +store the time remaining for each lease. If it is +.BR no , +store the expiration time for each lease. The default is +.BR yes . +.TP +.BI auto_time\ SECONDS +Write the lease information to a file every +.I SECONDS +seconds. The default is +.BR 7200 . +.TP +.BI decline_time\ SECONDS +Reserve an IP for +.I SECONDS +seconds if a DHCP decline message is received. The default is +.BR 3600 . +.TP +.BI conflict_time\ SECONDS +Reserve an IP for +.I SECONDS +seconds if an ARP conflict occurs. The default is +.BR 3600 . +.TP +.BI offer_time\ SECONDS +Reserve an IP for +.I SECONDS +seconds if it is offered. The default is +.BR 60 . +.TP +.BI min_lease\ SECONDS +Reserve an IP for the full lease time if the lease to be given is less than +.I SECONDS +seconds. The default is +.BR 60 . +.TP +.BI lease_file\ FILE +Write the lease information to +.IR FILE . +The default is +.BR /var/lib/misc/udhcpd.leases . +.TP +.BI pidfile\ FILE +Write the process ID to +.IR FILE . +The default is +.BR /var/run/udhcpd.pid . +.TP +.BI notify_file\ FILE +Execute +.I FILE +after the lease information is written. By default, no file is executed. +.TP +.BI siaddr\ ADDRESS +BOOTP specific option. The default is +.BR 0.0.0.0 . +.TP +.BI sname\ NAME +BOOTP specific option. There is no default. +.TP +.BI boot_file\ FILE +BOOTP specific option. There is no default. +.TP +.BI option\ OPTION +DHCP specific option. +.RS +.TP +.BI subnet\ ADDRESS +.TP +.BI timezone\ OFFSET +.TP +.BI router\ ADDRESS... +.TP +.BI timesvr\ ADDRESS... +.TP +.BI namesvr\ ADDRESS... +.TP +.BI dns\ ADDRESS... +.TP +.BI logsvr\ ADDRESS... +.TP +.BI cookiesvr\ ADDRESS... +.TP +.BI lprsvr\ ADDRESS... +.TP +.BI hostname\ HOSTNAME +.TP +.BI bootsize\ SIZE +.TP +.BI domain\ DOMAIN +.TP +.BI swapsvr\ ADDRESS +.TP +.BI rootpath\ PATH +.TP +.BI ipttl\ TTL +.TP +.BI mtu\ MTU +.TP +.BI broadcast\ ADDRESS +.TP +.BI ntpsrv\ ADDRESS... +.TP +.BI wins\ ADDRESS... +.TP +.BI requestip\ ADDRESS +.TP +.BI lease\ SECONDS +.TP +.BI dhcptype\ TYPE +.TP +.BI serverid\ ADDRESS +.TP +.BI tftp\ FILE +.TP +.BI bootfile\ FILE +The default for +.B lease +is +.BR 864000 . +There are no defaults for the other options. +.RE +.SH SEE ALSO +.BR udhcpd (8). diff --git a/platform/mcu/xr871/src/net/udhcp/usr_dhcpd.c b/platform/mcu/xr871/src/net/udhcp/usr_dhcpd.c new file mode 100644 index 0000000000..191eb74ecc --- /dev/null +++ b/platform/mcu/xr871/src/net/udhcp/usr_dhcpd.c @@ -0,0 +1,333 @@ +/* dhcpd.c + * + * udhcp Server + * Copyright (C) 1999 Matthew Ramsay + * Chris Trew + * + * Rewrite by Russ Dill July 2001 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "lwip/sockets.h" +#include "debug.h" +#include "dhcpd.h" +#include "arpping.h" +#include "socket.h" +#include "options.h" +#include "files.h" +#include "leases.h" +#include "packet.h" +#include "serverpacket.h" +#include "net/udhcp/usr_dhcpd.h" +#include "dns.h" + +/* globals */ +struct dhcpOfferedAddr *leases = NULL; +struct server_config_t server_config; + +#define DHCPD_THREAD_STACK_SIZE (2 * 1024) +static OS_Thread_t g_dhcpd_thread; + +static void udhcpd_start(void *arg) +{ +#ifdef DHCPD_DNS + fd_set fds; + int maxfdp; + int ret; + + int dns_socket = -1; + char *dns_buf = NULL; + dns_buf = malloc(DNS_BUF_SIZE); + if (!dns_buf) { + DNS_ERR("dns buf malloc faild\n"); + return; + } +#endif + int server_socket = -1; + int bytes = 0; + struct dhcpMessage *packet = NULL; + packet = malloc(sizeof(*packet)); + if (!packet) { + DHCPD_LOG(LOG_ERR, "udhcp server (v%s) started", VERSION); +#ifdef DHCPD_DNS + free(dns_buf); +#endif + return; + } + unsigned char *state; + unsigned char *server_id, *requested; + uint32_t server_id_align, requested_align; + struct option_set *option; + struct dhcpOfferedAddr *lease; + struct dhcp_server_info *server_param = NULL; + if (arg != NULL) + server_param = (struct dhcp_server_info *) arg; + + DEBUG(LOG_INFO, "udhcp server (v%s) started", VERSION); + + memset(&server_config, 0, sizeof(struct server_config_t)); + init_config(); + + if (server_param != NULL && server_param->lease_time > server_config.min_lease) { + server_config.lease = server_param->lease_time; + } else if ((option = find_option(server_config.options, DHCP_LEASE_TIME))) { + memcpy(&server_config.lease, option->data + 2, 4); + server_config.lease = ntohl(server_config.lease); + } else + server_config.lease = LEASE_TIME; + + if (read_interface(server_config.interface, &server_config.ifindex, + &server_config.server, server_config.arp) < 0) { + goto exit_server; + } + + if (server_param != NULL && server_param->max_leases > 0) + server_config.max_leases = server_param->max_leases; + + if (server_param != NULL && server_param->addr_start != 0 + && server_param->addr_end != 0 + && ntohl(server_param->addr_end) >= ntohl(server_param->addr_start)) { + server_config.start = server_param->addr_start; + server_config.end = server_param->addr_end; + } + if ((ntohl(server_param->addr_end) - ntohl(server_param->addr_start)) > (server_config.max_leases - 1)) + server_config.end = htonl((ntohl(server_config.start) + server_config.max_leases - 1)); + + leases = malloc(sizeof(struct dhcpOfferedAddr) * server_config.max_leases); + memset(leases, 0, sizeof(struct dhcpOfferedAddr) * server_config.max_leases); + DEBUG(LOG_DEBUG, "start ip=%s", inet_ntoa(server_config.start)); + DEBUG(LOG_DEBUG, "end ip=%s", inet_ntoa(server_config.end)); + + while(1) { /* loop until universe collapses */ + + if (server_socket < 0) + if ((server_socket = listen_socket(INADDR_ANY, SERVER_PORT, server_config.interface)) < 0) { + DEBUG(LOG_ERR, "FATAL: couldn't create server socket, %s", strerror(errno)); + goto exit_server; + } +#ifdef DHCPD_DNS + if (dns_socket < 0) + if ((dns_socket = dns_listen_socket()) < 0) { + DNS_ERR("FATAL: couldn't create dns server socket\n"); + goto exit_server; + } + + FD_ZERO(&fds); + FD_SET(server_socket, &fds); + FD_SET(dns_socket, &fds); + maxfdp = server_socket > dns_socket ? server_socket + 1 : dns_socket + 1; + ret = select(maxfdp, &fds, NULL, NULL, NULL); + if (ret < 0) + goto exit_server; + else if (ret == 0) + continue; + if (FD_ISSET(dns_socket, &fds)) + dns_server(dns_socket, dns_buf, DNS_BUF_SIZE); + + if (FD_ISSET(server_socket, &fds)) { +#endif + if ((bytes = get_packet(packet, server_socket)) < 0) { /* this waits for a packet - idle */ + if (bytes == -1 && errno != EINTR) { + DEBUG(LOG_INFO, "error on read, %s, reopening socket", strerror(errno)); + closesocket(server_socket); + server_socket = -1; + } else if (bytes == -3) { + DEBUG(LOG_INFO, "exit server.."); + closesocket(server_socket); + server_socket = -1; + goto exit_server; + } + continue; + } + + if ((state = get_option(packet, DHCP_MESSAGE_TYPE)) == NULL) { + DEBUG(LOG_ERR, "couldn't get option from packet, ignoring"); + continue; + } + /* ADDME: look for a static lease */ + lease = find_lease_by_chaddr(packet->chaddr); + DEBUG(LOG_INFO, "MAC %02x:%02x:%02x:%02x:%02x:%02x\n", + packet->chaddr[0],packet->chaddr[1], + packet->chaddr[2],packet->chaddr[3], + packet->chaddr[4],packet->chaddr[5]); + switch (state[0]) { + case DHCPDISCOVER: + DEBUG(LOG_INFO,"received DISCOVER"); + if (sendOffer(packet) < 0) { + DEBUG(LOG_ERR, "send OFFER failed"); + } + break; + case DHCPREQUEST: + DEBUG(LOG_INFO, "received REQUEST"); + requested = get_option(packet, DHCP_REQUESTED_IP); + server_id = get_option(packet, DHCP_SERVER_ID); + + if (requested) { + memcpy(&requested_align, requested, 4); + DEBUG(LOG_INFO, "requeste id = %s", inet_ntoa(requested_align)); + } + if (server_id) { + memcpy(&server_id_align, server_id, 4); + DEBUG(LOG_INFO, "server id = %s", inet_ntoa(server_id_align)); + } + + if (lease) { /*ADDME: or static lease */ + if (server_id) { + /* SELECTING State */ + DEBUG(LOG_INFO, "server_id = %08x", ntohl(server_id_align)); + if (server_id_align == server_config.server && requested && + requested_align == lease->yiaddr) { + sendACK(packet, lease->yiaddr); + } + } else { + if (requested) { + /* INIT-REBOOT State */ + if (lease->yiaddr == requested_align) + sendACK(packet, lease->yiaddr); + else sendNAK(packet); + } else { + /* RENEWING or REBINDING State */ + if (lease->yiaddr == packet->ciaddr) + sendACK(packet, lease->yiaddr); + else { + /* don't know what to do!!!! */ + sendNAK(packet); + } + } + } + + /* what to do if we have no record of the client */ + } else if (server_id) { + /* SELECTING State */ + + } else if (requested) { + /* INIT-REBOOT State */ + if ((lease = find_lease_by_yiaddr(requested_align))) { + if (lease_expired(lease)) { + /* probably best if we drop this lease */ + memset(lease->chaddr, 0, 16); + /* make some contention for this address */ + } else sendNAK(packet); + } else { + sendNAK(packet); + } + } else { + /* RENEWING or REBINDING State */ + } + break; + case DHCPDECLINE: + DEBUG(LOG_INFO,"received DECLINE"); + if (lease) { + memset(lease->chaddr, 0, 16); + lease->expires = time(0) + server_config.decline_time; + } + break; + case DHCPRELEASE: + DEBUG(LOG_INFO,"received RELEASE"); + if (lease) lease->expires = time(0); + break; + case DHCPINFORM: + DEBUG(LOG_INFO,"received INFORM"); + send_inform(packet); + break; + default: + DEBUG(LOG_WARNING, "unsupported DHCP message (%02x) -- ignoring", state[0]); + } +#ifdef DHCPD_DNS + } +#endif + } + +exit_server: +#ifdef DHCPD_DNS + if (dns_buf != NULL) { + free(dns_buf); + dns_buf = NULL; + } + if (dns_socket >= 0) + closesocket(dns_socket); +#endif + if (packet != NULL ) { + free(packet); + packet = NULL; + } + if (leases != NULL) { + free(leases); + leases = NULL; + } + OS_ThreadDelete(&g_dhcpd_thread); +} + +static int udhcpd_stop(void) +{ + int fd; + struct sockaddr_in addr_serv; + char *stop_msg = ""; + if ((fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) { + DEBUG(LOG_ERR, "socket call failed: %s", strerror(errno)); + return -1; + } + memset(&addr_serv, 0, sizeof(addr_serv)); + addr_serv.sin_family = AF_INET; + addr_serv.sin_port = htons(SERVER_PORT); + addr_serv.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + sendto(fd, stop_msg, strlen(stop_msg), 0, + (struct sockaddr *)&addr_serv, sizeof(addr_serv)); + closesocket(fd); + return 0; +} + +void dhcp_server_start(const struct dhcp_server_info *arg) +{ + if (OS_ThreadIsValid(&g_dhcpd_thread)) { + return; + } + + if (OS_ThreadCreate(&g_dhcpd_thread, + "", + udhcpd_start, + (void *) arg, + OS_THREAD_PRIO_APP, + DHCPD_THREAD_STACK_SIZE) != OS_OK) { + DEBUG(LOG_ERR, "create main task failed\n"); + } +} + +void dhcp_server_stop(void) +{ + if (!OS_ThreadIsValid(&g_dhcpd_thread)) { + return; + } + + if (udhcpd_stop() != 0) { + DEBUG(LOG_ERR, "stop dhcp server failed\n"); + return; + } + + while (OS_ThreadIsValid(&g_dhcpd_thread)) { + OS_MSleep(1); /* wait for thread termination */ + } + DEBUG(LOG_INFO, "stop dhcp server success\n"); +} diff --git a/platform/mcu/xr871/src/net/wlan/airkiss/airkiss.h b/platform/mcu/xr871/src/net/wlan/airkiss/airkiss.h new file mode 100644 index 0000000000..11f42722e2 --- /dev/null +++ b/platform/mcu/xr871/src/net/wlan/airkiss/airkiss.h @@ -0,0 +1,236 @@ +/* + * airkiss.h + * + * Created on: 2015-1-26 + * Author: peterfan + */ + +#ifndef AIRKISS_H_ +#define AIRKISS_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * AIRKISS_ENABLE_CRYPTΪ1AirKissܹ + */ +#ifndef AIRKISS_ENABLE_CRYPT +#define AIRKISS_ENABLE_CRYPT 1 +#endif + + +typedef void* (*airkiss_memset_fn) (void* ptr, int value, unsigned int num); +typedef void* (*airkiss_memcpy_fn) (void* dst, const void* src, unsigned int num); +typedef int (*airkiss_memcmp_fn) (const void* ptr1, const void* ptr2, unsigned int num); +typedef int (*airkiss_printf_fn) (const char* format, ...); + + +/* + * AirKissãĿǰһЩص + */ +typedef struct +{ + /* + * Ϊٿļc׼⺯Ҫϲʹṩ + * printfΪNULL + */ + airkiss_memset_fn memset; + airkiss_memcpy_fn memcpy; + airkiss_memcmp_fn memcmp; + airkiss_printf_fn printf; + +} airkiss_config_t; + + + +/* + * AirKiss APIҪĽṹ壬Ϊȫֱͨmalloc̬ + */ +typedef struct +{ + int dummyap[26]; + int dummy[32]; +} airkiss_context_t; + + + +/* + * AirKissɹĽ + */ +typedef struct +{ + char* pwd; /* wifi룬'\0'β */ + char* ssid; /* wifi ssid'\0'β */ + unsigned char pwd_length; /* wifi볤 */ + unsigned char ssid_length; /* wifi ssid */ + unsigned char random; /* ֵAirKissЭ飬wifiӳɹҪͨudp10000˿ڹ㲥ֵAirKissͶˣ΢ſͻ˻AirKissDebugger֪AirKissóɹ */ + unsigned char reserved; /* ֵ */ +} airkiss_result_t; + + + +/* + * airkiss_recv()µķֵ + */ +typedef enum +{ + /* ⴦airkiss_recv()ֱɹ */ + AIRKISS_STATUS_CONTINUE = 0, + + /* wifiŵѾϲӦֹͣлŵ */ + AIRKISS_STATUS_CHANNEL_LOCKED = 1, + + /* ɹԵairkiss_get_result()ȡý */ + AIRKISS_STATUS_COMPLETE = 2 + +} airkiss_status_t; + + + +#if AIRKISS_ENABLE_CRYPT + +/* + * ýkeyΪ128bitkey128bitĬ0 + * + * ֵ + * < 0ͨDz + * 0ɹ + */ +int airkiss_set_key(airkiss_context_t* context, const unsigned char* key, unsigned int length); + +#endif + + + +/* + * ȡAirKiss汾Ϣ + */ +const char* airkiss_version(void); + + + +/* + * ʼAirKiss⣬ҪcontextԶε + * + * ֵ + * < 0ͨDz + * 0ɹ + */ +int airkiss_init(airkiss_context_t* context, const airkiss_config_t* config); + + + +/* + * WiFi Promiscuous Mode󣬽յİairkiss_recvԽн + * + * ˵ + * frame802.11 frame mac header(must contain at least first 24 bytes) + * lengthtotal frame length + * + * ֵ + * < 0ͨDz + * >= 0ɹοairkiss_status_t + */ +int airkiss_recv(airkiss_context_t* context, const void* frame, unsigned short length); + + + +/* + * airkiss_recv()AIRKISS_STATUS_COMPLETE󣬵ô˺ȡAirKiss + * + * ֵ + * < 0״̬AIRKISS_STATUS_COMPLETE + * 0ɹ + */ +int airkiss_get_result(airkiss_context_t* context, airkiss_result_t* result); + + +/* + * ϲлŵԺ󣬿Եһ±ӿ建棬ŵĸʣעõ߼airkiss_init֮ + * + * ֵ + * < 0ͨDz + * 0ɹ + */ +int airkiss_change_channel(airkiss_context_t* context); + +/* + * + * ʵAPI΢API + * + */ + +/* + * airkiss_lan_recv()ķֵ + */ +typedef enum +{ + /* ṩݻȲ */ + AIRKISS_LAN_ERR_OVERFLOW = -5, + + /* ǰ汾ֵָ֧ */ + AIRKISS_LAN_ERR_CMD = -4, + + /* ݳ */ + AIRKISS_LAN_ERR_PAKE = -3, + + /* ݲ */ + AIRKISS_LAN_ERR_PARA = -2, + + /* ݴ */ + AIRKISS_LAN_ERR_PKG = -1, + + /* ĸʽȷDzҪ豸ݰ */ + AIRKISS_LAN_CONTINUE = 0, + + /* յ豸ݰ */ + AIRKISS_LAN_SSDP_REQ = 1, + + /* ݰ */ + AIRKISS_LAN_PAKE_READY = 2 + + +} airkiss_lan_ret_t; + + +typedef enum +{ + AIRKISS_LAN_SSDP_REQ_CMD = 0x1, + AIRKISS_LAN_SSDP_RESP_CMD = 0x1001, + AIRKISS_LAN_SSDP_NOTIFY_CMD = 0x1002 +} airkiss_lan_cmdid_t; + +/* + * 豸ģʽ󣬽յİairkiss_lan_recvԽн + * + * ˵ + * body802.11 frame mac header(must contain at least first 24 bytes) + * lengthtotal frame length + * configAirKissص + * + * ֵ + * < 0οairkiss_lan_ret_tͨDZݳ + * >= 0ɹοairkiss_lan_ret_t + */ +int airkiss_lan_recv(const void* body, unsigned short length, const airkiss_config_t* config); + +/* + * 豸ҪЭʱñӿݰ + * + * ˵ + * body802.11 frame mac header(must contain at least first 24 bytes) + * lengthtotal frame length + * configAirKissص + * + * ֵ + * < 0οairkiss_lan_ret_tͨDZݳ + * >= 0ɹοairkiss_lan_ret_t + */ +int airkiss_lan_pack(airkiss_lan_cmdid_t ak_lan_cmdid, void* appid, void* deviceid, void* _datain, unsigned short inlength, void* _dataout, unsigned short* outlength, const airkiss_config_t* config); + +#ifdef __cplusplus +} +#endif + +#endif /* AIRKISS_H_ */ diff --git a/platform/mcu/xr871/src/net/wlan/airkiss/airkiss_ack.c b/platform/mcu/xr871/src/net/wlan/airkiss/airkiss_ack.c new file mode 100644 index 0000000000..564c340029 --- /dev/null +++ b/platform/mcu/xr871/src/net/wlan/airkiss/airkiss_ack.c @@ -0,0 +1,151 @@ +#include + +#include "kernel/os/os.h" +#include "lwip/sockets.h" +#include "lwip/inet.h" +#include "lwip/ip.h" + +#include "airkiss.h" + +enum loglevel{ + OFF = 0, + ERROR = 1, + INFO = 2, +}; + +#define g_debuglevel ERROR + +#define AIRKISS_ACK_DBG(level, fmt, args...) do { \ + if (level <= g_debuglevel) \ + printf("[airkiss ack]"fmt,##args); \ + } while (0) + + +static struct netif *wlan_netif; + +#define MAX_TIME 4294967295 +#define AIRKISS_ACK_UDP_PORT 10000 + +static const airkiss_config_t ak_config = { + (airkiss_memset_fn)&memset, + (airkiss_memcpy_fn)&memcpy, + (airkiss_memcmp_fn)&memcmp, + (airkiss_printf_fn)&printf + }; + +static uint32_t airkiss_d_time(uint32_t start_time, uint32_t os_time) +{ + uint32_t d_time = 0; + if (start_time <= os_time) + d_time = os_time - start_time; + else + d_time = MAX_TIME - start_time + os_time; + return d_time; +} + +static int airkiss_ack_successful(uint32_t random_num) +{ + uint8_t num[1]; + num[0] = (uint8_t)random_num; + + struct netif *nif = wlan_netif; + + if (nif == NULL) + return 0; + if (netif_is_up(nif) && netif_is_link_up(nif)) { + AIRKISS_ACK_DBG(INFO, "airkiss ack start\n"); + + int ak_ack_socket_id = lwip_socket(AF_INET, SOCK_DGRAM, 0); + if (ak_ack_socket_id < 0) { + AIRKISS_ACK_DBG(ERROR, "%s(), %d, create socket fail\n", __func__, __LINE__); + return 0; + } + + struct sockaddr_in addr; + memset(&addr, 0, sizeof(addr)); + + addr.sin_port = htons(AIRKISS_ACK_UDP_PORT); + addr.sin_family = AF_INET; + if (inet_aton("255.255.255.255", &addr.sin_addr) < 0) { + AIRKISS_ACK_DBG(ERROR, "%s(), %d, inet_aton error\n", __func__, __LINE__); + return 0; + } + + int tmp = 1; + int ret = setsockopt(ak_ack_socket_id, SOL_SOCKET, SO_BROADCAST, &tmp, sizeof(int)); + if (ret != 0) { + AIRKISS_ACK_DBG(ERROR, "%s(), %d, setsockopt error\n", __func__, __LINE__); + lwip_close(ak_ack_socket_id); + return 0; + } + + int i = 0; + for (i = 0; i < 300; i ++) { + int ret = lwip_sendto(ak_ack_socket_id, num, + 1, 0,(struct sockaddr *)&addr, sizeof(addr)); + if (ret == -1) { + AIRKISS_ACK_DBG(ERROR, "%s(), %d, udp send error, %d\n", __func__, __LINE__, OS_GetErrno()); + lwip_close(ak_ack_socket_id); + return 0; + } + OS_MSleep(1); + } + + lwip_close(ak_ack_socket_id); + return 1; + } + return 0; +} + +static uint8_t airkiss_ack_run = 0; + +static int airkiss_ack(uint32_t random_num, uint32_t time_out_ms) +{ + uint32_t start_time = OS_JiffiesToMSecs(OS_GetJiffies()); + uint32_t os_time = 0; + + while (airkiss_ack_run) { + if (airkiss_ack_successful(random_num)) + break; + + os_time = OS_JiffiesToMSecs(OS_GetJiffies()); + uint32_t d_time = airkiss_d_time(start_time, os_time); + if (d_time >= time_out_ms) + return -1; + + OS_MSleep(100); + } + + AIRKISS_ACK_DBG(INFO, "airkiss ack end\n"); + + return 0; +} + +static int airkiss_ack_start(struct netif *nif, uint32_t random_num, uint32_t timeout_ms) +{ + AIRKISS_ACK_DBG(INFO, "ack start\n"); + + if (nif == NULL) + return -1; + + wlan_netif = nif; + airkiss_ack_run = 1; + + return airkiss_ack(random_num, timeout_ms); +} + +static void airkiss_ack_stop(void) +{ + airkiss_ack_run = 0; +} + +int wlan_airkiss_ack_start(struct netif *nif, uint32_t random_num, uint32_t timeout_ms) +{ + return airkiss_ack_start(nif, random_num, timeout_ms); +} + +int wlan_airkiss_ack_stop(void) +{ + airkiss_ack_stop(); + return 0; +} diff --git a/platform/mcu/xr871/src/net/wlan/airkiss/airkiss_discover.c b/platform/mcu/xr871/src/net/wlan/airkiss/airkiss_discover.c new file mode 100644 index 0000000000..0e49f24044 --- /dev/null +++ b/platform/mcu/xr871/src/net/wlan/airkiss/airkiss_discover.c @@ -0,0 +1,314 @@ +#include "string.h" + +#include "kernel/os/os.h" +#include "lwip/sockets.h" +#include "lwip/inet.h" +#include "lwip/ip.h" + +#include "airkiss.h" + +enum loglevel{ + OFF = 0, + ERROR = 1, + INFO = 2, +}; + +#define g_debuglevel ERROR + +#define AIRKISS_DISCOVER_DBG(level, fmt, args...) do { \ + if (level <= g_debuglevel) \ + printf("[airkiss ack]"fmt,##args); \ + } while (0) + +typedef struct { + char *app_id; + char *device_id; + uint32_t ack_period_ms; +} Airkiss_Online_Ack_Info; + +const airkiss_config_t akconf ={ + (airkiss_memset_fn)&memset, + (airkiss_memcpy_fn)&memcpy, + (airkiss_memcmp_fn)&memcmp, + (airkiss_printf_fn)&printf +}; + +#define AIRKISS_LAN_PORT 12476 +static Airkiss_Online_Ack_Info ack_info; + +/************************************************************************************************************/ +/** + * allwin + */ +static void airkiss_device_online_ack(Airkiss_Online_Ack_Info *info, int socket_id) +{ + AIRKISS_DISCOVER_DBG(INFO, "into airkiss_device_online_ack\n"); + + uint8_t ak_online_ack_buf[200]; + uint16_t ak_online_buf_len = 200; + int ret = airkiss_lan_pack(AIRKISS_LAN_SSDP_NOTIFY_CMD, + //wechat_public_id, device_id, + info->app_id, info->device_id, + 0, 0, + ak_online_ack_buf, &ak_online_buf_len, + &akconf); + + if (ret == AIRKISS_LAN_PAKE_READY) { + struct sockaddr_in addr; + memset(&addr, 0, sizeof(addr)); + + addr.sin_port = htons(AIRKISS_LAN_PORT); + addr.sin_family = AF_INET; + if (inet_aton("255.255.255.255", &addr.sin_addr) < 0) { + AIRKISS_DISCOVER_DBG(ERROR, "%s(), %d, inet_aton error\n", __func__, __LINE__); + return; + } + + int tmp = 1; + int ret = setsockopt(socket_id, SOL_SOCKET, SO_BROADCAST, &tmp, sizeof(int)); + if (ret != 0) { + AIRKISS_DISCOVER_DBG(ERROR, "%s(), %d, setsockopt error\n", __func__, __LINE__); + return; + } + ret = lwip_sendto(socket_id, ak_online_ack_buf, ak_online_buf_len, + 0,(const struct sockaddr *)&addr, sizeof(addr)); + if (ret == -1) + AIRKISS_DISCOVER_DBG(ERROR, "%s(), %d, udp send error\n", __func__, __LINE__); + } +} + +static OS_Thread_t g_airkiss_cycle_ack_thread; +#define AIRKISS_CYCLE_ACK_THREAD_STACK_SIZE 1024 * 1 +static uint8_t airkiss_cycle_ack_run = 0; + +static void airkiss_cycle_ack_task(void* param) +{ + Airkiss_Online_Ack_Info *info = (Airkiss_Online_Ack_Info *) param; + int socket_id; + struct sockaddr_in addr; + + socket_id = socket(AF_INET ,SOCK_DGRAM, 0); + if (socket_id < 0) { + AIRKISS_DISCOVER_DBG(ERROR,"create sock error! %s, %d\n", __func__, __LINE__); + return; + } + + memset(&addr, 0, sizeof(addr)); + addr.sin_family=AF_INET; + addr.sin_port= htons(AIRKISS_LAN_PORT); + addr.sin_addr.s_addr=htonl(INADDR_ANY) ; + + if (bind(socket_id, (struct sockaddr*)&addr, sizeof(addr))<0) { + AIRKISS_DISCOVER_DBG(ERROR,"bind sock error! %s, %d\n", __func__, __LINE__); + return; + } + + while(airkiss_cycle_ack_run){ + airkiss_device_online_ack(info, socket_id); + OS_MSleep(info->ack_period_ms); + } + /*since lan discovery should be send always,so we will never reach this*/ + OS_ThreadDelete(&g_airkiss_cycle_ack_thread); +} + +static int airkiss_online_cycle_ack_start(Airkiss_Online_Ack_Info *param) +{ + ack_info = *param; + airkiss_cycle_ack_run = 1; + + if (OS_ThreadCreate(&g_airkiss_cycle_ack_thread, + "", + airkiss_cycle_ack_task, + (void *)&ack_info, + OS_THREAD_PRIO_APP, + AIRKISS_CYCLE_ACK_THREAD_STACK_SIZE) != OS_OK) { + AIRKISS_DISCOVER_DBG(ERROR, "%s(), %d create airkiss ask thread failed\n", __func__, __LINE__); + return -1; + } + + return 0; +} + +static void airkiss_online_cycle_ack_stop(void) +{ + airkiss_cycle_ack_run = 0; + while (OS_ThreadIsValid (&g_airkiss_cycle_ack_thread)) + OS_MSleep(100); +} + +static int airkiss_send_active_lan_discovery_packets(int client_socket_fd, Airkiss_Online_Ack_Info *info) +{ + int ret = -1; + struct sockaddr_in to_addr; + uint8_t lan_buf[200]; + uint16_t lan_buf_len = sizeof(lan_buf); + + ret = airkiss_lan_pack(AIRKISS_LAN_SSDP_NOTIFY_CMD, info->app_id, info->device_id, 0, 0, lan_buf, &lan_buf_len, &akconf); + if (ret != AIRKISS_LAN_PAKE_READY) { + AIRKISS_DISCOVER_DBG(ERROR, "airkiss pack lan packet error!\n"); + return -1; + } + + + FD_ZERO(&to_addr); + to_addr.sin_family = AF_INET; + to_addr.sin_port = htons(AIRKISS_LAN_PORT); + to_addr.sin_addr.s_addr =inet_addr("255.255.255.255"); + + int tmp = 1; + ret = setsockopt(client_socket_fd, SOL_SOCKET, SO_BROADCAST, &tmp, sizeof(int)); + if (ret != 0) { + AIRKISS_DISCOVER_DBG(ERROR, "%s(), %d, setsockopt error\n", __func__, __LINE__); + return -1; + } + + ret = sendto(client_socket_fd, (unsigned char *)lan_buf, lan_buf_len, 0, (struct sockaddr *) &to_addr, sizeof(struct sockaddr)); + if(ret == -1){ + AIRKISS_DISCOVER_DBG(ERROR, "UDP send error %s, %d\n", __func__, __LINE__); + return -1; + } + + return 0; +} + +static void airkiss_lan_server_reply(int client_socket_fd, struct sockaddr_in addr, Airkiss_Online_Ack_Info *info, + char *pdata, unsigned short len) +{ + airkiss_lan_ret_t ret = -1; + airkiss_lan_ret_t pack_ret; + uint8_t lan_buf[200]; + uint16_t lan_buf_len = sizeof(lan_buf); + + ret = airkiss_lan_recv(pdata, len, &akconf); + + switch (ret){ + case AIRKISS_LAN_SSDP_REQ: + addr.sin_port = htons(AIRKISS_LAN_PORT); + lan_buf_len = sizeof(lan_buf); + pack_ret = airkiss_lan_pack(AIRKISS_LAN_SSDP_RESP_CMD, info->app_id, info->device_id, 0, 0, lan_buf, &lan_buf_len, &akconf); + if (pack_ret != AIRKISS_LAN_PAKE_READY) { + AIRKISS_DISCOVER_DBG(ERROR, "Pack lan packet error! %s, %d\n", __func__, __LINE__); + return; + } + + AIRKISS_DISCOVER_DBG(ERROR, "AIRKISS_LAN_SSDP_REQ !\n"); + ret = sendto(client_socket_fd, (unsigned char *)lan_buf, lan_buf_len, 0, (struct sockaddr *) &addr, sizeof(struct sockaddr)); + if(ret != 0){ + AIRKISS_DISCOVER_DBG(ERROR, "lan_server_reply sendto ret=%d\n", ret); + } + break; + default: + AIRKISS_DISCOVER_DBG(ERROR,"Pack is not ssdq req!\n"); + break; + } +} + +static OS_Thread_t g_airkiss_online_dialog_thread; +#define AIRKISS_ONLINE_DIALOG_THREAD_STACK_SIZE 1024 * 1 +static uint8_t airkiss_dialog_run = 0; + +static void airkiss_online_dialog_task(void *arg) +{ + Airkiss_Online_Ack_Info *info = (Airkiss_Online_Ack_Info *)arg; + int server_sock_fd,len; + struct sockaddr_in addr; + int sock_timeout_val = 1000; /* 1000 ms */ + int addr_len = sizeof(struct sockaddr_in); + char buffer[256] = {0}; + + if((server_sock_fd = socket(AF_INET,SOCK_DGRAM,0)) < 0){ + AIRKISS_DISCOVER_DBG(ERROR, "create sock error!\n"); + airkiss_dialog_run = 0; + return; + } + + memset(&addr, 0, sizeof(addr)); + addr.sin_family=AF_INET; + addr.sin_port = htons(AIRKISS_LAN_PORT); + addr.sin_addr.s_addr=htonl(INADDR_ANY) ; + + if (bind(server_sock_fd, (struct sockaddr *)&addr, sizeof(addr))<0) { + AIRKISS_DISCOVER_DBG(ERROR, "bind sock error!\n"); + airkiss_dialog_run = 0; + return; + } + + int ret = setsockopt(server_sock_fd, SOL_SOCKET, SO_RCVTIMEO, &sock_timeout_val, sizeof(sock_timeout_val)); + if (ret != 0) { + AIRKISS_DISCOVER_DBG(ERROR, "%s(), %d, setsockopt error\n", __func__, __LINE__); + } + + memset(&buffer, 0, sizeof(buffer)); + len = recvfrom(server_sock_fd, buffer, sizeof(buffer), 0 , (struct sockaddr *)&addr , (socklen_t *)&addr_len); + + if (len != -1) { + airkiss_lan_server_reply(server_sock_fd, addr, info, buffer, len); + } + + while (airkiss_dialog_run) { + if (airkiss_send_active_lan_discovery_packets(server_sock_fd, info) == -1) + break; + OS_MSleep(info->ack_period_ms); + } + + OS_ThreadDelete(&g_airkiss_online_dialog_thread); +} + +static int airkiss_online_dialog_mode_start(Airkiss_Online_Ack_Info *param) +{ + ack_info = *param; + if (airkiss_dialog_run) { + AIRKISS_DISCOVER_DBG(ERROR, "airkiss online already start %s(), %d \n", __func__, __LINE__); + return -1; + } + airkiss_dialog_run = 1; + + if (OS_ThreadCreate(&g_airkiss_online_dialog_thread, + "", + airkiss_online_dialog_task, + (void *)&ack_info, + OS_THREAD_PRIO_APP, + AIRKISS_ONLINE_DIALOG_THREAD_STACK_SIZE) != OS_OK) { + AIRKISS_DISCOVER_DBG(ERROR, "create airkiss ask thread failed %s(), %d \n", __func__, __LINE__); + return -1; + } + + return 0; +} + +static void airkiss_online_dialog_mode_stop(void) +{ + airkiss_dialog_run = 0; + while (OS_ThreadIsValid (&g_airkiss_cycle_ack_thread)) + OS_MSleep(100); +} + +int wlan_airkiss_online_cycle_ack_start(char *app_id, char *drv_id, uint32_t period_ms) +{ + Airkiss_Online_Ack_Info param; + param.app_id = app_id; + param.device_id = drv_id; + param.ack_period_ms = period_ms; + return airkiss_online_cycle_ack_start(¶m); +} + +int wlan_airkiss_online_cycle_ack_stop(void) +{ + airkiss_online_cycle_ack_stop(); + return 0; +} + +int wlan_airkiss_online_dialog_mode_start(char *app_id, char *drv_id, uint32_t period_ms) +{ + Airkiss_Online_Ack_Info param; + param.app_id = app_id; + param.device_id = drv_id; + param.ack_period_ms = period_ms; + return airkiss_online_dialog_mode_start(¶m); +} + +int wlan_airkiss_online_dialog_mode_stop(void) +{ + airkiss_online_dialog_mode_stop(); + return 0; +} diff --git a/platform/mcu/xr871/src/net/wlan/airkiss/airkiss_main.c b/platform/mcu/xr871/src/net/wlan/airkiss/airkiss_main.c new file mode 100644 index 0000000000..f0727aa530 --- /dev/null +++ b/platform/mcu/xr871/src/net/wlan/airkiss/airkiss_main.c @@ -0,0 +1,261 @@ +#include "stdio.h" +#include "stdlib.h" +#include "errno.h" +#include "string.h" +#include "sys/mbuf.h" +#include "kernel/os/os.h" +#include "net/wlan/wlan.h" + +#include "net/wlan/wlan_airkiss.h" +#include "airkiss.h" + +enum loglevel{ + OFF = 0, + ERROR = 1, + INFO = 2, +}; + +#define g_debuglevel INFO + +#define AIRKISS_DBG(level, fmt, args...) do { \ + if (level <= g_debuglevel) \ + printf("[airkiss]"fmt,##args); \ + } while (0) + +enum { + AK_END = 0, + AK_SEARCH_CHAN, + AK_LOCKED_CHAN, +}; + +static uint8_t ak_ctrl = 0; +static airkiss_context_t ak_context; +static airkiss_result_t priv_ak_result; +static airkiss_status_t ak_sta = AIRKISS_STATUS_CONTINUE; +static const airkiss_config_t config = { + (airkiss_memset_fn)&memset, + (airkiss_memcpy_fn)&memcpy, + (airkiss_memcmp_fn)&memcmp, + (airkiss_printf_fn)&printf + }; + +#define AK_KEY_LEN 16 +#define CHTABLESIZE 13 +#define MAX_TIME 4294967295 + +static char ak_key[AK_KEY_LEN]; +static struct netif *priv_nif = NULL; + +static uint32_t d_time(uint32_t time1, uint32_t time2) +{ + uint32_t d_time = 0; + if (time1 <= time2) + d_time = time2 - time1; + else + d_time = MAX_TIME - time1 + time2; + return d_time; +} + +static void airkiss_macframe(uint8_t *data, uint32_t len, void *info) +{ + ak_sta = airkiss_recv(&ak_context, data, len); + + if (ak_sta == AIRKISS_STATUS_COMPLETE || ak_ctrl == AK_END) { + wlan_monitor_set_rx_cb(priv_nif, NULL); + AIRKISS_DBG(INFO, "%s(), %d, airkiss complete\n", __func__, __LINE__); + + if (ak_ctrl) { + airkiss_get_result(&ak_context, &priv_ak_result); + AIRKISS_DBG(INFO, "ssid %s\n", priv_ak_result.ssid); + AIRKISS_DBG(INFO, "pwd %s\n", priv_ak_result.pwd); + AIRKISS_DBG(INFO, "random %d\n", priv_ak_result.random); + AIRKISS_DBG(INFO, "%s, %d airkiss end !!!!! \n", __func__, __LINE__); + } else + AIRKISS_DBG(ERROR, "%s, %d airkiss get result error\n", __func__, __LINE__); + + ak_ctrl = AK_END; + } else if (ak_sta == AIRKISS_STATUS_CHANNEL_LOCKED) { + AIRKISS_DBG(INFO, "airkiss channel locked\n"); + ak_ctrl = AK_LOCKED_CHAN; + } else if (ak_sta < 0) + AIRKISS_DBG(ERROR, "%s, %d airkiss recv error %d\n", __func__, __LINE__, ak_sta); +} + +static int airkiss_wifi_set_channel(struct netif *nif, int16_t ch) +{ + int ret = wlan_monitor_set_channel(nif, ch); + if (ret == -1) + AIRKISS_DBG(ERROR, "%s, %d airkiss set channel error\n", __func__, __LINE__); + + return ret; +} + +static void airkiss_config(void) +{ + int ret = -1; + AIRKISS_DBG(INFO, "%s\n", airkiss_version()); + + ret = airkiss_init(&ak_context, &config); + if (ret != 0) + AIRKISS_DBG(ERROR, "%s, %d, airkiss init error\n", __func__, __LINE__); + if(ak_key[0] != 0) { + ret = airkiss_set_key(&ak_context, (const unsigned char *)ak_key, AK_KEY_LEN); + if (ret != 0) + AIRKISS_DBG(ERROR, "%s, %d, airkiss set key error\n", __func__, __LINE__); + } +} + +static void airkiss_reset(void) +{ + /* release resource of airkiss */ + airkiss_change_channel(&ak_context); + priv_ak_result.ssid = NULL; + priv_ak_result.pwd = NULL; +} + +static void airkiss_search(int16_t * ch_buff) +{ + int i = 0; + + while (1) { + if (ak_ctrl == AK_SEARCH_CHAN) { + if (ak_sta == AIRKISS_STATUS_CONTINUE) { + airkiss_wifi_set_channel(priv_nif, ch_buff[i++]); + airkiss_reset(); + } + + if (i >= CHTABLESIZE) + i = 0; + } else if (ak_ctrl == AK_LOCKED_CHAN) + break; + else if (ak_ctrl == AK_END) + break; + + OS_MSleep(100); + } +} + +static int airkiss_read_result(wlan_airkiss_result_t *result) +{ + if (priv_ak_result.ssid != NULL) { + if (priv_ak_result.ssid_length > WLAN_SSID_MAX_LEN) { + AIRKISS_DBG(ERROR, "Invalid ssid len %d\n", priv_ak_result.ssid_length); + return -1; + } + + if (priv_ak_result.pwd_length > WLAN_PASSPHRASE_MAX_LEN) { + AIRKISS_DBG(ERROR, "Invalid passphrase len %d\n", priv_ak_result.pwd_length); + return -1; + } + + memcpy(result->ssid, priv_ak_result.ssid, priv_ak_result.ssid_length); + result->ssid_len = priv_ak_result.ssid_length; + + if (priv_ak_result.pwd_length > 0 && priv_ak_result.pwd != NULL) { + memcpy(result->passphrase, priv_ak_result.pwd, priv_ak_result.pwd_length); + result->passphrase[priv_ak_result.pwd_length] = '\0'; + } else { + result->passphrase[0] = '\0'; + } + + result->random_num = priv_ak_result.random; + return 0; + } + + return -1; +} + +static int airkiss_set_aeskey(const char *key, uint32_t length) +{ + if (length != AK_KEY_LEN) + return -1; + + memcpy(ak_key, key, AK_KEY_LEN); + + return 0; +} + +static void airkiss_close(void) +{ + ak_ctrl = AK_END; + + if (wlan_monitor_set_rx_cb(priv_nif, NULL) != 0) { + AIRKISS_DBG(ERROR, "%s(),%d monitor set rx cb error\n", __func__, __LINE__); + } + + airkiss_reset(); +} + +static wlan_airkiss_status_t airkiss_start(struct netif *nif, + uint32_t timeout_ms, + wlan_airkiss_result_t *result) +{ + int ret = -1; + wlan_airkiss_status_t status = WLAN_AIRKISS_FAIL; + int16_t ak_ch_buffer[CHTABLESIZE] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13}; + + AIRKISS_DBG(INFO, "start\n"); + + if (nif == NULL || result == NULL) { + AIRKISS_DBG(ERROR, "%s(),%d, nif %p, result %p\n", __func__, __LINE__, nif, result); + return WLAN_AIRKISS_INVALID; + } + + priv_nif = nif; + airkiss_config(); + + AIRKISS_DBG(INFO, "monitor_register\n"); + + ret = wlan_monitor_set_rx_cb(priv_nif, airkiss_macframe); + if (ret != 0) { + AIRKISS_DBG(ERROR, "%s(),%d monitor set rx cb fail\n", __func__, __LINE__); + goto out; + } + + uint32_t start_time = OS_JiffiesToMSecs(OS_GetJiffies()); + + ak_ctrl = AK_SEARCH_CHAN; + airkiss_search(ak_ch_buffer); + + while (ak_ctrl) { + uint32_t d_t = d_time(start_time, OS_JiffiesToMSecs(OS_GetJiffies())); + if (d_t >= timeout_ms) { + airkiss_close(); + status = WLAN_AIRKISS_TIMEOUT; + goto out; + } + + OS_MSleep(100); + } + + ret = airkiss_read_result(result); + if (ret == 0) { + status = WLAN_AIRKISS_SUCCESS; + } + +out: + airkiss_reset(); + return status; +} + +int wlan_airkiss_set_key(const char *key, uint32_t len) +{ + if (key == NULL || len != WLAN_AIRKISS_KEY_LEN) { + AIRKISS_DBG(ERROR, "invalid airkiss key (%p, %d)\n", key, len); + return -1; + } + + return airkiss_set_aeskey(key, len); +} + +wlan_airkiss_status_t wlan_airkiss_start(struct netif *nif, uint32_t timeout_ms, + wlan_airkiss_result_t *result) +{ + return airkiss_start(nif, timeout_ms, result); +} + +int wlan_airkiss_stop(void) +{ + airkiss_close(); + return 0; +} diff --git a/platform/mcu/xr871/src/net/wlan/ethernetif.c b/platform/mcu/xr871/src/net/wlan/ethernetif.c new file mode 100644 index 0000000000..3a4f75bd42 --- /dev/null +++ b/platform/mcu/xr871/src/net/wlan/ethernetif.c @@ -0,0 +1,529 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "lwip/opt.h" +#include "lwip/def.h" +#include "lwip/mem.h" +#include "lwip/pbuf.h" +#include "lwip/stats.h" +#include "lwip/snmp.h" +#include "lwip/tcpip.h" +#include "lwip/dhcp.h" +#include "lwip/etharp.h" +#include "lwip/ethip6.h" +#include "lwip/netifapi.h" +#include "net/wlan/ethernetif.h" +#include +#include "sys/mbuf.h" +#include "sys/xr_util.h" +#ifdef __CONFIG_ARCH_DUAL_CORE +#include "sys/ducc/ducc_app.h" + #else +#include "net80211/net80211_ifnet.h" +#endif + +#define ETH_DBG_ON 0 +#define ETH_WRN_ON 1 +#define ETH_ERR_ON 1 +#define ETH_ABORT_ON 1 + +#define ETH_SYSLOG printf +#define ETH_ABORT() sys_abort() + +#define ETH_LOG(flags, fmt, arg...) \ + do { \ + if (flags) \ + ETH_SYSLOG(fmt, ##arg); \ + } while (0) + +#define ETH_DBG(fmt, arg...) ETH_LOG(ETH_DBG_ON, "[eth] "fmt, ##arg) +#define ETH_WRN(fmt, arg...) ETH_LOG(ETH_WRN_ON, "[eth WRN] "fmt, ##arg) +#define ETH_ERR(fmt, arg...) \ + do { \ + ETH_LOG(ETH_ERR_ON, "[eth ERR] %s():%d, "fmt, \ + __func__, __LINE__, ##arg); \ + if (ETH_ABORT_ON) \ + ETH_ABORT(); \ + } while (0) + +#define ETHER_MTU_MAX 1500 +#define NETIF_LINK_SPEED_BPS (100 * 1000 * 1000) +#define NETIF_ATTACH_BASE_FLAGS (NETIF_FLAG_BROADCAST | \ + NETIF_FLAG_ETHARP | \ + NETIF_FLAG_ETHERNET | \ + NETIF_FLAG_IGMP) +#if LWIP_IPV6 +#define NETIF_ATTACH_FLAGS (NETIF_ATTACH_BASE_FLAGS | NETIF_FLAG_MLD6) +#else +#define NETIF_ATTACH_FLAGS (NETIF_ATTACH_BASE_FLAGS) +#endif + +struct ethernetif { + struct netif nif; + enum wlan_mode mode; +}; + +#define ethernetif2netif(eth) ((struct netif *)(eth)) +#define netif2ethernetif(nif) ((struct ethernetif *)(nif)) + +static struct ethernetif g_eth_netif; + +#if LWIP_NETIF_HOSTNAME +#define NETIF_HOSTNAME_MAX_LEN 32 +static char g_netif_hostname[NETIF_HOSTNAME_MAX_LEN]; + +void ethernetif_set_hostname(char *hostname) +{ + if (hostname != NULL) { + strlcpy(g_netif_hostname, hostname, NETIF_HOSTNAME_MAX_LEN); + } +} +#endif /* LWIP_NETIF_HOSTNAME */ + +static err_t tcpip_null_input(struct pbuf *p, struct netif *nif) +{ + ETH_WRN("%s() called\n", __func__); + pbuf_free(p); + return ERR_OK; +} + +#if LWIP_IPV4 +static err_t ethernetif_null_ip4output(struct netif *nif, struct pbuf *p, const ip4_addr_t *ip4addr) +{ + ETH_WRN("%s() called\n", __func__); + return ERR_IF; +} +#endif + +#if LWIP_IPV6 +static err_t ethernetif_null_ip6output(struct netif *nif, struct pbuf *p, const ip6_addr_t *ip6addr) +{ + ETH_WRN("%s() called\n", __func__); + return ERR_IF; +} +#endif + +static err_t ethernetif_null_linkoutput(struct netif *nif, struct pbuf *p) +{ + ETH_WRN("%s() called\n", __func__); + return ERR_IF; +} + +#ifdef __CONFIG_ARCH_DUAL_CORE + +#if (LWIP_MBUF_SUPPORT == 0) +static __inline struct mbuf *eth_pbuf2mbuf(struct pbuf *p) +{ + struct ducc_param_mbuf_get param; + struct mbuf *m; + struct pbuf *q; + uint8_t *data; + int32_t left; + + /* get a mbuf */ + param.len = p->tot_len; + param.tx = 1; + param.mbuf = NULL; + if (ducc_app_ioctl(DUCC_APP_CMD_MBUF_GET, ¶m) != 0) { + return NULL; + } + + /* copy all data to mbuf */ + m = param.mbuf; + data = mtod(m, uint8_t *); + left = m->m_len; + for (q = p; q != NULL; q = q->next) { + if (left >= q->len) { + memcpy(data, q->payload, q->len); + data += q->len; + left -= q->len; + } else { + break; + } + } + if (left != 0) { + ETH_ERR("left %d, total %u\n", left, p->tot_len); + ducc_app_ioctl(DUCC_APP_CMD_MBUF_FREE, m); + return NULL; + } + return m; +} +#endif /* (LWIP_MBUF_SUPPORT == 0) */ + +/* NB: @p is freed by Lwip. */ +static err_t ethernetif_linkoutput(struct netif *nif, struct pbuf *p) +{ + struct ducc_param_wlan_linkoutput param; + struct mbuf *m; + int ret; + +#if ETH_PAD_SIZE + pbuf_header(p, -ETH_PAD_SIZE); /* drop the padding word */ +#endif + +#if (LWIP_MBUF_SUPPORT == 0) + m = eth_pbuf2mbuf(p); +#elif (LWIP_MBUF_SUPPORT == 1) + m = mb_pbuf2mbuf(p); +#endif /* LWIP_MBUF_SUPPORT */ + +#if ETH_PAD_SIZE + pbuf_header(p, ETH_PAD_SIZE); /* reclaim the padding word */ +#endif + + if (m == NULL) { + ETH_DBG("pbuf2mbuf() failed\n"); + LINK_STATS_INC(link.memerr); + MIB2_STATS_NETIF_INC(nif, ifoutdiscards); + return ERR_MEM; + } + param.mbuf = m; + param.ifp = nif->state; + ret = ducc_app_ioctl(DUCC_APP_CMD_WLAN_LINKOUTPUT, ¶m); + if (ret != 0) { + ETH_WRN("linkoutput failed (%d)\n", ret); + LINK_STATS_INC(link.err); + MIB2_STATS_NETIF_INC(nif, ifouterrors); + } else { + LINK_STATS_INC(link.xmit); +#if MIB2_STATS + MIB2_STATS_NETIF_ADD(nif, ifoutoctets, p->tot_len); + if (((u8_t*)p->payload)[0] & 1) { + /* broadcast or multicast packet*/ + MIB2_STATS_NETIF_INC(nif, ifoutnucastpkts); + } else { + /* unicast packet */ + MIB2_STATS_NETIF_INC(nif, ifoutucastpkts); + } +#endif + } + return ERR_OK; +} + +#if LWIP_IPV4 +static err_t ethernetif_ip4output(struct netif *nif, struct pbuf *p, const ip4_addr_t *ip4addr) +{ + if (!netif_is_link_up(nif)) { + ETH_DBG("netif %p is link down\n", nif); + return ERR_IF; + } + + return etharp_output(nif, p, ip4addr); +} +#endif + +#if LWIP_IPV6 +static err_t ethernetif_ip6output(struct netif *nif, struct pbuf *p, const ip6_addr_t *ip6addr) +{ + if (!netif_is_link_up(nif)) { + ETH_DBG("netif %p is link down\n", nif); + return ERR_IF; + } + + return ethip6_output(nif, p, ip6addr); +} +#endif + +#endif /* __CONFIG_ARCH_DUAL_CORE */ + +/* NB: call by RX task to process received data */ +err_t ethernetif_input(struct netif *nif, struct pbuf *p) +{ + err_t err = ERR_MEM; + + do { + if (p == NULL) { + ETH_DBG("pbuf is NULL\n"); + LINK_STATS_INC(link.memerr); + break; + } +#if ETH_PAD_SIZE + if (pbuf_header(p, ETH_PAD_SIZE) != 0) { + /* add padding word for LwIP */ + ETH_WRN("pbuf_header(%d) failed!\n", ETH_PAD_SIZE); + LINK_STATS_INC(link.memerr); + break; + } +#endif /* ETH_PAD_SIZE */ + + /* send data to LwIP, nif->input() == tcpip_input() */ + err = nif->input(p, nif); + if (err != ERR_OK) { + ETH_WRN("lwip process data failed, err %d!\n", err); +// LINK_STATS_INC(link.err); + } else { + p = NULL; /* pbuf will be freed by LwIP */ + } + } while (0); + + if (p) { + pbuf_free(p); + } + + __asm("nop"); +#if (LINK_STATS || MIB2_STATS) + if (err == ERR_OK) { + LINK_STATS_INC(link.recv); + #if MIB2_STATS + MIB2_STATS_NETIF_ADD(nif, ifinoctets, p->tot_len); + if (((u8_t*)p->payload)[0] & 1) { + /* broadcast or multicast packet*/ + MIB2_STATS_NETIF_INC(nif, ifinnucastpkts); + } else { + /* unicast packet*/ + MIB2_STATS_NETIF_INC(nif, ifinucastpkts); + } + #endif + } else { + LINK_STATS_INC(link.drop); + MIB2_STATS_NETIF_INC(nif, ifindiscards); + } +#endif /* (LINK_STATS || MIB2_STATS) */ + return err; +} + +#if (LWIP_MBUF_SUPPORT == 0) +err_t ethernetif_raw_input(struct netif *nif, uint8_t *data, u16_t len) +{ + struct pbuf *p, *q; + +#if ETH_PAD_SIZE + len += ETH_PAD_SIZE; /* allow room for Ethernet padding */ +#endif + /* We allocate a pbuf chain of pbufs from the pool. */ + p = pbuf_alloc(PBUF_RAW, len, PBUF_POOL); + if (p != NULL) { +#if ETH_PAD_SIZE + pbuf_header(p, -ETH_PAD_SIZE); /* drop the padding word */ +#endif + /* We iterate over the pbuf chain until we have read the entire packet into the pbuf. */ + for(q = p; q != NULL; q = q->next) { + /* Read enough bytes to fill this pbuf in the chain. The available data + * in the pbuf is given by the q->len variable. + * This does not necessarily have to be a memcpy, you can also preallocate + * pbufs for a DMA-enabled MAC and after receiving truncate it to the + * actually received size. In this case, ensure the tot_len member of the + * pbuf is the sum of the chained pbuf len members. + */ + memcpy(q->payload, data, q->len); + data += q->len; + } + } + return ethernetif_input(nif, p); +} +#endif /* (LWIP_MBUF_SUPPORT == 0) */ + +static err_t ethernetif_hw_init(struct netif *nif, enum wlan_mode mode) +{ +#ifdef __CONFIG_ARCH_DUAL_CORE + char name[4]; + name[0] = nif->name[0]; + name[1] = nif->name[1]; + name[2] = nif->num + '0'; + name[3] = '\0'; + + struct ducc_param_wlan_create param; + param.mode = mode; + param.nif = nif; + param.name = name; + param.ifp = NULL; + if (ducc_app_ioctl(DUCC_APP_CMD_WLAN_IF_CREATE, ¶m) != 0) { + ETH_ERR("wlan interface create failed\n"); + return ERR_IF; + } + nif->state = param.ifp; + + struct ducc_param_wlan_get_mac_addr param2; + param2.ifp = nif->state; + param2.buf = nif->hwaddr; + param2.buf_len = ETHARP_HWADDR_LEN; + + if (ducc_app_ioctl(DUCC_APP_CMD_WLAN_GET_MAC_ADDR, ¶m2) != ETHARP_HWADDR_LEN) { + ETH_ERR("get mac addr failed\n"); + ducc_app_ioctl(DUCC_APP_CMD_WLAN_IF_DELETE, nif->state); + nif->state = NULL; + return ERR_IF; + } +#else /* __CONFIG_ARCH_DUAL_CORE */ + nif->state = net80211_ifnet_create(mode, nif); + if (nif->state == NULL) { + ETH_ERR("wlan interface create failed\n"); + return ERR_IF; + } +#endif /* __CONFIG_ARCH_DUAL_CORE */ + return ERR_OK; +} + +static void ethernetif_hw_deinit(struct netif *nif) +{ +#ifdef __CONFIG_ARCH_DUAL_CORE + ducc_app_ioctl(DUCC_APP_CMD_WLAN_IF_DELETE, nif->state); +#else + net80211_ifnet_delete(nif->state); +#endif +} + +static err_t ethernetif_init(struct netif *nif, enum wlan_mode mode) +{ + if (mode == WLAN_MODE_STA || mode == WLAN_MODE_HOSTAP) { +// nif->input = tcpip_input; +#ifdef __CONFIG_ARCH_DUAL_CORE + #if LWIP_IPV4 + nif->output = ethernetif_ip4output; + #endif + #if LWIP_IPV6 + nif->output_ip6 = ethernetif_ip6output; + #endif /* LWIP_IPV6 */ + nif->linkoutput = ethernetif_linkoutput; +#else /* __CONFIG_ARCH_DUAL_CORE */ + #if LWIP_IPV4 + nif->output = etharp_output; + #endif + #if LWIP_IPV6 + nif->output_ip6 = ethip6_output; + #endif /* LWIP_IPV6 */ + nif->linkoutput = net80211_linkoutput; +#endif /* __CONFIG_ARCH_DUAL_CORE */ + } else if (mode == WLAN_MODE_MONITOR) { +// nif->input = tcpip_null_input; +#if LWIP_IPV4 + nif->output = ethernetif_null_ip4output; +#endif +#if LWIP_IPV6 + nif->output_ip6 = ethernetif_null_ip6output; +#endif /* LWIP_IPV6 */ + nif->linkoutput = ethernetif_null_linkoutput; + } else { + ETH_ERR("mode %d\n", mode); + return ERR_ARG; + } + +#if LWIP_NETIF_HOSTNAME + /* Initialize interface hostname */ + nif->hostname = g_netif_hostname; +#endif /* LWIP_NETIF_HOSTNAME */ + + /* + * Initialize the snmp variables and counters inside the struct netif. + * The last argument should be replaced with your link speed, in units + * of bits per second. + */ + MIB2_INIT_NETIF(nif, snmp_ifType_ethernet_csmacd, NETIF_LINK_SPEED_BPS); + + nif->name[0] = 'e'; + nif->name[1] = 'n'; + nif->mtu = ETHER_MTU_MAX; + nif->hwaddr_len = ETHARP_HWADDR_LEN; + nif->flags |= NETIF_ATTACH_FLAGS; + +#if (LWIP_IPV6 && LWIP_IPV6_MLD) + /* + * For hardware/netifs that implement MAC filtering. + * All-nodes link-local is handled by default, so we must let + * the hardware know to allow multicast packets in. + * Should set mld_mac_filter previously. + */ + if (nif->mld_mac_filter != NULL) { + ip6_addr_t ip6_allnodes_ll; + ip6_addr_set_allnodes_linklocal(&ip6_allnodes_ll); + nif->mld_mac_filter(nif, &ip6_allnodes_ll, NETIF_ADD_MAC_FILTER); + } +#endif /* LWIP_IPV6 && LWIP_IPV6_MLD */ + + /* initialize the hardware */ + return ethernetif_hw_init(nif, mode); +} + +static err_t ethernetif_sta_init(struct netif *nif) +{ + return ethernetif_init(nif, WLAN_MODE_STA); +} + +static err_t ethernetif_hostap_init(struct netif *nif) +{ + return ethernetif_init(nif, WLAN_MODE_HOSTAP); +} + +static err_t ethernetif_monitor_init(struct netif *nif) +{ + return ethernetif_init(nif, WLAN_MODE_MONITOR); +} + +struct netif *ethernetif_create(enum wlan_mode mode) +{ + netif_init_fn init_fn; + netif_input_fn input_fn; + struct netif *nif; + + if (mode == WLAN_MODE_STA) { + init_fn = ethernetif_sta_init; + input_fn = tcpip_input; + } else if (mode == WLAN_MODE_HOSTAP) { + init_fn = ethernetif_hostap_init; + input_fn = tcpip_input; + } else if (mode == WLAN_MODE_MONITOR) { + init_fn = ethernetif_monitor_init; + input_fn = tcpip_null_input; + } else { + ETH_ERR("mode %d\n", mode); + return NULL; + } + + nif = ethernetif2netif(&g_eth_netif); + memset(nif, 0, sizeof(*nif)); + g_eth_netif.mode = mode; + + /* add netif */ +#if LWIP_IPV4 + netifapi_netif_add(nif, NULL, NULL, NULL, NULL, init_fn, input_fn); +#else + netifapi_netif_add(nif, NULL, init_fn, input_fn); +#endif + netifapi_netif_set_default(nif); + return nif; + +} + +void ethernetif_delete(struct netif *nif) +{ + /* remove netif from LwIP stack */ + netifapi_netif_remove(nif); + netifapi_netif_common(nif, dhcp_cleanup, NULL); + nif->flags &= ~NETIF_ATTACH_FLAGS; + ethernetif_hw_deinit(nif); +} + +enum wlan_mode ethernetif_get_mode(struct netif *nif) +{ + if (nif == ethernetif2netif(&g_eth_netif)) { + return g_eth_netif.mode; + } else { + return WLAN_MODE_INVALID; + } +} diff --git a/platform/mcu/xr871/src/net/wlan/smartconfig/smart_config_ack.c b/platform/mcu/xr871/src/net/wlan/smartconfig/smart_config_ack.c new file mode 100644 index 0000000000..9cfcf272e1 --- /dev/null +++ b/platform/mcu/xr871/src/net/wlan/smartconfig/smart_config_ack.c @@ -0,0 +1,170 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include "kernel/os/os.h" +#include "lwip/sockets.h" +#include "lwip/inet.h" +#include "lwip/ip.h" + +enum loglevel{ + OFF = 0, + ERROR = 1, + INFO = 2, +}; + +#define g_debuglevel ERROR + +#define SMART_ACK_DBG(level, fmt, args...) do { \ + if (level <= g_debuglevel) \ + printf("[sc ack]"fmt,##args); \ + } while (0) + + +static struct netif *wlan_netif; + +#define MAX_TIME 4294967295 +#define SC_ACK_UDP_PORT 10000 + +static uint32_t sc_d_time(uint32_t start_time, uint32_t os_time) +{ + uint32_t d_time = 0; + if (start_time <= os_time) + d_time = os_time - start_time; + else + d_time = MAX_TIME - start_time + os_time; + return d_time; +} + +static int sc_ack_successful(uint32_t random_num) +{ + uint8_t num[1]; + num[0] = (uint8_t)random_num; + + struct netif *nif = wlan_netif; + + if (nif == NULL) + return 0; + if (netif_is_up(nif) && netif_is_link_up(nif)) { + SMART_ACK_DBG(INFO, "airkiss ack start\n"); + + int ak_ack_socket_id = lwip_socket(AF_INET, SOCK_DGRAM, 0); + if (ak_ack_socket_id < 0) { + SMART_ACK_DBG(ERROR, "%s(), %d, create socket fail\n", __func__, __LINE__); + return 0; + } + + struct sockaddr_in addr; + memset(&addr, 0, sizeof(addr)); + + addr.sin_port = htons(SC_ACK_UDP_PORT); + addr.sin_family = AF_INET; + if (inet_aton("239.0.0.1", &addr.sin_addr) < 0) { + SMART_ACK_DBG(ERROR, "%s(), %d, inet_aton error\n", __func__, __LINE__); + return 0; + } + + int tmp = 1; + int ret = setsockopt(ak_ack_socket_id, SOL_SOCKET, SO_BROADCAST, &tmp, sizeof(int)); + if (ret != 0) { + SMART_ACK_DBG(ERROR, "%s(), %d, setsockopt error\n", __func__, __LINE__); + lwip_close(ak_ack_socket_id); + return 0; + } + + int i = 0; + for (i = 0; i < 300; i ++) { + int ret = lwip_sendto(ak_ack_socket_id, num, + 1, 0,(struct sockaddr *)&addr, sizeof(addr)); + if (ret == -1) { + SMART_ACK_DBG(ERROR, "%s(), %d, udp send error, %d\n", __func__, __LINE__, OS_GetErrno()); + lwip_close(ak_ack_socket_id); + return 0; + } + OS_MSleep(1); + } + + lwip_close(ak_ack_socket_id); + return 1; + } + return 0; +} + +static uint8_t sc_ack_run = 0; + +static int sc_ack(uint32_t random_num, uint32_t time_out_ms) +{ + uint32_t start_time = OS_JiffiesToMSecs(OS_GetJiffies()); + uint32_t os_time = 0; + + while (sc_ack_run) { + if (sc_ack_successful(random_num)) + break; + + os_time = OS_JiffiesToMSecs(OS_GetJiffies()); + uint32_t d_time = sc_d_time(start_time, os_time); + if (d_time >= time_out_ms) + return -1; + + OS_MSleep(100); + } + + SMART_ACK_DBG(INFO, "smart_config ack end\n"); + + return 0; +} + +static int smart_config_ack_start(struct netif *nif, uint32_t random_num, uint32_t timeout_ms) +{ + SMART_ACK_DBG(INFO, "%s(), %d smart_config ask start\n", __func__, __LINE__); + + if (nif == NULL) + return -1; + + wlan_netif = nif; + sc_ack_run = 1; + + return sc_ack(random_num, timeout_ms); +} + +static void smart_config_ack_stop(void) +{ + sc_ack_run = 0; +} + +int wlan_smart_config_ack_start(struct netif *nif, uint32_t random_num, uint32_t timeout_ms) +{ + return smart_config_ack_start(nif, random_num, timeout_ms); +} + +int wlan_smart_config_ack_stop(void) +{ + smart_config_ack_stop(); + return 0; +} diff --git a/platform/mcu/xr871/src/net/wlan/smartconfig/smart_config_crc.c b/platform/mcu/xr871/src/net/wlan/smartconfig/smart_config_crc.c new file mode 100644 index 0000000000..f50c8e7e2f --- /dev/null +++ b/platform/mcu/xr871/src/net/wlan/smartconfig/smart_config_crc.c @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "smart_config_crc.h" +#include "driver/chip/hal_crypto.h" + +#define CRC8_INIT 0x0 +#define CRC8_POLY 0x31 + +unsigned char cal_crc8(const unsigned char *in, int num) +{ + unsigned char crc = CRC8_INIT; + int i, j; + + for (i = 0; i < num; i++ ){ + crc ^= in[i]; + for(j = 0; j < 8; j++){ + if (crc & 0x1) + crc = (crc >> 1) ^ CRC8_POLY; + else + crc = crc >> 1; + } + + } + + return crc; +} + +static int aes_decrypt(char *aes_key, char *enc_data, uint32_t enc_data_len, char *dec_buf) +{ + HAL_Status status = HAL_ERROR; + CE_AES_Config aes_cfg; + memset(&aes_cfg, 0, sizeof(aes_cfg)); + + sprintf((char*)aes_cfg.key, aes_key); + aes_cfg.keysize = CE_CTL_AES_KEYSIZE_128BITS; + aes_cfg.mode = CE_CRYPT_MODE_ECB; //CBC; + aes_cfg.src = CE_CTL_KEYSOURCE_INPUT; + + //HAL_CE_Init(); + + status = HAL_AES_Decrypt(&aes_cfg, (uint8_t*)enc_data, (uint8_t*)dec_buf, enc_data_len); + if (status != HAL_OK) { + printf("AES decrypt error %d\n", status); + return -1; + } + + return 0; +} + +static int aes_128_decrypt_block(char *key, char *in, int in_len, char *out) +{ + if (aes_decrypt(key, in, in_len, out) != 0) { + printf("ce decrypt error\n"); + return -1; + } + + return 0; +} + +int aes_ebc_decrypt(char *in, char* out, int in_len, char *key) +{ + int ret = -1; + + ret = aes_128_decrypt_block(key, in, in_len, out); + + return ret; +} diff --git a/platform/mcu/xr871/src/net/wlan/smartconfig/smart_config_crc.h b/platform/mcu/xr871/src/net/wlan/smartconfig/smart_config_crc.h new file mode 100644 index 0000000000..14e8996a4a --- /dev/null +++ b/platform/mcu/xr871/src/net/wlan/smartconfig/smart_config_crc.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __SC_CRC_H__ +#define __SC_CRC_H__ + +typedef unsigned char uint8_t; + +#include + +unsigned char cal_crc8(const unsigned char* in, int num); +//return 0 on success, len should be times of AES_BLOCK_SIZE, caller handles de-padding +int aes_ebc_decrypt(char *in, char* out, int in_len, char *key); + +#endif diff --git a/platform/mcu/xr871/src/net/wlan/smartconfig/smart_config_decode.c b/platform/mcu/xr871/src/net/wlan/smartconfig/smart_config_decode.c new file mode 100644 index 0000000000..62f2203e4d --- /dev/null +++ b/platform/mcu/xr871/src/net/wlan/smartconfig/smart_config_decode.c @@ -0,0 +1,370 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include "types.h" + +#include "smart_config_crc.h" +#include "smart_config_decode.h" + +enum loglevel{ + OFF = 0, + ERROR = 1, + INFO = 2, +}; + +#define g_debuglevel ERROR + +#define SC_DECODE_DBG(level, fmt, args...) \ + do { \ + if (level <= g_debuglevel) \ + printf("[smart config]"fmt,##args); \ + } while (0) + +#define __SC_BUG() \ + do { printf("BUG at %s:%d!\n", __func__, __LINE__); while (1);} while (0) + +#define SC_BUG_ON(condition) do { if (condition) __SC_BUG(); } while(0) + +#define LOCDED_FIELD 4 + +enum LEADCODE_SEQ { + LEAD_CODE_NOME, + LEAD_CODE_GET_CHANNEL, + LEAD_CODE_GET_SSIDPWD_SIZE, + LEAD_CODE_GET_PWD_SIZE, + LEAD_CODE_GET_ROUND_NUM, + LEAD_CODE_COMPLETE, +}; + +#define IEEE80211_ADDR_LEN 6 + +struct ieee80211_frame { + uint8_t i_fc[2]; + uint8_t i_dur[2]; + uint8_t i_addr1[IEEE80211_ADDR_LEN]; + uint8_t i_addr2[IEEE80211_ADDR_LEN]; + uint8_t i_addr3[IEEE80211_ADDR_LEN]; + uint8_t i_seq[2]; + /* possibly followed by addr4[IEEE80211_ADDR_LEN]; */ + /* see below */ +} __packed; + +extern void print_hex_dump_bytes(const char *prefix_str, int prefix_type, + const void *buf, size_t len); + +static int dec_valid_packet_count(uint8_t *count_pkt, uint8_t packet_num) +{ + /* count Success return 1, else return 0 */ + int check; + + if (packet_num > 127) + return 0; + + /* if data already exist, return 1,else return 0; if data is null return -1 */ + check = (((*(count_pkt + packet_num / 8)) & (1 << (8 - (packet_num % 8) - 1))) > 0); + if (check == 1) + return 0; + + *(count_pkt + packet_num / 8) |= 1 << (8 - (packet_num % 8) - 1); + + return 1; +} + +static enum LEADCODE_SEQ dec_get_lead_code(SC_Priv *sc_priv, struct ieee80211_frame *iframe, SC_Lead_Code *lead_code) +{ + uint8_t *sa = iframe->i_addr2; + uint8_t packet_num = iframe->i_addr3[3]; + uint8_t *data = &iframe->i_addr3[4]; + uint8_t *crc = &iframe->i_addr3[5]; + + /* the leadcode complete return 1,else return 0 */ + if ((sc_priv->Sc_Status != SC_LOCKED_CHAN) || \ + (lead_code->ssidpwd_size == 0) || (lead_code->pwd_size == -1) || \ + (lead_code->random_num == 0)) { + ;//SC_DECODE_DBG(INFO, "%s, %d\n", __func__, __LINE__); + } else { + SC_DECODE_DBG(INFO, "lead complete!!!\n"); + SC_DECODE_DBG(INFO, "ssidpwd_size : %d\n", lead_code->ssidpwd_size); + SC_DECODE_DBG(INFO, "pwd_size : %d\n", lead_code->pwd_size); + SC_DECODE_DBG(INFO, "round_num : %d\n", lead_code->random_num); + SC_DECODE_DBG(INFO, "channel : %d\n", lead_code->channel); + if (sc_priv->locked_mac_flag) { + sc_priv->locked_mac_flag = 0; + } + return LEAD_CODE_COMPLETE; + } + + if (packet_num > 0 && packet_num <= LOCDED_FIELD) { + /*crc check the leadcode*/ + uint8_t crc8 = cal_crc8(data, 1); + if (crc8 != *crc) + return LEAD_CODE_NOME; + + SC_DECODE_DBG(INFO, "get_leadcode\n"); + if (!sc_priv->locked_mac_flag) { + SC_DECODE_DBG(INFO, "locked src:\n"); + memcpy(sc_priv->locked_mac, sa, 6); + sc_priv->locked_mac_flag = 1; + print_hex_dump_bytes(NULL, 0, sc_priv->locked_mac, 6); + } + + if(packet_num == LEAD_CODE_GET_CHANNEL) { + lead_code->channel = *data; + sc_priv->Sc_Status = SC_LOCKED_CHAN; + SC_DECODE_DBG(INFO, "CHANNEL LOCKED : %d\n", lead_code->channel); + return LEAD_CODE_GET_CHANNEL; + } else if (packet_num == LEAD_CODE_GET_SSIDPWD_SIZE) { + lead_code->ssidpwd_size = *data; + lead_code->packet_sum = lead_code->ssidpwd_size / 2 + 1 + lead_code->ssidpwd_size % 2; + SC_DECODE_DBG(INFO, "PACKET_SUM %d \n", lead_code->packet_sum); + return LEAD_CODE_GET_SSIDPWD_SIZE; + } else if (packet_num == LEAD_CODE_GET_PWD_SIZE) { + lead_code->pwd_size = *data; + return LEAD_CODE_GET_PWD_SIZE; + } else if (packet_num == LEAD_CODE_GET_ROUND_NUM) { + lead_code->random_num = *data; + return LEAD_CODE_GET_ROUND_NUM; + } + } + + return LEAD_CODE_NOME; +} + +static int dec_data_decode(SC_Priv *sc_priv, SC_Result *result, SC_Lead_Code *lead_code, uint8_t *src_data_buff) +{ + int i = 0; + uint8_t src_ssid_size = 0; + uint8_t src_pwd_size = 0; + uint8_t crc8_pwd = 0; + uint8_t crc8_ssid = 0; + uint8_t *src_pwd_data = NULL; + uint8_t *src_ssid_data = NULL; + + src_ssid_size = lead_code->ssidpwd_size - lead_code->pwd_size; + src_pwd_size = lead_code->pwd_size; + SC_DECODE_DBG(INFO, "DATA_DECODE PWD_SIZE : %d SSID_SIZE : %d\n",\ + src_pwd_size, src_ssid_size); + + src_pwd_data = src_data_buff; + src_ssid_data = src_data_buff + src_pwd_size; + + if (sc_priv->Aes_Key[0] != 0) { + uint8_t temp_pwd[66]; + uint8_t temp_ssid[65]; + + SC_BUG_ON(src_pwd_size >= 66); + SC_BUG_ON(src_ssid_size >= 65); + memset(temp_pwd , 0, 66); + memset(temp_ssid, 0, 65); + + SC_DECODE_DBG(INFO, "DATA_DECODE pwd:\n"); + + if (src_pwd_size) { + if (aes_ebc_decrypt((char *)src_pwd_data, (char *) \ + temp_pwd, src_pwd_size, sc_priv->Aes_Key) == 0) { + int pwd_dlen; + + pwd_dlen = *(temp_pwd + src_pwd_size - 1); + SC_DECODE_DBG(INFO, "pwd_dlen: %d\n", pwd_dlen); + for (i = 0; i < pwd_dlen; i++) { + int value = *(temp_pwd + src_pwd_size - 1 - i); + if (value != pwd_dlen) { + SC_DECODE_DBG(ERROR, "%s(), %d, aes pwd err, value:%d\n", __func__, __LINE__, value); + return 0; + } + } + result->pwd_size = src_pwd_size - pwd_dlen; + memcpy(result->pwd, temp_pwd, strlen((const char *)temp_pwd)); + } else + SC_DECODE_DBG(ERROR, "%s,%d, aes pwd err\n", __func__, __LINE__); + } + if (src_ssid_size) { + if (aes_ebc_decrypt((char *)src_ssid_data, \ + (char *)temp_ssid, src_ssid_size, sc_priv->Aes_Key) == 0) { + int ssid_dlen = *(temp_ssid + src_ssid_size - 1); + + SC_DECODE_DBG(INFO, "ssid_dlen: %d\n", ssid_dlen); + for (i = 0; i < ssid_dlen; i++) { + int value = *(temp_ssid + src_ssid_size - 1 - i); + if (value != ssid_dlen) { + SC_DECODE_DBG(ERROR, "%s,%d, aes ssid err, value:%d\n", + __func__, __LINE__, value); + return 0; + } + } + result->ssid_size = src_ssid_size- ssid_dlen; + memcpy(result->ssid, temp_ssid, strlen((const char *)temp_ssid)); + } else + SC_DECODE_DBG(ERROR, "%s,%d, aes ssid err\n", __func__, __LINE__); + } + + *(result->pwd + result->pwd_size) = 0; + *(result->ssid + result->ssid_size) = 0; + } else if (sc_priv->Aes_Key[0] == 0) { + SC_BUG_ON(src_ssid_size >= 65); + SC_BUG_ON(src_pwd_size >= 66); + result->ssid_size = src_ssid_size; + result->pwd_size = src_pwd_size; + + memcpy(result->ssid, src_ssid_data, result->ssid_size); + memcpy(result->pwd, src_pwd_data, result->pwd_size); + + *(result->pwd + result->pwd_size) = 0; + *(result->ssid + result->ssid_size) = 0; + } + + result->random_num = lead_code->random_num; + SC_DECODE_DBG(INFO, "pwd:%s, ssid:%s\n", result->pwd, result->ssid); + + i = (lead_code->ssidpwd_size % 2); + + crc8_pwd = *(src_data_buff + lead_code->ssidpwd_size + i); + + if (cal_crc8(result->pwd, result->pwd_size) != crc8_pwd) { + src_data_buff[0] = 0; + SC_DECODE_DBG(ERROR, "%s,%d pwd crc check err\n", __func__, __LINE__); + + return 0; + } + + crc8_ssid = *(src_data_buff + lead_code->ssidpwd_size + i + 1); + + if (cal_crc8(result->ssid, result->ssid_size) != crc8_ssid) { + src_data_buff[0] = 0; + SC_DECODE_DBG(ERROR, "%s,%d ssid crc check err\n", __func__, __LINE__); + + return 0; + } + + src_data_buff[0] = 0; + + return 1; +} + +/*save data*/ +static int dec_push_data(struct ieee80211_frame *iframe, SC_Lead_Code *lead_code, uint8_t *src_data_buff) +{ + uint8_t packet_num = iframe->i_addr3[3]; + uint8_t *data = &iframe->i_addr3[4]; + int d = 0; + + packet_num -= 5; + + if (dec_valid_packet_count(lead_code->count_pkt, packet_num) == 1) + lead_code->packet_count += 1; + SC_DECODE_DBG(INFO, "push packet_num : %d\n", packet_num); + + *(src_data_buff + packet_num * 2 ) = *data; + *(src_data_buff + packet_num * 2 + 1) = *(data + 1); + d = lead_code->packet_count - lead_code->packet_sum; + + return d; +} + +SMART_CONFIG_STATUS_T sc_dec_packet_deoced(SC_Priv *sc_priv, uint8_t *data) +{ + struct ieee80211_frame *iframe = (struct ieee80211_frame *)data; + uint8_t packet_num = iframe->i_addr3[3]; + int ret; + + SC_BUG_ON(!sc_priv); + + if ((iframe->i_addr3[0] == SC_ADD0) && (iframe->i_addr3[1] == SC_ADD1) && \ + (iframe->i_addr3[2]== SC_ADD2)) + ; + else + return sc_priv->Sc_Status; + + if (!sc_priv->lead_code_inited) { + memset(&sc_priv->lead_code , 0, sizeof(SC_Lead_Code)); + sc_priv->lead_code.pwd_size = -1; + sc_priv->lead_code_inited = 1; + } + + if (sc_priv->lead_code.lead_complete_flag == 0) { + ret = dec_get_lead_code(sc_priv, iframe, &sc_priv->lead_code); + if(ret == LEAD_CODE_COMPLETE) + sc_priv->lead_code.lead_complete_flag = 1; + return sc_priv->Sc_Status; + } + + if (packet_num <= 4) + return sc_priv->Sc_Status; + + if (sc_priv->lead_code.lead_complete_flag == 1 && packet_num <= 127) { + if (dec_push_data(iframe, &sc_priv->lead_code, sc_priv->src_data_buff) != 0) + return sc_priv->Sc_Status; + } + + if (sc_priv->lead_code.packet_sum == sc_priv->lead_code.packet_count){ + SC_DECODE_DBG(INFO, "DATA IS ALL RECEIVE\n"); + SC_DECODE_DBG(INFO, "data_decode\n"); + int data_decode_ret = dec_data_decode(sc_priv, &sc_priv->sc_result, &sc_priv->lead_code, sc_priv->src_data_buff); + if (data_decode_ret) { + SC_DECODE_DBG(INFO, "decode success\n"); + sc_priv->Sc_Status = SC_COMPLETED; + } else { + SC_DECODE_DBG(INFO, "decode err\n"); + if (sc_priv->lead_code_inited) { + sc_priv->lead_code_inited = 0; + } + + if (sc_priv->src_data_buff[0]) { + sc_priv->src_data_buff[0] = 0; + } + + sc_priv->Sc_Status = SC_END; + } + } + + return sc_priv->Sc_Status; +} + +int sc_set_aeskey(SC_Priv *sc_priv, const char *key, uint32_t length) +{ + if (length != SC_KEY_LEN) + return -1; + + memcpy(sc_priv->Aes_Key, key, SC_KEY_LEN); + + return 0; +} + +uint16_t sc_read_locked_channel(SC_Priv *sc_priv) +{ + return sc_priv->lead_code.channel; +} + +SC_Result *sc_read_result(SC_Priv *sc_priv) +{ + return (sc_priv->sc_result.ssid[0] ? &sc_priv->sc_result : NULL); +} diff --git a/platform/mcu/xr871/src/net/wlan/smartconfig/smart_config_decode.h b/platform/mcu/xr871/src/net/wlan/smartconfig/smart_config_decode.h new file mode 100644 index 0000000000..2f199c829c --- /dev/null +++ b/platform/mcu/xr871/src/net/wlan/smartconfig/smart_config_decode.h @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __SC_DECODE_H__ +#define __SC_DECODE_H__ + +#include "types.h" + +#define SC_ADD0 0x01 +#define SC_ADD1 0x00 +#define SC_ADD2 0X5E + +typedef enum { + SC_VALUE_FALSE = 0, + SC_VALUE_TRUE = 1, +} SC_BOOL; + +typedef enum { + SC_VALUE_NOT_READY, + SC_VALUE_COMPLETE, +} SC_RESULT_STA; + +typedef enum { + SC_END = 0, + SC_SEARCH_CHAN, + SC_LOCKED_CHAN, + SC_COMPLETED, +} SMART_CONFIG_STATUS_T; + +typedef struct { + uint8_t ssid[65]; + uint8_t pwd[66]; + uint8_t ssid_size ; + uint8_t pwd_size ; + uint8_t random_num; +} SC_Result; + +typedef struct { + int channel; + int ssidpwd_size; + int pwd_size; + int packet_sum; + int packet_count; + uint8_t random_num; + uint8_t lead_complete_flag; + uint8_t count_pkt[16]; +} SC_Lead_Code; + +#define SC_KEY_LEN 16 + +typedef struct { + char Aes_Key[SC_KEY_LEN]; + SC_Lead_Code lead_code; + int lead_code_inited; + SMART_CONFIG_STATUS_T sc_ctrl; + uint8_t locked_mac[6]; + int locked_mac_flag; + SMART_CONFIG_STATUS_T Sc_Status; + SC_Result sc_result; + uint8_t src_data_buff[1500]; +} SC_Priv; + +SMART_CONFIG_STATUS_T sc_dec_packet_deoced(SC_Priv *sc_priv, uint8_t *data); +SC_Result *sc_read_result(SC_Priv *sc_priv); +uint16_t sc_read_locked_channel(SC_Priv *sc_priv); +/* the key length must be 16 byte */ +int sc_set_aeskey(SC_Priv *sc_priv, const char *key, uint32_t length); + +#endif /* __SC_DECODE_H__ */ diff --git a/platform/mcu/xr871/src/net/wlan/smartconfig/smart_config_main.c b/platform/mcu/xr871/src/net/wlan/smartconfig/smart_config_main.c new file mode 100644 index 0000000000..63d4c02bd8 --- /dev/null +++ b/platform/mcu/xr871/src/net/wlan/smartconfig/smart_config_main.c @@ -0,0 +1,255 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "stdio.h" +#include "stdlib.h" +#include "errno.h" +#include "string.h" +#include "sys/mbuf.h" +#include "kernel/os/os.h" +#include "net/wlan/wlan.h" + +#include "net/wlan/wlan_smart_config.h" +#include "smart_config_decode.h" + +enum loglevel{ + OFF = 0, + ERROR = 1, + INFO = 2, +}; + +#define g_debuglevel ERROR + +#define SMART_DBG(level, fmt, args...) \ + do { \ + if (level <= g_debuglevel) \ + printf("[smart config]"fmt,##args); \ + } while (0) + +#define MAX_TIME 4294967295 +static struct netif *priv_nif = NULL; +SC_Priv *sc_priv; + +static uint32_t d_time(uint32_t time1, uint32_t time2) +{ + uint32_t d_time = 0; + if (time1 <= time2) + d_time = time2 - time1; + else + d_time = MAX_TIME - time1 + time2; + return d_time; +} + +static int smartconfig_wifi_set_channel(struct netif *nif, int16_t ch) +{ + return wlan_monitor_set_channel(nif, ch); +} + +static void process_macframe(uint8_t *data, uint32_t len, void *info) +{ + /* make sure frame is qos data frame. */ + SMART_CONFIG_STATUS_T sta = sc_dec_packet_deoced(sc_priv, data); + + if (sta == SC_COMPLETED) { + SMART_DBG(INFO, "%s(),%d complete\n", __func__, __LINE__); + sc_priv->sc_ctrl = SC_END; + + if (wlan_monitor_set_rx_cb(priv_nif, NULL) == -1) + SMART_DBG(ERROR, "%s(),%d monitor set rx cb error\n", __func__, __LINE__); + } else if (sta == SC_LOCKED_CHAN) { + sc_priv->sc_ctrl = SC_LOCKED_CHAN; + } +} + +static int smartconfig_read_result(wlan_smart_config_result_t *result) +{ + SC_Result *sc_result = sc_read_result(sc_priv); + + if (sc_result != NULL) { + if (sc_result->ssid_size > WLAN_SSID_MAX_LEN) { + SMART_DBG(ERROR, "Invalid ssid len %d\n", sc_result->ssid_size); + return -1; + } + + if (sc_result->pwd_size > WLAN_PASSPHRASE_MAX_LEN) { + SMART_DBG(ERROR, "Invalid passphrase len %d\n", sc_result->pwd_size); + return -1; + } + + SMART_DBG(INFO, "smart config: ssid: %.32s, passphrase: %s, random_num: %d\n", + sc_result->ssid, sc_result->pwd, sc_result->random_num); + + memcpy(result->ssid, sc_result->ssid, sc_result->ssid_size); + result->ssid_len = sc_result->ssid_size; + + if (sc_result->pwd_size > 0 && sc_result->pwd != NULL) { + memcpy(result->passphrase, sc_result->pwd, sc_result->pwd_size); + result->passphrase[sc_result->pwd_size] = '\0'; + } else { + result->passphrase[0] = '\0'; + } + + result->random_num = sc_result->random_num; + return 0; + } + + return -1; +} + +/* the key length must be 16 byte */ +static int smartconfig_set_key(const char *key, uint32_t length) +{ + if (!sc_priv) { + sc_priv = malloc(sizeof(SC_Priv)); + if (!sc_priv) { + SMART_DBG(ERROR, "%s(),%d, malloc faild!\n", __func__, __LINE__); + return -1; + } + memset(sc_priv, 0, sizeof(SC_Priv)); + } + + return sc_set_aeskey(sc_priv, key, length); +} + +static void smartconfig_close(void) +{ + SMART_DBG(INFO, "close\n"); + + if (sc_priv) { + if (wlan_monitor_set_rx_cb(priv_nif, NULL) != 0) + SMART_DBG(ERROR, "%s(),%d monitor set rx cb fail\n", __func__, __LINE__); + + sc_priv->sc_ctrl = SC_END; + OS_MSleep(150); + free(sc_priv); + sc_priv = NULL; + } +} + +static wlan_smart_config_status_t smartconfig_start(struct netif *nif, + uint32_t timeout_ms, + wlan_smart_config_result_t *result) +{ + int ret = -1; + wlan_smart_config_status_t status = WLAN_SMART_CONFIG_FAIL; + int16_t channel = 0; + int16_t channel_burrer[] = {1, 6, 12, 2, 3, 4, 5, 7, 8, 9, 10, 11, 13}; + + if (nif == NULL || result == NULL) { + SMART_DBG(ERROR, "%s(),%d, nif %p, result %p\n", __func__, __LINE__, nif, result); + return WLAN_SMART_CONFIG_INVALID; + } + + priv_nif = nif; + SMART_DBG(INFO, "start\n"); + + if (!sc_priv) { + sc_priv = malloc(sizeof(SC_Priv)); + if (!sc_priv) { + SMART_DBG(ERROR, "malloc faild!\n"); + return status; + } + memset(sc_priv, 0, sizeof(SC_Priv)); + } + + sc_priv->sc_ctrl = SC_SEARCH_CHAN; + + SMART_DBG(INFO, "monitor_register\n"); + ret = wlan_monitor_set_rx_cb(priv_nif, process_macframe); + if (ret != 0) { + SMART_DBG(ERROR, "%s(),%d monitor set rx cb fail\n", __func__, __LINE__); + goto out; + } + + uint32_t start_time = OS_JiffiesToMSecs(OS_GetJiffies()); + + while (1) { + if (sc_priv->sc_ctrl == SC_SEARCH_CHAN) { + smartconfig_wifi_set_channel(priv_nif, channel_burrer[channel]); + + if (++channel >= sizeof(channel_burrer)) + channel = 0; + } else if (sc_priv->sc_ctrl == SC_LOCKED_CHAN) { + if (sc_read_locked_channel(sc_priv)) + smartconfig_wifi_set_channel(priv_nif, sc_read_locked_channel(sc_priv)); + + SMART_DBG(INFO, "channel locked\n"); + while (sc_priv->sc_ctrl) { + OS_MSleep(10); + } + break; + } else if (sc_priv->sc_ctrl == SC_END) { + break; + } + uint32_t d_t = d_time(start_time, OS_JiffiesToMSecs(OS_GetJiffies())); + if (d_t >= timeout_ms) { + smartconfig_close(); + status = WLAN_SMART_CONFIG_TIMEOUT; + goto out; + } + + OS_MSleep(100); + } + + ret = smartconfig_read_result(result); + if (ret == 0) { + status = WLAN_SMART_CONFIG_SUCCESS; + } + +out: + if (sc_priv) { + free(sc_priv); + sc_priv = NULL; + } + SMART_DBG(INFO, "end\n"); + return status; +} + +int wlan_smart_config_set_key(const char *key, uint32_t len) +{ + if (key == NULL || len != WLAN_SMART_CONFIG_KEY_LEN) { + SMART_DBG(ERROR, "invalid smart config key (%p, %d)\n", key, len); + return -1; + } + + return smartconfig_set_key(key, len); +} + +wlan_smart_config_status_t wlan_smart_config_start(struct netif *nif, + uint32_t timeout_ms, + wlan_smart_config_result_t *result) +{ + return smartconfig_start(nif, timeout_ms, result); +} + +int wlan_smart_config_stop(void) +{ + smartconfig_close(); + return 0; +} diff --git a/platform/mcu/xr871/src/net/wlan/wlan.c b/platform/mcu/xr871/src/net/wlan/wlan.c new file mode 100644 index 0000000000..b4ba1d9209 --- /dev/null +++ b/platform/mcu/xr871/src/net/wlan/wlan.c @@ -0,0 +1,459 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#if (defined(__CONFIG_ARCH_DUAL_CORE) && defined(__CONFIG_ARCH_APP_CORE)) + +#include "wlan_debug.h" +#include "wpa_ctrl_req.h" +#include "lwip/netif.h" +#include "sys/ducc/ducc_net.h" +#include "sys/ducc/ducc_app.h" + +#include "net/wlan/wlan.h" +#include "net/wlan/wlan_defs.h" + +#include "kernel/os/os.h" + +#define WLAN_ASSERT_POINTER(p) \ + do { \ + if (p == NULL) { \ + WLAN_ERR("invalid parameter\n"); \ + return -1; \ + } \ + } while (0) + +/** + * @brief Configure station in a convenient way to join a specified network + * @param[in] ssid Network name, length is [1, 32] + * @param[in] ssid_len The length of network name + * @param[in] psk Network password, in one of the optional formats: + * - NULL, to join an OPEN network + * - an ASCII string, length is [8, 63] + * - a hex string (two characters per octet of PSK), length is 64 + * @return 0 on success, -1 on failure + * + * @note This way is only adapted to join an OPEN, WPA-PSK, WPA2-PSK or + * WPA-PSK/WPA2-PSK network. + */ +int wlan_sta_set(uint8_t *ssid, uint8_t ssid_len, uint8_t *psk) +{ + WLAN_ASSERT_POINTER(ssid); + + if ((ssid_len == 0) || (ssid_len > 32)) { + WLAN_ERR("invalid ssid len %d\n", ssid_len); + return -1; + } + + wlan_sta_config_t config; + wlan_memset(&config, 0, sizeof(config)); + + /* ssid */ + config.field = WLAN_STA_FIELD_SSID; + wlan_memcpy(config.u.ssid.ssid, ssid, ssid_len); + config.u.ssid.ssid_len = ssid_len; + if (wpa_ctrl_request(WPA_CTRL_CMD_STA_SET, &config) != 0) + return -1; + + /* auth_alg: OPEN */ + config.field = WLAN_STA_FIELD_AUTH_ALG; + config.u.auth_alg = WPA_AUTH_ALG_OPEN; + if (wpa_ctrl_request(WPA_CTRL_CMD_STA_SET, &config) != 0) + return -1; + + if (psk == NULL) { + /* key_mgmt: NONE */ + config.field = WLAN_STA_FIELD_KEY_MGMT; + config.u.key_mgmt = WPA_KEY_MGMT_NONE; + if (wpa_ctrl_request(WPA_CTRL_CMD_STA_SET, &config) != 0) + return -1; + } else { + /* psk */ + config.field = WLAN_STA_FIELD_PSK; + wlan_strlcpy((char *)config.u.psk, (char *)psk, sizeof(config.u.psk)); + if (wpa_ctrl_request(WPA_CTRL_CMD_STA_SET, &config) != 0) + return -1; + + /* proto: WPA | RSN */ + config.field = WLAN_STA_FIELD_PROTO; + config.u.proto = WPA_PROTO_WPA | WPA_PROTO_RSN; + if (wpa_ctrl_request(WPA_CTRL_CMD_STA_SET, &config) != 0) + return -1; + + /* key_mgmt: PSK */ + config.field = WLAN_STA_FIELD_KEY_MGMT; + config.u.key_mgmt = WPA_KEY_MGMT_PSK; + if (wpa_ctrl_request(WPA_CTRL_CMD_STA_SET, &config) != 0) + return -1; + + /* pairwise: CCMP | TKIP */ + config.field = WLAN_STA_FIELD_PAIRWISE_CIPHER; + config.u.pairwise_cipher = WPA_CIPHER_CCMP | WPA_CIPHER_TKIP; + if (wpa_ctrl_request(WPA_CTRL_CMD_STA_SET, &config) != 0) + return -1; + + /* group: CCMP | TKIP | WEP40 | WEP104 */ + config.field = WLAN_STA_FIELD_GROUP_CIPHER; + config.u.pairwise_cipher = WPA_CIPHER_CCMP | WPA_CIPHER_TKIP + | WPA_CIPHER_WEP40 | WPA_CIPHER_WEP104; + if (wpa_ctrl_request(WPA_CTRL_CMD_STA_SET, &config) != 0) + return -1; + } + + /* scan_ssid: 1 */ + config.field = WLAN_STA_FIELD_SCAN_SSID; + config.u.scan_ssid = 1; + if (wpa_ctrl_request(WPA_CTRL_CMD_STA_SET, &config) != 0) + return -1; + + return 0; +} + +/** + * @brief Set station specified field configuration + * @param[in] config Pointer to the configuration + * @return 0 on success, -1 on failure + */ +int wlan_sta_set_config(wlan_sta_config_t *config) +{ + WLAN_ASSERT_POINTER(config); + + return wpa_ctrl_request(WPA_CTRL_CMD_STA_SET, config); +} + +/** + * @brief Get station specified field configuration + * @param[in] config Pointer to the configuration + * @return 0 on success, -1 on failure + */ +int wlan_sta_get_config(wlan_sta_config_t *config) +{ + WLAN_ASSERT_POINTER(config); + + return wpa_ctrl_request(WPA_CTRL_CMD_STA_GET, config); +} + +/** + * @brief Enable the station + * @return 0 on success, -1 on failure + */ +int wlan_sta_enable(void) +{ + return wpa_ctrl_request(WPA_CTRL_CMD_STA_ENABLE, NULL); +} + +/** + * @brief Disable the station + * @return 0 on success, -1 on failure + */ +int wlan_sta_disable(void) +{ + return wpa_ctrl_request(WPA_CTRL_CMD_STA_DISABLE, NULL); +} + +/** + * @brief Station scan once + * @return 0 on success, -1 on failure + */ +int wlan_sta_scan_once(void) +{ + return wpa_ctrl_request(WPA_CTRL_CMD_STA_SCAN, NULL); +} + +/** + * @brief Get station scan results + * @param[in] results Pointer to the scan results + * @return 0 on success, -1 on failure + */ +int wlan_sta_scan_result(wlan_sta_scan_results_t *results) +{ + WLAN_ASSERT_POINTER(results); + + return wpa_ctrl_request(WPA_CTRL_CMD_STA_SCAN_RESULTS, results); +} + +/** + * @brief Set station scan interval + * @param[in] sec Scan interval in Seconds + * @return 0 on success, -1 on failure + */ +int wlan_sta_scan_interval(int sec) +{ + return wpa_ctrl_request(WPA_CTRL_CMD_STA_SCAN_INTERVAL, (void *)sec); +} + +/** + * @brief Flush station old BSS entries + * @param[in] age Maximum entry age in seconds + * @return 0 on success, -1 on failure + * + * @note Remove BSS entries that have not been updated during the last @age + * seconds. + */ +int wlan_sta_bss_flush(int age) +{ + return wpa_ctrl_request(WPA_CTRL_CMD_STA_BSS_FLUSH, (void *)age); +} + +/** + * @brief Request a new connection + * @return 0 on success, -1 on failure + */ +int wlan_sta_connect(void) +{ + return wpa_ctrl_request(WPA_CTRL_CMD_STA_REASSOCIATE, NULL); +} + +/** + * @brief Disconnect the current connection + * @return 0 on success, -1 on failure + */ +int wlan_sta_disconnect(void) +{ + return wpa_ctrl_request(WPA_CTRL_CMD_STA_DISCONNECT, NULL); +} + +/** + * @brief Get station connection state + * @param[in] state Pointer to the connection state + * @return 0 on success, -1 on failure + */ +int wlan_sta_state(wlan_sta_states_t *state) +{ + return wpa_ctrl_request(WPA_CTRL_CMD_STA_STATE, state); +} + +/** + * @brief Get the information of connected AP + * @param[in] ap Pointer to the AP information + * @return 0 on success, -1 on failure + */ +int wlan_sta_ap_info(wlan_sta_ap_t *ap) +{ + WLAN_ASSERT_POINTER(ap); + + return wpa_ctrl_request(WPA_CTRL_CMD_STA_AP, ap); +} + +/** + * @brief Start the WPS negotiation with PBC method + * @return 0 on success, -1 on failure + * + * @note WPS will be turned off automatically after two minutes. + */ +int wlan_sta_wps_pbc(void) +{ + return wpa_ctrl_request(WPA_CTRL_CMD_STA_WPS_PBC, NULL); +} + +/** + * @brief Get a random valid WPS PIN + * @param[in] wps Pointer to the WPS pin + * @return 0 on success, -1 on failure + */ +int wlan_sta_wps_pin_get(wlan_sta_wps_pin_t *wps) +{ + WLAN_ASSERT_POINTER(wps); + + return wpa_ctrl_request(WPA_CTRL_CMD_STA_WPS_GET_PIN, wps); +} + +/** + * @brief Start the WPS negotiation with PIN method + * @param[in] wps Pointer to the WPS pin + * @return 0 on success, -1 on failure + * + * @note WPS will be turned off automatically after two minutes. + */ +int wlan_sta_wps_pin_set(wlan_sta_wps_pin_t *wps) +{ + WLAN_ASSERT_POINTER(wps); + + return wpa_ctrl_request(WPA_CTRL_CMD_STA_WPS_SET_PIN, wps); +} + +/** + * @brief Configure AP in a convenient way to build a specified network + * @param[in] ssid Network name, length is [1, 32] + * @param[in] ssid_len The length of network name + * @param[in] psk Network password, in one of the optional formats: + * - NULL, to build an OPEN network + * - an ASCII string, length is [8, 63] + * - a hex string (two characters per octet of PSK), length is 64 + * @return 0 on success, -1 on failure + * + * @note This way is only adapted to build an OPEN or WPA-PSK/WPA2-PSK network. + */ +int wlan_ap_set(uint8_t *ssid, uint8_t ssid_len, uint8_t *psk) +{ + WLAN_ASSERT_POINTER(ssid); + + if ((ssid_len == 0) || (ssid_len > 32)) { + WLAN_ERR("invalid ssid len %d\n", ssid_len); + return -1; + } + + wlan_ap_config_t config; + wlan_memset(&config, 0, sizeof(config)); + + /* ssid */ + config.field = WLAN_AP_FIELD_SSID; + wlan_memcpy(config.u.ssid.ssid, ssid, ssid_len); + config.u.ssid.ssid_len = ssid_len; + if (wpa_ctrl_request(WPA_CTRL_CMD_AP_SET, &config) != 0) + return -1; + + /* auth_alg: OPEN */ + config.field = WLAN_AP_FIELD_AUTH_ALG; + config.u.auth_alg = WPA_AUTH_ALG_OPEN; + if (wpa_ctrl_request(WPA_CTRL_CMD_AP_SET, &config) != 0) + return -1; + + if (psk == NULL) { + /* proto: 0 */ + config.field = WLAN_AP_FIELD_PROTO; + config.u.proto = 0; + if (wpa_ctrl_request(WPA_CTRL_CMD_AP_SET, &config) != 0) + return -1; + + /* key_mgmt: NONE */ + config.field = WLAN_AP_FIELD_KEY_MGMT; + config.u.key_mgmt = WPA_KEY_MGMT_NONE; + if (wpa_ctrl_request(WPA_CTRL_CMD_AP_SET, &config) != 0) + return -1; + } else { + /* psk */ + config.field = WLAN_AP_FIELD_PSK; + wlan_strlcpy((char *)config.u.psk, (char *)psk, sizeof(config.u.psk)); + if (wpa_ctrl_request(WPA_CTRL_CMD_AP_SET, &config) != 0) + return -1; + + /* proto: WPA | RSN */ + config.field = WLAN_AP_FIELD_PROTO; + config.u.proto = WPA_PROTO_WPA | WPA_PROTO_RSN; + if (wpa_ctrl_request(WPA_CTRL_CMD_AP_SET, &config) != 0) + return -1; + + /* key_mgmt: PSK */ + config.field = WLAN_AP_FIELD_KEY_MGMT; + config.u.key_mgmt = WPA_KEY_MGMT_PSK; + if (wpa_ctrl_request(WPA_CTRL_CMD_AP_SET, &config) != 0) + return -1; + } + + /* wpa_cipher: TKIP */ + config.field = WLAN_AP_FIELD_WPA_CIPHER; + config.u.wpa_cipher = WPA_CIPHER_TKIP; + if (wpa_ctrl_request(WPA_CTRL_CMD_AP_SET, &config) != 0) + return -1; + + /* rsn_cipher: CCMP */ + config.field = WLAN_AP_FIELD_RSN_CIPHER; + config.u.rsn_cipher = WPA_CIPHER_CCMP; + if (wpa_ctrl_request(WPA_CTRL_CMD_AP_SET, &config) != 0) + return -1; + + return 0; +} + +/** + * @brief Set AP specified field configuration + * @param[in] config Pointer to the configuration + * @return 0 on success, -1 on failure + */ +int wlan_ap_set_config(wlan_ap_config_t *config) +{ + WLAN_ASSERT_POINTER(config); + + return wpa_ctrl_request(WPA_CTRL_CMD_AP_SET, config); +} + +/** + * @brief Get AP specified field configuration + * @param[in] config Pointer to the configuration + * @return 0 on success, -1 on failure + */ +int wlan_ap_get_config(wlan_ap_config_t *config) +{ + WLAN_ASSERT_POINTER(config); + + return wpa_ctrl_request(WPA_CTRL_CMD_AP_GET, config); +} + +/** + * @brief Enable the AP + * @return 0 on success, -1 on failure + */ +int wlan_ap_enable(void) +{ + return wpa_ctrl_request(WPA_CTRL_CMD_AP_ENABLE, NULL); +} + +/** + * @brief Reload AP configuration + * @return 0 on success, -1 on failure + */ +int wlan_ap_reload(void) +{ + return wpa_ctrl_request(WPA_CTRL_CMD_AP_RELOAD, NULL); +} + +/** + * @brief Disable the AP + * @return 0 on success, -1 on failure + */ +int wlan_ap_disable(void) +{ + return wpa_ctrl_request(WPA_CTRL_CMD_AP_DISABLE, NULL); +} + +/** + * @brief Get the number of connected stations + * @param[in] num Pointer to the number + * @return 0 on success, -1 on failure + */ +int wlan_ap_sta_num(int *num) +{ + WLAN_ASSERT_POINTER(num); + + return wpa_ctrl_request(WPA_CTRL_CMD_AP_STA_NUM, num); +} + +/** + * @brief Get the information of connected stations + * @param[in] stas Pointer to the stations information + * @return 0 on success, -1 on failure + */ +int wlan_ap_sta_info(wlan_ap_stas_t *stas) +{ + WLAN_ASSERT_POINTER(stas); + + return wpa_ctrl_request(WPA_CTRL_CMD_AP_STA_INFO, stas); +} + +#endif /* (defined(__CONFIG_ARCH_DUAL_CORE) && defined(__CONFIG_ARCH_APP_CORE)) */ diff --git a/platform/mcu/xr871/src/net/wlan/wlan.mk b/platform/mcu/xr871/src/net/wlan/wlan.mk new file mode 100644 index 0000000000..4349603a88 --- /dev/null +++ b/platform/mcu/xr871/src/net/wlan/wlan.mk @@ -0,0 +1,13 @@ +NAME := wlan + +$(NAME)_TYPE := kernel +$(NAME)_SOURCES := airkiss/airkiss_ack.c \ + airkiss/airkiss_discover.c \ + airkiss/airkiss_main.c \ + smartconfig/smart_config_ack.c \ + smartconfig/smart_config_crc.c \ + smartconfig/smart_config_decode.c \ + smartconfig/smart_config_main.c \ + ethernetif.c \ + wlan.c \ + wlan_ctrl.c diff --git a/platform/mcu/xr871/src/net/wlan/wlan_ctrl.c b/platform/mcu/xr871/src/net/wlan/wlan_ctrl.c new file mode 100644 index 0000000000..4e96d87b52 --- /dev/null +++ b/platform/mcu/xr871/src/net/wlan/wlan_ctrl.c @@ -0,0 +1,572 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#if (defined(__CONFIG_ARCH_DUAL_CORE) && defined(__CONFIG_ARCH_APP_CORE)) + +#include "wlan_debug.h" +#include "wpa_ctrl_req.h" + +#include "pm/pm.h" +#include "sys/image.h" +#include "net/wlan/wlan.h" +#include "xz/decompress.h" +#include "sys/ducc/ducc_net.h" +#include "sys/ducc/ducc_app.h" +#include "driver/chip/hal_util.h" +#include "driver/chip/hal_ccm.h" +#include "driver/chip/hal_prcm.h" +#include "driver/chip/hal_efuse.h" +#include "kernel/os/os_semaphore.h" + +int wpa_ctrl_request(wpa_ctrl_cmd_t cmd, void *data) +{ + struct ducc_param_wlan_wpa_ctrl_req param; + param.cmd = cmd; + param.data = data; + + return ducc_app_ioctl(DUCC_APP_CMD_WLAN_WPA_CTRL_REQUEST, ¶m); +} + +static __inline int wpa_ctrl_open(void) +{ + return ducc_app_ioctl(DUCC_APP_CMD_WLAN_WPA_CTRL_OPEN, NULL); +} + +static __inline void wpa_ctrl_close(void) +{ + ducc_app_ioctl(DUCC_APP_CMD_WLAN_WPA_CTRL_CLOSE, NULL); +} + +int wlan_start(struct netif *nif) +{ + if (ducc_app_ioctl(DUCC_APP_CMD_WLAN_START, nif->state) != 0) { + return -1; + } + + return wpa_ctrl_open(); +} + +/* Note: make sure wlan is disconnect before calling wlan_stop() */ +int wlan_stop(void) +{ + if (ducc_app_ioctl(DUCC_APP_CMD_WLAN_STOP, NULL) != 0) { + return -1; + } + wpa_ctrl_close(); + return 0; +} + +int wlan_set_mac_addr(uint8_t *mac_addr, int mac_len) +{ + struct ducc_param_wlan_set_mac_addr param; + param.mac_addr = mac_addr; + param.mac_len = mac_len; + return ducc_app_ioctl(DUCC_APP_CMD_WLAN_SET_MAC_ADDR, ¶m); +} + +int wlan_set_ip_addr(struct netif *nif, uint8_t *ip_addr, int ip_len) +{ + struct ducc_param_wlan_set_ip_addr param; + param.ifp = nif->state; + param.ip_addr = ip_addr; + param.ip_len = ip_len; + return ducc_app_ioctl(DUCC_APP_CMD_WLAN_SET_IP_ADDR, ¶m); +} + +/** + * @brief Set application-specified IE to specified management frame + * @param[in] nif Pointer to the network interface + * @param[in] type Management frame type to be set + * eg. (IEEE80211_FC_TYPE_MGMT | IEEE80211_FC_STYPE_BEACON) + * @param[in] ie Pointer to application-specified IE data + * @param[in] ie_len Length of application-specified IE data + * @return 0 on success + * + * @note To delete existing application-specified IE, set ie to NULL, or set + * ie_len to 0. + */ +int wlan_set_appie(struct netif *nif, uint8_t type, uint8_t *ie, uint16_t ie_len) +{ + struct ducc_param_wlan_appie param; + enum wlan_mode mode = ethernetif_get_mode(nif); + + if (mode != WLAN_MODE_STA && mode != WLAN_MODE_HOSTAP) { + WLAN_DBG("unsupport mode %d\n", mode); + return -1; + } + + param.ifp = nif->state; + param.type = type; + param.ie = ie; + param.ie_len = ie_len; + return ducc_app_ioctl(DUCC_APP_CMD_WLAN_SET_APPIE, ¶m); +} + +/* monitor */ +static wlan_monitor_rx_cb m_wlan_monitor_rx_cb = NULL; + +#define DUMP_MON_DATA 0 +#if DUMP_MON_DATA +#define DUMPDLEN 40 +uint8_t dump_data[DUMPDLEN]; +#endif + +void wlan_monitor_input(struct netif *nif, uint8_t *data, uint32_t len, void *info) +{ + #if DUMP_MON_DATA + { + uint32_t dump_len = len > DUMPDLEN ? DUMPDLEN : len; + memset(dump_data, 0, dump_len); + memcpy(dump_data, data, dump_len); + if( dump_data[4] == 0x01 && dump_data[5] == 0x00 && dump_data[6] == 0x5e ) { + for(int i= 4; i < dump_len ;i++) { + printf("0x%02x ", dump_data[i]); + } + printf("\n"); + } + } + #endif + + if (m_wlan_monitor_rx_cb) { + m_wlan_monitor_rx_cb(data, len, info); + } +} + +int wlan_monitor_set_rx_cb(struct netif *nif, wlan_monitor_rx_cb cb) +{ + int enable = cb ? 1 : 0; + m_wlan_monitor_rx_cb = cb; + return ducc_app_ioctl(DUCC_APP_CMD_WLAN_MONITOR_ENABLE_RX, (void *)enable); +} + +int wlan_monitor_set_channel(struct netif *nif, int16_t channel) +{ + struct ducc_param_wlan_mon_set_chan param; + enum wlan_mode mode = ethernetif_get_mode(nif); + + if (mode != WLAN_MODE_MONITOR) { + WLAN_DBG("NOT in monitor mode, current mode %d\n", mode); + return -1; + } + + param.ifp = nif->state; + param.channel = channel; + return ducc_app_ioctl(DUCC_APP_CMD_WLAN_MONITOR_SET_CHAN, ¶m); +} + +/* PM */ +#ifdef CONFIG_PM +static int m_wlan_suspending; + +static int wlan_power_notify(enum suspend_state_t state) +{ + return ducc_app_ioctl(DUCC_APP_CMD_POWER_NOTIFY, (void *)state); +} + +int wlan_wakeup_net(void) +{ + return wlan_power_notify(PM_MODE_ON); +} + +static int wlan_power_callback(int state) +{ + m_wlan_suspending = 0; + + return 0; +} + +static int wlan_sys_suspend(struct soc_device *dev, enum suspend_state_t state) +{ + int err = 0; + uint32_t _timeout = OS_GetTicks() + OS_SecsToJiffies(3); + + switch (state) { + case PM_MODE_STANDBY: + m_wlan_suspending = 1; + wlan_power_notify(PM_MODE_ON); + wlan_power_notify(state); + while (!HAL_PRCM_IsCPUNDeepSleep() && m_wlan_suspending && + OS_TimeBefore(OS_GetTicks(), _timeout)) { + OS_MSleep(5); + } + if (OS_TimeAfterEqual(OS_GetTicks(), _timeout) || !m_wlan_suspending) { + err = -1; + break; + } + OS_MSleep(2); + break; + case PM_MODE_HIBERNATION: + case PM_MODE_POWEROFF: + /* step1: notify net cpu to switch to HOSC, turn off SYSCLK2 and enter WFI state. */ + m_wlan_suspending = 1; + wlan_power_notify(PM_MODE_POWEROFF); + while (!HAL_PRCM_IsCPUNSleep() && m_wlan_suspending) { + OS_MSleep(5); + } + if (!m_wlan_suspending) + WLAN_WARN("wlan poweroff faild!\n"); + OS_MSleep(5); /* wait net cpu enter wfi */ + + /* step2: writel(0x00, GPRCM_SYS2_CRTL) to reset and isolation network system. */ + HAL_PRCM_EnableSys2Isolation(); + HAL_PRCM_ForceCPUNReset(); + HAL_PRCM_ForceSys2Reset(); + OS_MSleep(5); + HAL_PRCM_DisableSys2Power(); + WLAN_DBG("%s okay\n", __func__); + break; + default: + break; + } + + return err; +} + +static int wlan_sys_resume(struct soc_device *dev, enum suspend_state_t state) +{ + switch (state) { + case PM_MODE_STANDBY: + /* maybe wakeup net at this time better than later by other cmds */ + pm_set_sync_magic(); + wlan_power_notify(PM_MODE_ON); + m_wlan_suspending = 0; + WLAN_DBG("%s okay\n", __func__); + break; + default: + break; + } + + return 0; +} + +static struct soc_device_driver m_wlan_sys_drv = { + .name = "wlan_sys", + .suspend = wlan_sys_suspend, + .resume = wlan_sys_resume, +}; + +static struct soc_device m_wlan_sys_dev = { + .name = "wlan_sys", + .driver = &m_wlan_sys_drv, +}; + +#define WLAN_SYS_DEV (&m_wlan_sys_dev) + +#else /* CONFIG_PM */ + +static int wlan_power_callback(int state) +{ + return 0; +} + +#define WLAN_SYS_DEV NULL + +#endif /* CONFIG_PM */ + +static OS_Semaphore_t m_ducc_sync_sem; /* use to sync with net system */ +static ducc_cb_func m_wlan_net_sys_cb = NULL; + +#define COMP_BUF_SIZE (4 * 1024) +#define STREAN_FOOT_LEN 12 + +static uint32_t wlan_net_uncompress_size(uint32_t image_id, section_header_t *sh, uint8_t *buf, uint32_t buf_size) +{ + uint32_t read_len = 0; + + if (buf_size < 1204) { + WLAN_ERR("%s: %d The buf size minimum 1KB, buf_size %d\n", __func__, __LINE__, buf_size); + goto error; + } + + read_len = image_read(image_id, IMAGE_SEG_BODY, sh->body_len - STREAN_FOOT_LEN, buf, STREAN_FOOT_LEN); + if (read_len != STREAN_FOOT_LEN) { + WLAN_ERR("%s: %d read image error, len %d\n", __func__, __LINE__, read_len); + goto error; + } + + uint32_t index_len = xz_index_len(buf); + + read_len = image_read(image_id, IMAGE_SEG_BODY, sh->body_len - STREAN_FOOT_LEN - index_len, buf, index_len); + if (read_len != index_len) { + WLAN_ERR("%s: %d read image error, len %d\n", __func__, __LINE__, read_len); + goto error; + } + + return xz_file_uncompress_size(buf, read_len); + + error: + return 0; +} + +static int wlan_compress_bin(uint32_t image_id, section_header_t *sh) +{ + struct xz_buf stream; + uint32_t read_len = 0; + uint32_t ret; + uint32_t d_len = 0; + uint32_t compress_len = 0; + uint32_t read_count = 0; + uint32_t last_len = 0; + uint16_t checksum = 0; + uint8_t *read_buf = NULL; + int umcompress_sta = 0; + int i = 0; + + read_buf = (uint8_t *)malloc(COMP_BUF_SIZE); + if (!read_buf) { + WLAN_ERR("%s: %d malloc error\n", __func__, __LINE__); + goto error; + } + + checksum += sh->data_chksum; + last_len = sh->body_len % COMP_BUF_SIZE; + read_count = (sh->body_len + COMP_BUF_SIZE - 1 ) & (~(COMP_BUF_SIZE - 1)); + read_count /= COMP_BUF_SIZE; + if (last_len) + read_count -= 1; + + d_len = wlan_net_uncompress_size(image_id, sh, read_buf, COMP_BUF_SIZE); + + if (!xz_uncompress_init(&stream)) { + WLAN_ERR("%s: %d xz uncompress init error\n", __func__, __LINE__); + goto error; + } + + read_len = COMP_BUF_SIZE; + for (i = 0; i <= read_count; i ++) { + if (i == read_count) + read_len = last_len; + + ret = image_read(image_id, IMAGE_SEG_BODY, i * COMP_BUF_SIZE, read_buf, read_len); + if (ret != read_len) { + WLAN_ERR("%s: %d read image error, len %d\n", __func__, __LINE__, ret); + goto error; + } + + checksum += image_get_checksum(read_buf, read_len); + + umcompress_sta = xz_uncompress_stream(&stream, read_buf, read_len, + (uint8_t *)sh->load_addr, d_len, &compress_len); + if (umcompress_sta != XZ_OK && umcompress_sta != XZ_STREAM_END) { + WLAN_ERR("%s: %d uncompress error %d\n", __func__, __LINE__, umcompress_sta); + goto error; + } + sh->load_addr += compress_len; + d_len -= compress_len; + } + + if (checksum != 0xFFFF) { + WLAN_ERR("%s: checksum error error, checksum %d\n", __func__, checksum); + goto error; + } + + xz_uncompress_end(); + free(read_buf); + return 0; +error: + free(read_buf); + return -1; +} + +static int wlan_load_net_bin(enum wlan_mode mode) +{ + section_header_t section_header; + section_header_t *sh = §ion_header; + uint32_t image_id; + + image_id = (mode == WLAN_MODE_HOSTAP) ? IMAGE_NET_AP_ID : IMAGE_NET_ID; + + if (image_read(image_id, IMAGE_SEG_HEADER, 0, sh, IMAGE_HEADER_SIZE) != IMAGE_HEADER_SIZE + || (image_check_header(sh) == IMAGE_INVALID)) { + WLAN_ERR("%s: failed to load net section\n", __func__); + return -1; + } + + if (sh->attribute & (1 << 4)) { + if (wlan_compress_bin(image_id, sh) == -1) + return -1; + } else { + if ((image_read(image_id, IMAGE_SEG_BODY, 0, (void *)sh->load_addr, sh->body_len) != sh->body_len) + || (image_check_data(sh, (void *)sh->load_addr, sh->data_size, NULL, 0) == IMAGE_INVALID)) { + WLAN_ERR("%s: failed to load net section\n", __func__); + return -1; + } + } + + return 0; +} + +static int wlan_get_wlan_bin(int type, int offset, uint8_t *buf, int len) +{ + uint32_t id; + section_header_t section_header; + section_header_t *sh = §ion_header; + + switch (type) { + case DUCC_WLAN_BIN_TYPE_BL: + id = IMAGE_WLAN_BL_ID; + break; + case DUCC_WLAN_BIN_TYPE_FW: + id = IMAGE_WLAN_FW_ID; + break; + case DUCC_WLAN_BIN_TYPE_SDD: + id = IMAGE_WLAN_SDD_ID; + break; + default: + WLAN_ERR("invalid wlan bin type\n"); + return 0; + } + + if (offset == 0) { + if (image_read(id, IMAGE_SEG_HEADER, 0, sh, IMAGE_HEADER_SIZE) != IMAGE_HEADER_SIZE) { + WLAN_ERR("load section (id: %#08x) header failed\n", id); + return 0; + } + if (image_check_header(sh) == IMAGE_INVALID) { + WLAN_ERR("check section (id: %#08x) header failed\n", id); + return 0; + } + + if (len > sh->body_len) + len = sh->body_len; + } + + if (image_read(id, IMAGE_SEG_BODY, offset, buf, len) != len) { + WLAN_ERR("load section (id: %#010x) body failed\n", id); + return 0; + } + + if (offset == 0) + return sh->body_len; + else + return len; +} + +static int wlan_sys_callback(uint32_t param0, uint32_t param1) +{ + struct ducc_param_wlan_bin *p; + struct ducc_param_efuse *efuse; + + switch (param0) { + case DUCC_NET_CMD_SYS_EVENT: + switch (param1) { + case DUCC_NET_SYS_READY: + OS_SemaphoreRelease(&m_ducc_sync_sem); + break; + default: + break; + } + break; + case DUCC_NET_CMD_POWER_NOTIFY: + wlan_power_callback(param1); + break; + case DUCC_NET_CMD_BIN_READ: + p = (struct ducc_param_wlan_bin *)param1; + return wlan_get_wlan_bin(p->type, p->index, p->buf, p->len); + case DUCC_NET_CMD_EFUSE_READ: + efuse = (struct ducc_param_efuse *)param1; + if (HAL_EFUSE_Read(efuse->start_bit, efuse->bit_num, efuse->data) != HAL_OK) + return -1; + default: + if (m_wlan_net_sys_cb) { + return m_wlan_net_sys_cb(param0, param1); + } + break; + } + return 0; +} + +int wlan_sys_init(enum wlan_mode mode, ducc_cb_func cb) +{ +#ifndef __CONFIG_ARCH_MEM_PATCH + HAL_PRCM_DisableSys2(); + HAL_PRCM_DisableSys2Power(); + HAL_PRCM_EnableSys2Power(); +#endif + + m_wlan_net_sys_cb = cb; + OS_SemaphoreCreateBinary(&m_ducc_sync_sem); + +#ifndef __CONFIG_ARCH_MEM_PATCH + HAL_PRCM_DisableSys2Isolation(); + HAL_PRCM_ReleaseSys2Reset(); + HAL_UDelay(500); +#endif + + if (wlan_load_net_bin(mode) != 0) { + WLAN_ERR("%s: wlan load net bin failed\n", __func__); + +#ifndef __CONFIG_ARCH_MEM_PATCH + HAL_PRCM_ForceSys2Reset(); + HAL_PRCM_EnableSys2Isolation(); + HAL_PRCM_DisableSys2Power(); + HAL_PRCM_DisableSys2(); +#endif + + return -1; + } + + struct ducc_app_param param = { wlan_sys_callback }; + ducc_app_start(¶m); + + HAL_PRCM_ReleaseCPUNReset(); + OS_SemaphoreWait(&m_ducc_sync_sem, OS_WAIT_FOREVER); + OS_SemaphoreDelete(&m_ducc_sync_sem); + WLAN_DBG("wlan sys init done\n"); + +#ifdef CONFIG_PM + pm_register_ops(WLAN_SYS_DEV); +#endif + + return 0; +} + +int wlan_sys_deinit(void) +{ +#ifdef CONFIG_PM + pm_unregister_ops(WLAN_SYS_DEV); +#endif + + HAL_PRCM_ForceCPUNReset(); + WLAN_DBG("wlan sys deinit done\n"); + + ducc_app_stop(); + m_wlan_net_sys_cb = NULL; + +#ifndef __CONFIG_ARCH_MEM_PATCH + HAL_PRCM_ForceSys2Reset(); + HAL_PRCM_EnableSys2Isolation(); + HAL_PRCM_DisableSys2Power(); + HAL_PRCM_DisableSys2(); +#endif + + return 0; +} + +#endif /* (defined(__CONFIG_ARCH_DUAL_CORE) && defined(__CONFIG_ARCH_APP_CORE)) */ diff --git a/platform/mcu/xr871/src/net/wlan/wlan_debug.h b/platform/mcu/xr871/src/net/wlan/wlan_debug.h new file mode 100644 index 0000000000..f44294d0b1 --- /dev/null +++ b/platform/mcu/xr871/src/net/wlan/wlan_debug.h @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _NET_WLAN_WLAN_DEBUG_H_ +#define _NET_WLAN_WLAN_DEBUG_H_ + +#include +#include +#include +#include "sys/xr_util.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define wlan_malloc(l) malloc(l) +#define wlan_free(p) free(p) +#define wlan_memcpy(d, s, n) memcpy(d, s, n) +#define wlan_memset(s, c, n) memset(s, c, n) +#define wlan_strlen(s) strlen(s) +#define wlan_strlcpy(d, s, n) strlcpy(d, s, n) + +#define WLAN_DBG_ON 0 +#define WLAN_WARN_ON 1 +#define WLAN_ERR_ON 1 +#define WLAN_ABORT_ON 0 + +#define WLAN_SYSLOG printf +#define WLAN_ABORT() sys_abort() + +#define WLAN_LOG(flags, fmt, arg...) \ + do { \ + if (flags) \ + WLAN_SYSLOG(fmt, ##arg); \ + } while (0) + +#define WLAN_DBG(fmt, arg...) \ + WLAN_LOG(WLAN_DBG_ON, "[wlan] "fmt, ##arg) + +#define WLAN_WARN(fmt, arg...) \ + WLAN_LOG(WLAN_WARN_ON, "[wlan WARN] "fmt, ##arg) + +#define WLAN_ERR(fmt, arg...) \ + do { \ + WLAN_LOG(WLAN_ERR_ON, "[wlan ERR] %s():%d, "fmt, \ + __func__, __LINE__, ##arg); \ + if (WLAN_ABORT_ON) \ + WLAN_ABORT(); \ + } while (0) + +#ifdef __cplusplus +} +#endif + +#endif /* _NET_WLAN_WLAN_DEBUG_H_ */ diff --git a/platform/mcu/xr871/src/net/wlan/wpa_ctrl_req.h b/platform/mcu/xr871/src/net/wlan/wpa_ctrl_req.h new file mode 100644 index 0000000000..12977e8a23 --- /dev/null +++ b/platform/mcu/xr871/src/net/wlan/wpa_ctrl_req.h @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _NET_WLAN_WPA_CTRL_REQ_H_ +#define _NET_WLAN_WPA_CTRL_REQ_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief WPA control command definition + */ +typedef enum wpa_ctrl_cmd { + /* STA */ + WPA_CTRL_CMD_STA_SCAN, + WPA_CTRL_CMD_STA_SCAN_RESULTS, + WPA_CTRL_CMD_STA_SCAN_INTERVAL, + + WPA_CTRL_CMD_STA_REASSOCIATE, + WPA_CTRL_CMD_STA_REATTACH, + WPA_CTRL_CMD_STA_RECONNECT, + WPA_CTRL_CMD_STA_TERMINATE, + WPA_CTRL_CMD_STA_DISCONNECT, + + WPA_CTRL_CMD_STA_ENABLE, + WPA_CTRL_CMD_STA_DISABLE, + + WPA_CTRL_CMD_STA_SET, + WPA_CTRL_CMD_STA_GET, + WPA_CTRL_CMD_STA_AUTOCONNECT, + + WPA_CTRL_CMD_STA_STATE, + WPA_CTRL_CMD_STA_AP, + + WPA_CTRL_CMD_STA_BSS_EXPIRE_AGE, + WPA_CTRL_CMD_STA_BSS_EXPIRE_COUNT, + WPA_CTRL_CMD_STA_BSS_FLUSH, + + WPA_CTRL_CMD_STA_WPS_PBC, + WPA_CTRL_CMD_STA_WPS_GET_PIN, + WPA_CTRL_CMD_STA_WPS_SET_PIN, + + /* softAP */ + WPA_CTRL_CMD_AP_ENABLE, + WPA_CTRL_CMD_AP_RELOAD, + WPA_CTRL_CMD_AP_DISABLE, + WPA_CTRL_CMD_AP_TERMINATE, + + WPA_CTRL_CMD_AP_SET, + WPA_CTRL_CMD_AP_GET, + WPA_CTRL_CMD_AP_STA_NUM, + WPA_CTRL_CMD_AP_STA_INFO, +} wpa_ctrl_cmd_t; + +int wpa_ctrl_request(wpa_ctrl_cmd_t cmd, void *data); + +#ifdef __cplusplus +} +#endif + +#endif /* _NET_WLAN_WPA_CTRL_REQ_H_ */ diff --git a/platform/mcu/xr871/src/ota/ota.c b/platform/mcu/xr871/src/ota/ota.c new file mode 100644 index 0000000000..674bfa1ade --- /dev/null +++ b/platform/mcu/xr871/src/ota/ota.c @@ -0,0 +1,765 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "ota_i.h" +#include "ota_debug.h" +#include "ota_file.h" +#include "ota_http.h" +#include "sys/ota.h" +#include "sys/fdcm.h" +#include "sys/image.h" +#include "driver/chip/hal_crypto.h" +#include "driver/chip/hal_flash.h" +#include "driver/chip/hal_wdg.h" + +static ota_priv_t ota_priv; + +/** + * @brief Initialize the OTA service according to the specified parameters + * @param[in] param Pointer to image_ota_param_t structure + * @retval ota_status_t, OTA_STATUS_OK on success + */ +ota_status_t ota_init(image_ota_param_t *param) +{ + if (param == NULL) { + OTA_ERR("param %p\n", param); + return OTA_STATUS_ERROR; + } + + ota_priv.flash[IMAGE_SEQ_1ST] = param->flash[IMAGE_SEQ_1ST]; + ota_priv.addr[IMAGE_SEQ_1ST] = param->addr[IMAGE_SEQ_1ST]; + ota_priv.flash[IMAGE_SEQ_2ND] = param->flash[IMAGE_SEQ_2ND]; + ota_priv.addr[IMAGE_SEQ_2ND] = param->addr[IMAGE_SEQ_2ND]; + ota_priv.image_size = param->image_size; + ota_priv.boot_size = param->boot_size; + ota_priv.get_size = 0; + + OTA_DBG("%s(), %d, flash_1st %d, addr_1st %#010x, flash_2nd %d, " + "addr_2nd %#010x, image_size %#010x, boot_size %#010x\n", + __func__, __LINE__, + ota_priv.flash[IMAGE_SEQ_1ST], ota_priv.addr[IMAGE_SEQ_1ST], + ota_priv.flash[IMAGE_SEQ_2ND], ota_priv.addr[IMAGE_SEQ_2ND], + ota_priv.image_size, ota_priv.boot_size); + + return OTA_STATUS_OK; +} + +/** + * @brief DeInitialize the OTA service + * @return None + */ +void ota_deinit(void) +{ + ota_priv.flash[IMAGE_SEQ_1ST] = 0; + ota_priv.addr[IMAGE_SEQ_1ST] = IMAGE_INVALID_ADDR; + ota_priv.flash[IMAGE_SEQ_2ND] = 0; + ota_priv.addr[IMAGE_SEQ_2ND] = IMAGE_INVALID_ADDR; + ota_priv.image_size = 0; + ota_priv.boot_size = 0; + ota_priv.get_size = 0; +} + +static ota_status_t ota_read_boot_cfg(ota_boot_cfg_t *boot_cfg) +{ + fdcm_handle_t *fdcm_hdl; + + fdcm_hdl = fdcm_open(ota_priv.flash[IMAGE_SEQ_2ND], ota_priv.addr[IMAGE_SEQ_2ND], ota_priv.boot_size); + if (fdcm_hdl == NULL) { + OTA_ERR("fdcm hdl %p\n", fdcm_hdl); + return OTA_STATUS_ERROR; + } + + if (fdcm_read(fdcm_hdl, boot_cfg, OTA_BOOT_CFG_SIZE) != OTA_BOOT_CFG_SIZE) { + OTA_ERR("fdcm read failed\n"); + fdcm_close(fdcm_hdl); + return OTA_STATUS_ERROR; + } + + OTA_DBG("%s(), %d, boot image %#06x, boot state %#06x\n", + __func__, __LINE__, boot_cfg->boot_image, boot_cfg->boot_state); + + fdcm_close(fdcm_hdl); + return OTA_STATUS_OK; +} + +static ota_status_t ota_write_boot_cfg(ota_boot_cfg_t * boot_cfg) +{ + fdcm_handle_t *fdcm_hdl; + + fdcm_hdl = fdcm_open(ota_priv.flash[IMAGE_SEQ_2ND], ota_priv.addr[IMAGE_SEQ_2ND], ota_priv.boot_size); + if (fdcm_hdl == NULL) { + OTA_ERR("fdcm hdl %p\n", fdcm_hdl); + return OTA_STATUS_ERROR; + } + + OTA_DBG("%s(), %d, boot image %#06x, boot state %#06x\n", + __func__, __LINE__, boot_cfg->boot_image, boot_cfg->boot_state); + + if (fdcm_write(fdcm_hdl, boot_cfg, OTA_BOOT_CFG_SIZE) != OTA_BOOT_CFG_SIZE) { + OTA_ERR("fdcm write failed\n"); + fdcm_close(fdcm_hdl); + return OTA_STATUS_ERROR; + } + + fdcm_close(fdcm_hdl); + return OTA_STATUS_OK; +} + +/** + * @brief Get the OTA configuration + * @param[in] cfg Pointer to OTA configuration + * @retval ota_status_t, OTA_STATUS_OK on success + */ +ota_status_t ota_read_cfg(ota_cfg_t *cfg) +{ + ota_boot_cfg_t boot_cfg; + + if ((cfg == NULL) + || (ota_priv.boot_size == 0) + || (ota_priv.addr[IMAGE_SEQ_2ND] == IMAGE_INVALID_ADDR)) { + OTA_ERR("cfg %p, boot size %#010x, addr 2nd %#010x\n", + cfg, ota_priv.boot_size, ota_priv.addr[IMAGE_SEQ_2ND]); + return OTA_STATUS_ERROR; + } + + if (ota_read_boot_cfg(&boot_cfg) != OTA_STATUS_OK) { + OTA_ERR("read boot cfg failed\n"); + return OTA_STATUS_ERROR; + } + + OTA_DBG("%s(), %d, boot image %#06x, boot state %#06x\n", + __func__, __LINE__, boot_cfg.boot_image, boot_cfg.boot_state); + + if (boot_cfg.boot_image == OTA_BOOT_IMAGE_1ST) { + cfg->image = OTA_IMAGE_1ST; + } else if (boot_cfg.boot_image == OTA_BOOT_IMAGE_2ND) { + cfg->image = OTA_IMAGE_2ND; + } else { + OTA_ERR("boot image %#06x\n", boot_cfg.boot_image); + return OTA_STATUS_ERROR; + } + + if (boot_cfg.boot_state == OTA_BOOT_STATE_UNVERIFIED) { + cfg->state = OTA_STATE_UNVERIFIED; + } else if (boot_cfg.boot_state == OTA_BOOT_STATE_VERIFIED) { + cfg->state = OTA_STATE_VERIFIED; + } else { + OTA_ERR("boot state %#06x\n", boot_cfg.boot_state); + return OTA_STATUS_ERROR; + } + + OTA_DBG("%s(), %d, image %d, state %d\n", __func__, __LINE__, cfg->image, cfg->state); + + return OTA_STATUS_OK; +} + +/** + * @brief Set the OTA configuration + * @param[in] cfg Pointer to OTA configuration + * @retval ota_status_t, OTA_STATUS_OK on success + */ +ota_status_t ota_write_cfg(ota_cfg_t *cfg) +{ + ota_boot_cfg_t boot_cfg; + + if ((cfg == NULL) + || (ota_priv.boot_size == 0) + || (ota_priv.addr[IMAGE_SEQ_2ND] == IMAGE_INVALID_ADDR)) { + OTA_ERR("cfg %p, boot size %#010x, addr 2nd %#010x\n", + cfg, ota_priv.boot_size, ota_priv.addr[IMAGE_SEQ_2ND]); + return OTA_STATUS_ERROR; + } + + OTA_DBG("%s(), %d, image %d, state %d\n", __func__, __LINE__, cfg->image, cfg->state); + + if (cfg->image == OTA_IMAGE_1ST) { + boot_cfg.boot_image = OTA_BOOT_IMAGE_1ST; + } else if (cfg->image == OTA_IMAGE_2ND) { + boot_cfg.boot_image = OTA_BOOT_IMAGE_2ND; + } else { + OTA_ERR("image %d\n", cfg->image); + return OTA_STATUS_ERROR; + } + + if (cfg->state == OTA_STATE_UNVERIFIED) { + boot_cfg.boot_state= OTA_BOOT_STATE_UNVERIFIED; + } else if (cfg->state == OTA_STATE_VERIFIED) { + boot_cfg.boot_state = OTA_BOOT_STATE_VERIFIED; + } else { + OTA_ERR("tate %d\n", cfg->state); + return OTA_STATUS_ERROR; + } + + OTA_DBG("%s(), %d, boot image %#06x, boot state %#06x\n", + __func__, __LINE__, boot_cfg.boot_image, boot_cfg.boot_state); + + if (ota_write_boot_cfg(&boot_cfg) != OTA_STATUS_OK) { + OTA_ERR("write boot cfg failed\n"); + return OTA_STATUS_ERROR; + } + + return OTA_STATUS_OK; +} + +static ota_status_t ota_erase_flash(uint32_t flash, uint32_t addr, uint32_t size) +{ + uint32_t start; + uint32_t multiples; + FlashEraseMode erase_size; + HAL_Status status; + + OTA_DBG("%s(), %d, flash %d, addr %#010x, size %#010x\n", + __func__, __LINE__, flash, addr, size); + + if ((size >= (64 << 10)) + && (HAL_Flash_MemoryOf(flash, FLASH_ERASE_64KB, addr, &start) == HAL_OK) + && (addr == start) + && ((size & 0xFFFF) == 0)) { + multiples = size / (64 << 10); + erase_size = FLASH_ERASE_64KB; + } else if ((size >= (32 << 10)) + && (HAL_Flash_MemoryOf(flash, FLASH_ERASE_32KB, addr, &start) == HAL_OK) + && (addr == start) + && ((size & 0x7FFF) == 0)) { + multiples = size / (32 << 10); + erase_size = FLASH_ERASE_32KB; + } else if ((size >= (4 << 10)) + && (HAL_Flash_MemoryOf(flash, FLASH_ERASE_4KB, addr, &start) == HAL_OK) + && (addr == start) + && ((size & 0xFFF) == 0)) { + multiples = size / (4 << 10); + erase_size = FLASH_ERASE_4KB; + } else { + OTA_ERR("flash %d, addr %#010x, size %#010x\n", flash, addr, size); + return OTA_STATUS_ERROR; + } + + OTA_DBG("%s(), %d, erase size %#010x, multiples %d\n", + __func__, __LINE__, erase_size, multiples); + + if (HAL_Flash_Open(flash, OTA_FLASH_TIMEOUT) != HAL_OK) { + OTA_ERR("flash %d\n", flash); + return OTA_STATUS_ERROR; + } + + status = HAL_Flash_Erase(flash, erase_size, addr, multiples); + if (status != HAL_OK) { + OTA_ERR("status %d\n", status); + HAL_Flash_Close(flash); + return OTA_STATUS_ERROR; + } + + HAL_Flash_Close(flash); + return OTA_STATUS_OK; +} + +static ota_status_t ota_update_image_process(image_seq_t seq, void *url, + ota_update_init_t init_cb, + ota_update_get_t get_cb) +{ + ota_status_t status; + uint32_t flash; + uint32_t addr; + uint32_t boot_size; + uint32_t recv_size; + uint32_t image_size; + uint8_t *ota_buf; + uint8_t eof_flag; + uint8_t cnt; + + flash = ota_priv.flash[seq]; + addr = ota_priv.addr[seq] + ota_priv.boot_size; + + OTA_DBG("%s(), %d, seq %d, flash %d, addr %#010x\n", __func__, __LINE__, seq, flash, addr); + OTA_SYSLOG("OTA: erase flash...\n"); + + if (ota_erase_flash(flash, addr, ota_priv.image_size - ota_priv.boot_size) != OTA_STATUS_OK) { + OTA_ERR("flash %d, addr %#010x, image size %#010x\n", flash, addr, ota_priv.image_size); + return OTA_STATUS_ERROR; + } + + OTA_DBG("%s(), %d, erase flash success\n", __func__, __LINE__); + + if (init_cb(url) != OTA_STATUS_OK) { + OTA_ERR("ota update init failed\n"); + return OTA_STATUS_ERROR; + } + + ota_buf = ota_malloc(OTA_BUF_SIZE); + if (ota_buf == NULL) { + OTA_ERR("ota buf %p\n", ota_buf); + return OTA_STATUS_ERROR; + } + + OTA_SYSLOG("OTA: start loading image...\n"); + cnt = 0; + + ota_priv.get_size = 0; + boot_size = ota_priv.boot_size; + while (boot_size > 0) { + if (boot_size > OTA_BUF_SIZE) + status = get_cb(ota_buf, OTA_BUF_SIZE, &recv_size, &eof_flag); + else + status = get_cb(ota_buf, boot_size, &recv_size, &eof_flag); + + if ((status != OTA_STATUS_OK) || eof_flag) { + OTA_ERR("status %d, eof %d\n", status, eof_flag); + ota_free(ota_buf); + return OTA_STATUS_ERROR; + } + boot_size -= recv_size; + ota_priv.get_size += recv_size; + + cnt++; + if (cnt == 100) { + OTA_SYSLOG("OTA: loading image(%#010x)...\n", ota_priv.get_size); + cnt = 0; + } + } + + OTA_DBG("%s(), %d, load bootloader success\n", __func__, __LINE__); + + if (HAL_Flash_Open(flash, OTA_FLASH_TIMEOUT) != HAL_OK) { + OTA_ERR("flash %d\n", flash); + ota_free(ota_buf); + return OTA_STATUS_ERROR; + } + + image_size = ota_priv.image_size; + while (image_size > 0) { + if (image_size > OTA_BUF_SIZE) + status = get_cb(ota_buf, OTA_BUF_SIZE, &recv_size, &eof_flag); + else + status = get_cb(ota_buf, image_size, &recv_size, &eof_flag); + + if (status != OTA_STATUS_OK) { + OTA_ERR("status %d\n", status); + HAL_Flash_Close(flash); + ota_free(ota_buf); + return OTA_STATUS_ERROR; + } + image_size -= recv_size; + ota_priv.get_size += recv_size; + + if (HAL_Flash_Write(flash, addr, ota_buf, recv_size) != HAL_OK) { + OTA_ERR("flash %d, addr %#010x, size %#010x\n", flash, addr, recv_size); + HAL_Flash_Close(flash); + ota_free(ota_buf); + return OTA_STATUS_ERROR; + } + addr += recv_size; + if (eof_flag) + break; + + cnt++; + if (cnt == 100) { + OTA_SYSLOG("OTA: loading image(%#010x)...\n", ota_priv.get_size); + cnt = 0; + } + } + + HAL_Flash_Close(flash); + ota_free(ota_buf); + + OTA_SYSLOG("OTA: finish loading image(%#010x).\n", ota_priv.get_size); + + if (image_check_sections(seq) != IMAGE_VALID) { + OTA_ERR("ota check image failed\n"); + return OTA_STATUS_ERROR; + } + + OTA_SYSLOG("OTA: finish checking image.\n"); + return OTA_STATUS_OK; +} + +static ota_status_t ota_update_image(void *url, + ota_update_init_t init_cb, + ota_update_get_t get_cb) +{ + ota_boot_cfg_t boot_cfg; + image_seq_t seq; + + if ((ota_priv.image_size == 0) || (ota_priv.boot_size == 0)) { + OTA_ERR("need to init, image size %#010x, boot size %#010x\n", + ota_priv.image_size, ota_priv.boot_size); + return OTA_STATUS_ERROR; + } + + if (ota_read_boot_cfg(&boot_cfg) != OTA_STATUS_OK) { + OTA_ERR("read boot cfg failed\n"); + return OTA_STATUS_ERROR; + } + + OTA_DBG("%s(), %d, boot image %#06x, boot state %#06x\n", + __func__, __LINE__, boot_cfg.boot_image, boot_cfg.boot_state); + + if (((boot_cfg.boot_image == OTA_BOOT_IMAGE_1ST) && (boot_cfg.boot_state == OTA_BOOT_STATE_UNVERIFIED)) + || ((boot_cfg.boot_image == OTA_BOOT_IMAGE_2ND) && (boot_cfg.boot_state == OTA_BOOT_STATE_VERIFIED))) { + seq = IMAGE_SEQ_1ST; + boot_cfg.boot_image = OTA_BOOT_IMAGE_1ST; + } else if (((boot_cfg.boot_image == OTA_BOOT_IMAGE_1ST) && (boot_cfg.boot_state == OTA_BOOT_STATE_VERIFIED)) + || ((boot_cfg.boot_image == OTA_BOOT_IMAGE_2ND) && (boot_cfg.boot_state == OTA_BOOT_STATE_UNVERIFIED))) { + seq = IMAGE_SEQ_2ND; + boot_cfg.boot_image = OTA_BOOT_IMAGE_2ND; + } else { + OTA_ERR("image %#06x, state %#06x\n", boot_cfg.boot_image, boot_cfg.boot_state); + return OTA_STATUS_ERROR; + } + + OTA_DBG("%s(), %d, seq %d\n", __func__, __LINE__, seq); + + if (ota_update_image_process(seq, url, init_cb, get_cb) != OTA_STATUS_OK) { + OTA_ERR("ota update image failed\n"); + return OTA_STATUS_ERROR; + } + + boot_cfg.boot_state = OTA_BOOT_STATE_UNVERIFIED; + return ota_write_boot_cfg(&boot_cfg); +} + +/** + * @brief Get the image file with the specified protocol and write to flash + * @param[in] protocol Pointer to the protocol of getting image file + * @param[in] url URL of the image file + * @retval ota_status_t, OTA_STATUS_OK on success + */ +ota_status_t ota_get_image(ota_protocol_t protocol, void *url) +{ + if (url == NULL) { + OTA_ERR("url %p\n", url); + return OTA_STATUS_ERROR; + } + + switch (protocol) { + case OTA_PROTOCOL_FILE: + return ota_update_image(url, ota_update_file_init, ota_update_file_get); + case OTA_PROTOCOL_HTTP: + return ota_update_image(url, ota_update_http_init, ota_update_http_get); + default: + OTA_ERR("invalid protocol %d\n", protocol); + return OTA_STATUS_ERROR; + } +} + +static ota_status_t ota_verify_image_append_process(uint32_t flash, + uint32_t addr, + uint32_t size, + ota_verify_append_t append, + void *hdl) +{ + uint32_t read_size; + uint8_t *buf; + + buf = ota_malloc(OTA_BUF_SIZE); + if (buf == NULL) { + OTA_ERR("buf %p\n", buf); + return OTA_STATUS_ERROR; + } + + if (HAL_Flash_Open(flash, OTA_FLASH_TIMEOUT) != HAL_OK) { + OTA_ERR("flash %d\n", flash); + ota_free(buf); + return OTA_STATUS_ERROR; + } + + while (size > 0) { + read_size = size > OTA_BUF_SIZE ? OTA_BUF_SIZE : size; + if ((HAL_Flash_Read(flash, addr, buf, read_size) != HAL_OK) + || (append(hdl, buf, read_size) != HAL_OK)) { + OTA_ERR("flash %d, addr %#010x, size %#010x\n", flash, addr, read_size); + HAL_Flash_Close(flash); + ota_free(buf); + return OTA_STATUS_ERROR; + } + size -= read_size; + addr += read_size; + } + + HAL_Flash_Close(flash); + ota_free(buf); + return OTA_STATUS_OK; +} + +static ota_status_t ota_verify_image_append(ota_boot_cfg_t *cfg, + ota_verify_append_t append, + void *hdl) +{ + image_seq_t seq; + + if (cfg->boot_image == OTA_BOOT_IMAGE_1ST) { + seq = IMAGE_SEQ_1ST; + } else if (cfg->boot_image == OTA_BOOT_IMAGE_2ND) { + seq = IMAGE_SEQ_2ND; + } else { + OTA_ERR("invalid boot image %#06x\n", cfg->boot_image); + return OTA_STATUS_ERROR; + } + + OTA_DBG("%s(), %d, image %#06x, seq %d\n", __func__, __LINE__, cfg->boot_image, seq); + + if ((ota_verify_image_append_process(ota_priv.flash[IMAGE_SEQ_1ST], + ota_priv.addr[IMAGE_SEQ_1ST], + ota_priv.boot_size, + append, + hdl) != OTA_STATUS_OK) + || (ota_verify_image_append_process(ota_priv.flash[seq], + ota_priv.addr[seq] + ota_priv.boot_size, + ota_priv.get_size - ota_priv.boot_size, + append, + hdl) != OTA_STATUS_OK)) { + OTA_ERR("append failed, seq %d\n", seq); + return OTA_STATUS_ERROR; + } + + return OTA_STATUS_OK; +} + +static inline ota_status_t ota_verify_image_none(ota_boot_cfg_t *cfg, uint32_t *value) +{ + cfg->boot_state = OTA_BOOT_STATE_VERIFIED; + return OTA_STATUS_OK; +} + +static ota_status_t ota_verify_image_crc32(ota_boot_cfg_t *cfg, uint32_t *value) +{ + CE_CRC_Handler hdl; + uint32_t crc; + + if (HAL_CRC_Init(&hdl, CE_CRC32, ota_priv.get_size) != HAL_OK) { + OTA_ERR("CRC init failed\n"); + return OTA_STATUS_ERROR; + } + + if (ota_verify_image_append(cfg, + (ota_verify_append_t)HAL_CRC_Append, + (void *)&hdl) != OTA_STATUS_OK) { + OTA_ERR("CRC append failed\n"); + return OTA_STATUS_ERROR; + } + + if (HAL_CRC_Finish(&hdl, &crc) != HAL_OK) { + OTA_ERR("CRC finish failed\n"); + return OTA_STATUS_ERROR; + } + + if (ota_memcmp(value, &crc, sizeof(crc)) == 0) { + cfg->boot_state = OTA_BOOT_STATE_VERIFIED; + } else { + OTA_WARN("%s(), %d, value %#010x, crc %#010x\n", + __func__, __LINE__, *value, crc); + cfg->boot_state = OTA_BOOT_STATE_UNVERIFIED; + } + + OTA_DBG("%s(), %d, state %#06x, value %#010x, crc %#010x\n", + __func__, __LINE__, cfg->boot_state, *value, crc); + + return OTA_STATUS_OK; +} + +static ota_status_t ota_verify_image_md5(ota_boot_cfg_t *cfg, uint32_t *value) +{ + CE_MD5_Handler hdl; + uint32_t digest[4]; + + if (HAL_MD5_Init(&hdl, CE_CTL_IVMODE_SHA_MD5_FIPS180, NULL) != HAL_OK) { + OTA_ERR("MD5 init failed\n"); + return OTA_STATUS_ERROR; + } + + if (ota_verify_image_append(cfg, + (ota_verify_append_t)HAL_MD5_Append, + (void *)&hdl) != OTA_STATUS_OK) { + OTA_ERR("MD5 append failed\n"); + return OTA_STATUS_ERROR; + } + + if (HAL_MD5_Finish(&hdl, digest) != HAL_OK) { + OTA_ERR("MD5 finish failed\n"); + return OTA_STATUS_ERROR; + } + + if (ota_memcmp(value, digest, sizeof(digest)) == 0) { + cfg->boot_state = OTA_BOOT_STATE_VERIFIED; + } else { + OTA_WARN("%s(), %d, value[0] %#010x, digest[0] %#010x\n", + __func__, __LINE__, value[0], digest[0]); + cfg->boot_state = OTA_BOOT_STATE_UNVERIFIED; + } + + OTA_DBG("%s(), %d, state %#06x, value[0] %#010x, digest[0] %#010x\n", + __func__, __LINE__, cfg->boot_state, value[0], digest[0]); + + return OTA_STATUS_OK; +} + +static ota_status_t ota_verify_image_sha1(ota_boot_cfg_t *cfg, uint32_t *value) +{ + CE_SHA1_Handler hdl; + uint32_t digest[5]; + + if (HAL_SHA1_Init(&hdl, CE_CTL_IVMODE_SHA_MD5_FIPS180, NULL) != HAL_OK) { + OTA_ERR("SHA1 init failed\n"); + return OTA_STATUS_ERROR; + } + + if (ota_verify_image_append(cfg, + (ota_verify_append_t)HAL_SHA1_Append, + (void *)&hdl) != OTA_STATUS_OK) { + OTA_ERR("SHA1 append failed\n"); + return OTA_STATUS_ERROR; + } + + if (HAL_SHA1_Finish(&hdl, digest) != HAL_OK) { + OTA_ERR("SHA1 finish failed\n"); + return OTA_STATUS_ERROR; + } + + if (ota_memcmp(value, digest, sizeof(digest)) == 0) { + cfg->boot_state = OTA_BOOT_STATE_VERIFIED; + } else { + OTA_WARN("%s(), %d, value[0] %#010x, digest[0] %#010x\n", + __func__, __LINE__, value[0], digest[0]); + cfg->boot_state = OTA_BOOT_STATE_UNVERIFIED; + } + + OTA_DBG("%s(), %d, state %#06x, value[0] %#010x, digest[0] %#010x\n", + __func__, __LINE__, cfg->boot_state, value[0], digest[0]); + + return OTA_STATUS_OK; +} + +static ota_status_t ota_verify_image_sha256(ota_boot_cfg_t *cfg, uint32_t *value) +{ + CE_SHA256_Handler hdl; + uint32_t digest[8]; + + if (HAL_SHA256_Init(&hdl, CE_CTL_IVMODE_SHA_MD5_FIPS180, NULL) != HAL_OK) { + OTA_ERR("SHA256 init failed\n"); + return OTA_STATUS_ERROR; + } + + if (ota_verify_image_append(cfg, + (ota_verify_append_t)HAL_SHA256_Append, + (void *)&hdl) != OTA_STATUS_OK) { + OTA_ERR("SHA256 append failed\n"); + return OTA_STATUS_ERROR; + } + + if (HAL_SHA256_Finish(&hdl, digest) != HAL_OK) { + OTA_ERR("SHA256 finish failed\n"); + return OTA_STATUS_ERROR; + } + + if (ota_memcmp(value, digest, sizeof(digest)) == 0) { + cfg->boot_state = OTA_BOOT_STATE_VERIFIED; + } else { + OTA_WARN("%s(), %d, value[0] %#010x, digest[0] %#010x\n", + __func__, __LINE__, value[0], digest[0]); + cfg->boot_state = OTA_BOOT_STATE_UNVERIFIED; + } + + OTA_DBG("%s(), %d, state %#06x, value[0] %#010x, digest[0] %#010x\n", + __func__, __LINE__, cfg->boot_state, value[0], digest[0]); + + return OTA_STATUS_OK; +} + +/** + * @brief Verify the image file and modify OTA configuration correspondingly + * @param[in] verify Verification algorithm + * @param[in] value Pointer to standard value of the verification algorithm + * @retval ota_status_t, OTA_STATUS_OK on success + */ +ota_status_t ota_verify_image(ota_verify_t verify, uint32_t *value) +{ + ota_boot_cfg_t boot_cfg; + ota_status_t status; + + if ((verify != OTA_VERIFY_NONE) && (value == NULL)) { + OTA_ERR("verify %d, res %p\n", verify, value); + return OTA_STATUS_ERROR; + } + + if (ota_priv.get_size == 0) { + OTA_ERR("need to get image, get size %#010x\n", ota_priv.get_size); + return OTA_STATUS_ERROR; + } + + OTA_DBG("%s(), %d, verify %d, get size %#010x\n", + __func__, __LINE__, verify, ota_priv.get_size); + + if (ota_read_boot_cfg(&boot_cfg) != OTA_STATUS_OK) { + OTA_ERR("read boot cfg failed\n"); + return OTA_STATUS_ERROR; + } + + switch (verify) { + case OTA_VERIFY_NONE: + status = ota_verify_image_none(&boot_cfg, value); + break; + case OTA_VERIFY_CRC32: + status = ota_verify_image_crc32(&boot_cfg, value); + break; + case OTA_VERIFY_MD5: + status = ota_verify_image_md5(&boot_cfg, value); + break; + case OTA_VERIFY_SHA1: + status = ota_verify_image_sha1(&boot_cfg, value); + break; + case OTA_VERIFY_SHA256: + status = ota_verify_image_sha256(&boot_cfg, value); + break; + default: + OTA_ERR("invalid verify %d\n", verify); + return OTA_STATUS_ERROR; + } + + if (status != OTA_STATUS_OK) { + OTA_ERR("status %d, verify %d\n", status, verify); + return OTA_STATUS_ERROR; + } + + return ota_write_boot_cfg(&boot_cfg); +} + +/** + * @brief Reboot system + * @return None + */ +void ota_reboot_system(void) +{ + OTA_DBG("%s(), %d, OTA: reboot.\n", __func__, __LINE__); + HAL_WDG_Reboot(); +} + +void ota_set_size(uint32_t size) +{ + ota_priv.get_size = size; + +} diff --git a/platform/mcu/xr871/src/ota/ota.mk b/platform/mcu/xr871/src/ota/ota.mk new file mode 100644 index 0000000000..d0d5ec0b51 --- /dev/null +++ b/platform/mcu/xr871/src/ota/ota.mk @@ -0,0 +1,6 @@ +NAME := ota + +$(NAME)_TYPE := kernel +$(NAME)_SOURCES := ota.c \ + ota_file.c \ + ota_http.c diff --git a/platform/mcu/xr871/src/ota/ota_debug.h b/platform/mcu/xr871/src/ota/ota_debug.h new file mode 100644 index 0000000000..d87fd727db --- /dev/null +++ b/platform/mcu/xr871/src/ota/ota_debug.h @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _OTA_DEBUG_H_ +#define _OTA_DEBUG_H_ + +#include +#include "sys/xr_util.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define OTA_DBG_ON 0 +#define OTA_WARN_ON 1 +#define OTA_ERR_ON 1 +#define OTA_ABORT_ON 0 + +#define OTA_SYSLOG printf +#define OTA_ABORT() sys_abort() + +#define OTA_LOG(flags, fmt, arg...) \ + do { \ + if (flags) \ + OTA_SYSLOG(fmt, ##arg); \ + } while (0) + +#define OTA_DBG(fmt, arg...) OTA_LOG(OTA_DBG_ON, "[OTA] "fmt, ##arg) +#define OTA_WARN(fmt, arg...) OTA_LOG(OTA_WARN_ON, "[OTA WARN] "fmt, ##arg) +#define OTA_ERR(fmt, arg...) \ + do { \ + OTA_LOG(OTA_ERR_ON, "[OTA ERR] %s():%d, "fmt, \ + __func__, __LINE__, ##arg); \ + if (OTA_ABORT_ON) \ + OTA_ABORT(); \ + } while (0) + +#ifdef __cplusplus +} +#endif + +#endif /* _OTA_DEBUG_H_ */ diff --git a/platform/mcu/xr871/src/ota/ota_file.c b/platform/mcu/xr871/src/ota/ota_file.c new file mode 100644 index 0000000000..4d5948189b --- /dev/null +++ b/platform/mcu/xr871/src/ota/ota_file.c @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include "ota_i.h" +#include "ota_debug.h" +#include "ota_file.h" +#include "ff.h" + +typedef struct ota_fs_param { + char *url; + FRESULT res; + FIL file; +} ota_fs_param_t; + +static ota_fs_param_t g_fs_param; + +ota_status_t ota_update_file_init(void *url) +{ + g_fs_param.url = strdup((char *)url + 7); + + g_fs_param.res = f_open(&g_fs_param.file, g_fs_param.url, FA_READ | FA_OPEN_EXISTING); + if (g_fs_param.res != FR_OK) { + OTA_ERR("open res %d\n", g_fs_param.res); + return OTA_STATUS_ERROR; + } + + OTA_DBG("%s(), %d, open success\n", __func__, __LINE__); + + return OTA_STATUS_OK; +} + +ota_status_t ota_update_file_get(uint8_t *buf, uint32_t buf_size, uint32_t *recv_size, uint8_t *eof_flag) +{ + g_fs_param.res = f_read(&g_fs_param.file, buf, buf_size, recv_size); + if (g_fs_param.res != FR_OK) { + OTA_ERR("read res %d\n", g_fs_param.res); + return OTA_STATUS_ERROR; + } + + if (*recv_size < buf_size) { + *eof_flag = 1; + f_close(&g_fs_param.file); + } else + *eof_flag = 0; + + return OTA_STATUS_OK; +} + diff --git a/platform/mcu/xr871/src/ota/ota_file.h b/platform/mcu/xr871/src/ota/ota_file.h new file mode 100644 index 0000000000..57d9d83c4f --- /dev/null +++ b/platform/mcu/xr871/src/ota/ota_file.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _OTA_FILE_H_ +#define _OTA_FILE_H_ + +#include "types.h" +#include "sys/ota.h" + +#ifdef __cplusplus +extern "C" { +#endif + +ota_status_t ota_update_file_init(void *url); +ota_status_t ota_update_file_get(uint8_t *buf, uint32_t buf_size, uint32_t *recv_size, uint8_t *eof_flag); + +#ifdef __cplusplus +} +#endif + +#endif /* _OTA_FILE_H_ */ diff --git a/platform/mcu/xr871/src/ota/ota_http.c b/platform/mcu/xr871/src/ota/ota_http.c new file mode 100644 index 0000000000..a3bf609848 --- /dev/null +++ b/platform/mcu/xr871/src/ota/ota_http.c @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include "ota_i.h" +#include "ota_debug.h" +#include "ota_http.h" +//TODO: #include "net/HTTPClient/HTTPCUsr_api.h" + +//TODO: static HTTPParameters g_http_param; + +ota_status_t ota_update_http_init(void *url) +{ +//TODO: ota_memset(&g_http_param, 0, sizeof(g_http_param)); +//TODO: ota_memcpy(g_http_param.Uri, url, strlen(url)); + + return OTA_STATUS_OK; +} + +ota_status_t ota_update_http_get(uint8_t *buf, uint32_t buf_size, uint32_t *recv_size, uint8_t *eof_flag) +{ +//TODO: int ret; +//TODO: +//TODO: ret = HTTPC_get(&g_http_param, (CHAR *)buf, (INT32)buf_size, (INT32 *)recv_size); +//TODO: if (ret == HTTP_CLIENT_SUCCESS) { +//TODO: *eof_flag = 0; +//TODO: return OTA_STATUS_OK; +//TODO: } else if (ret == HTTP_CLIENT_EOS) { +//TODO: *eof_flag = 1; +//TODO: return OTA_STATUS_OK; +//TODO: } else { +//TODO: OTA_ERR("ret %d\n", ret); +//TODO: return OTA_STATUS_ERROR; +//TODO: } + return OTA_STATUS_ERROR; +} + diff --git a/platform/mcu/xr871/src/ota/ota_http.h b/platform/mcu/xr871/src/ota/ota_http.h new file mode 100644 index 0000000000..c3b25988a2 --- /dev/null +++ b/platform/mcu/xr871/src/ota/ota_http.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _OTA_HTTP_H_ +#define _OTA_HTTP_H_ + +#include "types.h" +#include "sys/ota.h" + +#ifdef __cplusplus +extern "C" { +#endif + +ota_status_t ota_update_http_init(void *url); +ota_status_t ota_update_http_get(uint8_t *buf, uint32_t buf_size, uint32_t *recv_size, uint8_t *eof_flag); + +#ifdef __cplusplus +} +#endif + +#endif /* _OTA_HTTP_H_ */ diff --git a/platform/mcu/xr871/src/ota/ota_i.h b/platform/mcu/xr871/src/ota/ota_i.h new file mode 100644 index 0000000000..744f4def0f --- /dev/null +++ b/platform/mcu/xr871/src/ota/ota_i.h @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _OTA_I_H_ +#define _OTA_I_H_ + +#include +#include "types.h" +#include "sys/ota.h" +#include "driver/chip/hal_crypto.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define ota_malloc(l) malloc(l) +#define ota_free(p) free(p) +#define ota_memcpy(d, s, n) memcpy(d, s, n) +#define ota_memset(s, c, n) memset(s, c, n) +#define ota_memcmp(a, b, l) memcmp(a, b, l) + +#define OTA_BUF_SIZE (2 << 10) +#define OTA_FLASH_TIMEOUT (5000) + +#define OTA_BOOT_IMAGE_1ST (0x5555) +#define OTA_BOOT_IMAGE_2ND (0xAAAA) +#define OTA_BOOT_STATE_UNVERIFIED (0x6996) +#define OTA_BOOT_STATE_VERIFIED (0x9669) + +typedef struct { + uint16_t boot_image; + uint16_t boot_state; +} ota_boot_cfg_t; + +#define OTA_BOOT_CFG_SIZE sizeof(ota_boot_cfg_t) + +typedef struct { + uint32_t flash[IMAGE_SEQ_NUM]; + uint32_t addr[IMAGE_SEQ_NUM]; + uint32_t image_size; + uint32_t boot_size; + uint32_t get_size; +} ota_priv_t; + +typedef ota_status_t (*ota_update_init_t)(void *url); +typedef ota_status_t (*ota_update_get_t)(uint8_t *buf, uint32_t buf_size, uint32_t *recv_size, uint8_t *eof_flag); + +typedef HAL_Status (*ota_verify_append_t)(void *hdl, uint8_t *data, uint32_t size); + +#ifdef __cplusplus +} +#endif + +#endif /* _OTA_I_H_ */ diff --git a/platform/mcu/xr871/src/pm/pm.c b/platform/mcu/xr871/src/pm/pm.c new file mode 100644 index 0000000000..c8b5762d6e --- /dev/null +++ b/platform/mcu/xr871/src/pm/pm.c @@ -0,0 +1,1058 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include "stdlib.h" + +#include "sys/io.h" +#include "errno.h" +#include "sys/list.h" +#include "sys/interrupt.h" +#include "sys/param.h" +#include "kernel/os/os_thread.h" + +#include "driver/chip/system_chip.h" +#include "driver/chip/chip.h" +#include "driver/chip/hal_wakeup.h" +#include "driver/chip/hal_prcm.h" +#include "driver/chip/hal_ccm.h" +#include "driver/chip/hal_util.h" + +#include "pm/pm.h" +#include "pm_i.h" +#include "port.h" + +#ifdef CONFIG_PM + +typedef enum { + PM_SHUTDOWN = 0, + PM_REBOOT = 1, +} pm_operate_t; + +#define nop() __NOP() +#define isb() __ISB() +#define dsb() __DSB() +#define dmb() __DMB() +#define wfi() __WFI() +#define wfe() __WFE() + +#define PM_SYS "appos" +#define PM_SetCPUBootFlag(f) HAL_PRCM_SetCPUABootFlag(f) +#define PM_SetCPUBootArg(a) HAL_PRCM_SetCPUABootArg(a) +#define PM_SystemDeinit() SystemDeInit(SYSTEM_DEINIT_FLAG_RESET_CLK) +#define pm_udelay(us) HAL_UDelay(us) + +static struct arm_CMX_core_regs vault_arm_registers; +#define __set_last_record_step(s) HAL_PRCM_SetCPUAPrivateData(s) +#define __get_last_record_step() HAL_PRCM_GetCPUAPrivateData() + +static const char *const pm_states[PM_MODE_MAX] = { + [PM_MODE_ON] = "on", + [PM_MODE_SLEEP] = "sleep", + [PM_MODE_STANDBY] = "standby", + [PM_MODE_HIBERNATION] = "hibernation", + [PM_MODE_POWEROFF] = "poweroff", +}; + +static int __suspend_begin(enum suspend_state_t state) +{ + /* set SEVONPEND flag */ + if (state < PM_MODE_POWEROFF) { + SCB->SCR = 0x10; + } else { + SCB->SCR = 0x14; + } + return 0; +} + +static inline void __record_dbg_status(int val) +{ + __set_last_record_step(val); + dsb(); + isb(); +} + +/* power off whole system, save user data to flash before call this func. + * BE SUURE: all hardware has been closed and it's prcm config setted to default value. + * @type:0:shutdown, 1:reboot */ +static void pm_power_off(pm_operate_t type) +{ + __record_dbg_status(PM_POWEROFF | 0); + + if (type == PM_REBOOT) { + NVIC_SystemReset(); /* never return */ + } + +#ifdef __CONFIG_ARCH_APP_CORE + /* step 1 & 2 has been done when wlan sys poweroff */ + /* step3: writel(0x0f, GPRCM_SYS1_WAKEUP_CTRL) to tell PMU that turn on + * SW1, SW2, SRSW1, LDO before release application system reset signal. */ + HAL_PRCM_SetSys1WakeupPowerFlags(0x0f); + __record_dbg_status(PM_POWEROFF | 5); + + /* step4: writel(0x0f, GPRCM_SYS1_SLEEP_CTRL) to tell PMU that turn off SW1, + * SW3 SRSW1, LDO after pull down application system reset signal. */ + HAL_PRCM_SetSys1SleepPowerFlags(0x0f); + __record_dbg_status(PM_POWEROFF | 7); + + /* step5: switch to HOSC, close SYS1_CLK. */ + PM_SystemDeinit(); + __record_dbg_status(PM_POWEROFF | 9); + + /* step6: set nvic deepsleep flag, and enter wfe. */ + PM_SetCPUBootFlag(0); + + __disable_fault_irq(); + __disable_irq(); + + while (1) { + wfe(); + } + __record_dbg_status(PM_POWEROFF | 0x0ff); + +#else /* net cpu */ + /* check wifi is closed by app? */ + PM_WARN_ON(HAL_PRCM_IsSys3Release()); + + /* step1: cpu to switch to HOSC */ + HAL_PRCM_SetCPUNClk(PRCM_CPU_CLK_SRC_HFCLK, PRCM_SYS_CLK_FACTOR_80M); + __record_dbg_status(PM_POWEROFF | 1); + + /* step2: turn off SYSCLK2. */ + HAL_PRCM_DisableSysClk2(PRCM_SYS_CLK_FACTOR_80M); + __record_dbg_status(PM_POWEROFF | 3); + PM_SystemDeinit(); + + /* step3: enter WFI state */ + arch_suspend_disable_irqs(); + while (1) + wfi(); + __record_dbg_status(PM_POWEROFF | 0x0ff); +#endif +} + +static void __suspend_enter(enum suspend_state_t state) +{ + __record_dbg_status(PM_SUSPEND_ENTER | 5); + + if (HAL_Wakeup_SetSrc()) + return ; + + debug_jtag_deinit(); + __record_dbg_status(PM_SUSPEND_ENTER | 6); + + PM_LOGN("device info. rst:%x clk:%x\n", CCM->BUS_PERIPH_RST_CTRL, + CCM->BUS_PERIPH_CLK_CTRL); /* debug info. */ + + PM_SetCPUBootArg((uint32_t)&vault_arm_registers); + + if (state == PM_MODE_POWEROFF) { +#ifdef __CONFIG_ARCH_APP_CORE + HAL_Wakeup_SetIOHold((1 << WAKEUP_IO_MAX) - 1); +#endif + pm_power_off(PM_SHUTDOWN); /* never return */ + } else if (state < PM_MODE_STANDBY) { + /* TODO: set system bus to low freq */ + __cpu_sleep(state); + /* TODO: restore system bus to normal freq */ + } else { + __cpu_suspend(state); + } + + PM_BUG_ON(!arch_irq_get_flags()); + + debug_jtag_init(); + + HAL_Wakeup_ClrSrc(); +} + +static void __suspend_end(enum suspend_state_t state) +{ + /* clear SEVONPEND flag */ + SCB->SCR = 0x0; +} + +static const struct platform_suspend_ops suspend_ops = +{ + .begin = __suspend_begin, + .prepare = platform_prepare, + .enter = __suspend_enter, + .wake = platform_wake, + .end = __suspend_end, +}; + +static volatile int user_sel_power_mode = PM_MODE_ON; + +static int valid_state(enum suspend_state_t state) +{ + if (user_sel_power_mode == PM_MODE_ON) + return 0; + + return (user_sel_power_mode == state); +} + +#ifdef CONFIG_PM_DEBUG +static int pm_test_level = TEST_NONE; + +static unsigned long suspend_test_start_time; + +static void suspend_test_start(void) +{ + suspend_test_start_time = ktime_get(); +} + +static void suspend_test_finish(const char *label) +{ + long nj = ktime_get() - suspend_test_start_time; + unsigned msec; + + msec = ktime_to_msecs(abs(nj)); + PM_LOGN("%s took %d ms\n", label, msec); +} + +static int suspend_test(int level) +{ + if (pm_test_level == level) { + PM_LOGD("suspend debug:%d Return.\n", level); + return 1; + } + return 0; +} + +/** + * @brief Set suspend test level. + * @param level: + * @arg level->Suspend will exit when run up to setted level. + */ +void pm_set_test_level(enum suspend_test_level_t level) +{ + pm_test_level = level; +} + +void pm_dump_regs(unsigned int flag) +{ + if (flag & 1<<0) { /* cpu */ + int i, j; + + PM_LOGD("regs:\n"); + PM_LOGD("msp:0x%08x, psp:0x%08x, psr:0x%08x, primask:0x%08x\n", + vault_arm_registers.msp, vault_arm_registers.psp, + vault_arm_registers.psr, vault_arm_registers.primask); + PM_LOGD("faultmask:0x%08x, basepri:0x%08x, control:0x%08x\n", + vault_arm_registers.faultmask, vault_arm_registers.basepri, + vault_arm_registers.control); + for (i = 0; i < 3; i++) { + for (j = 0; j < 4; j++) { + PM_LOGD("reg[%d]:0x%08x ", j+i*4, + vault_arm_registers.reg12[j+i*4]); + } + PM_LOGD("\n"); + } + PM_LOGD("last step:%x\n", __get_last_record_step()); + } + if (flag & 1<<1) { /* nvic */ + //nvic_print_regs(); + } + if (flag & 1<<2) { /* ccmu */ + //ccm_print_regs(); + } + if (flag & 1<<3) { /* gpio */ + //gpio_print_regs(); + } +} + +static void dpm_show_time(ktime_t starttime, enum suspend_state_t state, char *info) +{ + ktime_t calltime; + + calltime = ktime_get(); + PM_LOGD("%s of devices complete after %d ms\n", info ? info : "", (int)(calltime - starttime)); +} + +#else +#define suspend_test_start() +#define suspend_test_finish(x...) +#define suspend_test(l) 0 +void pm_set_test_level(enum suspend_test_level_t level) {;} +#define pm_dump_regs(flag) +#define dpm_show_time(x...) +#endif + +static LIST_HEAD_DEF(dpm_late_early_list); +static LIST_HEAD_DEF(dpm_noirq_list); + +static LIST_HEAD_DEF(dpm_list); +static LIST_HEAD_DEF(dpm_suspended_list); + +#ifdef CONFIG_PM_DEBUG +static struct suspend_stats suspend_stats; + +void parse_dpm_list(struct list_head *head, unsigned int idx) +{ + struct list_head *list; + struct soc_device *dev; + + PM_DBG("(%p)", head); + list_for_each(list, head) { + dev = to_device(list, idx); + PM_DBG("-->%s(%p)", dev->name, dev); + } + PM_DBG("\n"); +} +#endif + +static unsigned int initcall_debug_delay_us = 0; + +/** + * @brief Set delay ms in debug mode. + * @note To prevent mutual interference between devices. + * @param ms: + * @arg ms->The delayed ms between two devices. + */ +void pm_set_debug_delay_ms(unsigned int ms) +{ + initcall_debug_delay_us = ms * 1000; +} + +/** + * check_wakeup_irqs - check if any wake-up interrupts are pending + */ +int check_wakeup_irqs(void) +{ + int i; + unsigned int *addr; + unsigned int val; + + /* Check if there are any interrupts pending */ + addr = (unsigned int *)NVIC->ISPR; + for (i = 0; i < DIV_ROUND_UP(NVIC_PERIPH_IRQ_NUM, 32); i++) { + val= addr[i]; + if (val & nvic_int_mask[i]) { + PM_LOGN("nvic[%d]:%x, mask:%x\n", i, val, nvic_int_mask[i]); + return 1; + } + } + + return 0; +} + +/** + * dpm_suspend_noirq - Execute "noirq suspend" callbacks for all devices. + * @state: PM transition of the system being carried out. + * + * Prevent device drivers from receiving interrupts and call the "noirq" suspend + * handlers for all non-sysdev devices. + */ +static int dpm_suspend_noirq(enum suspend_state_t state) +{ + struct soc_device *dev = NULL; +#ifdef CONFIG_PM_DEBUG + ktime_t starttime = ktime_get(); +#endif + int error = 0; + + while (!list_empty(&dpm_late_early_list)) { + dev = to_device(dpm_late_early_list.next, PM_OP_NOIRQ); + + get_device(dev); + + error = dev->driver->suspend_noirq(dev, state); + if (initcall_debug_delay_us > 0) { + PM_LOGD("sleep %d ms for debug.\n", initcall_debug_delay_us); + pm_udelay(initcall_debug_delay_us); + } + + if (error || !arch_irq_get_flags()) { + PM_LOGE("%s suspend noirq failed! primask:%d\n", + dev->name, (int)arch_irq_get_flags()); + put_device(dev); + break; + } + list_move(&dev->node[PM_OP_NOIRQ], &dpm_noirq_list); + dsb(); + isb(); + put_device(dev); + + if (check_wakeup_irqs()) { + error = -1; + break; + } + } + +#ifdef CONFIG_PM_DEBUG + if (error) { + suspend_stats.failed_suspend_noirq++; + if (dev && dev->name) + memcpy(suspend_stats.failed_devs, dev->name, MAX_DEV_NAME); + suspend_stats.last_failed_step = TEST_DEVICES; + dpm_show_time(starttime, state, "noirq"); + } +#endif + + return error; +} + +/** + * dpm_suspend - Execute "suspend" callbacks for all devices. + * @state: PM transition of the system being carried out. + */ +static int dpm_suspend(enum suspend_state_t state) +{ + struct soc_device *dev = NULL; +#ifdef CONFIG_PM_DEBUG + ktime_t starttime = ktime_get(); +#endif + int error = 0; + + while (!list_empty(&dpm_list)) { + dev = to_device(dpm_list.next, PM_OP_NORMAL); + + get_device(dev); + error = dev->driver->suspend(dev, state); + if (initcall_debug_delay_us > 0) { + PM_LOGD("sleep %d ms for debug.\n", initcall_debug_delay_us); + pm_udelay(initcall_debug_delay_us); + } + + if (error) { + PM_LOGE("%s suspend failed!\n", dev->name); + put_device(dev); + break; + } + list_move(&dev->node[PM_OP_NORMAL], &dpm_suspended_list); + dsb(); + isb(); + put_device(dev); + } + +#ifdef CONFIG_PM_DEBUG + if (error) { + suspend_stats.failed_suspend++; + if (dev && dev->name) + memcpy(suspend_stats.failed_devs, dev->name, MAX_DEV_NAME); + suspend_stats.last_failed_step = TEST_DEVICES; + dpm_show_time(starttime, state, "suspend"); + } +#endif + + return error; +} + +/** + * dpm_resume_noirq - Execute "noirq resume" callbacks for all devices. + * @state: PM transition of the system being carried out. + * + * Call the "noirq" resume handlers for all devices in dpm_noirq_list and + * enable device drivers to receive interrupts. + */ +static void dpm_resume_noirq(enum suspend_state_t state) +{ + struct soc_device *dev; + ktime_t starttime = ktime_get(); + int error; + + while (!list_empty(&dpm_noirq_list)) { + dev = to_device(dpm_noirq_list.next, PM_OP_NOIRQ); + + get_device(dev); + list_move(&dev->node[PM_OP_NOIRQ], &dpm_late_early_list); + dsb(); + isb(); + + error = dev->driver->resume_noirq(dev, state); +#ifdef CONFIG_PM_DEBUG + if (error) { + suspend_stats.failed_resume_noirq++; + PM_LOGE("%s resume noirq failed!\n", dev->name); + } +#else + (void)error; + (void)starttime; +#endif + + put_device(dev); + } + dpm_show_time(starttime, state, "noirq"); +} + +/** + * dpm_resume - Execute "resume" callbacks for non-sysdev devices. + * @state: PM transition of the system being carried out. + * + * Execute the appropriate "resume" callback for all devices whose status + * indicates that they are suspended. + */ +static void dpm_resume(enum suspend_state_t state) +{ + struct soc_device *dev; + ktime_t starttime = ktime_get(); + int error; + + while (!list_empty(&dpm_suspended_list)) { + dev = to_device(dpm_suspended_list.next, PM_OP_NORMAL); + + get_device(dev); + list_move(&dev->node[PM_OP_NORMAL], &dpm_list); + dsb(); + isb(); + + error = dev->driver->resume(dev, state); +#ifdef CONFIG_PM_DEBUG + if (error) { + suspend_stats.failed_resume++; + PM_LOGE("%s resume failed!\n", dev->name); + } +#else + (void)error; + (void)starttime; +#endif + + put_device(dev); + } + dpm_show_time(starttime, state, "resume"); +} + +/** + * suspend_enter - Make the system enter the given sleep state. + * @state: System sleep state to enter. + * @wakeup: Returns information that the sleep state should not be re-entered. + * + * This function should be called after devices have been suspended. + */ +static int suspend_enter(enum suspend_state_t state) +{ + int wakeup; + int error; + + if (suspend_ops.prepare) { + error = suspend_ops.prepare(state); + if (error) + goto Platform_finish; + } + + arch_suspend_disable_irqs(); + + wakeup = check_wakeup_irqs(); + if (wakeup) { + error = -1; + goto Platform_finish; + } + + __record_dbg_status(PM_SUSPEND_DEVICES | 0x10); + suspend_test_start(); + error = dpm_suspend_noirq(state); + if (error) { +#ifdef CONFIG_PM_DEBUG + suspend_stats.fail++; + PM_LOGE("Some devices noirq failed to suspend\n"); + parse_dpm_list(&dpm_noirq_list, PM_OP_NOIRQ); +#endif + goto Resume_noirq_devices; + } + + suspend_test_finish("suspend noirq devices"); + if (suspend_test(TEST_DEVICES)) + goto Resume_noirq_devices; + + __record_dbg_status(PM_SUSPEND_ENTER); + + __record_dbg_status(PM_SUSPEND_ENTER | 1); + if (suspend_test(TEST_PLATFORM)) + goto Platform_wake; + + __record_dbg_status(PM_SUSPEND_ENTER | 2); + wakeup = check_wakeup_irqs(); + if (!(suspend_test(TEST_CORE) || wakeup)) { + __record_dbg_status(PM_SUSPEND_ENTER | 3); + suspend_ops.enter(state); + __record_dbg_status(PM_SUSPEND_ENTER | 4); + } + +Platform_wake: + if (suspend_ops.wake) + suspend_ops.wake(state); + +Resume_noirq_devices: + __record_dbg_status(PM_RESUME_DEVICES | 0x10); + suspend_test_start(); + dpm_resume_noirq(state); + suspend_test_finish("resume noirq devices"); + +Platform_finish: + arch_suspend_enable_irqs(); + + return wakeup; +} + +/** + * suspend_devices_and_enter - Suspend devices and enter system sleep state. + * @state: System sleep state to enter. + */ +static int suspend_devices_and_enter(enum suspend_state_t state) +{ + int error; + + __record_dbg_status(PM_EARLY_SUSPEND); + if (!valid_state(state)) { + return -1; + } + + __record_dbg_status(PM_SUSPEND_BEGIN); + if (suspend_ops.begin) { + error = suspend_ops.begin(state); + if (error) + goto Close; + } + + __record_dbg_status(PM_SUSPEND_DEVICES); + suspend_test_start(); + error = dpm_suspend(state); + if (error) { +#ifdef CONFIG_PM_DEBUG + suspend_stats.fail++; +#endif + PM_LOGE("Some devices failed to suspend\n"); + goto Resume_devices; + } +#ifdef CONFIG_PM_DEBUG + suspend_stats.success++; +#endif + suspend_test_finish("suspend devices"); + if (suspend_test(TEST_DEVICES)) + goto Resume_devices; + + OS_ThreadSuspendScheduler(); + __record_dbg_status(PM_SUSPEND_ENTER); + suspend_enter(state); + OS_ThreadResumeScheduler(); + +Resume_devices: + __record_dbg_status(PM_RESUME_DEVICES); + suspend_test_start(); + dpm_resume(state); + suspend_test_finish("resume devices"); + +Close: + __record_dbg_status(PM_RESUME_END); + if (suspend_ops.end) + suspend_ops.end(state); + __record_dbg_status(PM_RESUME_COMPLETE); + + return error; +} + +/** + * @brief Register a set of system core operations. + * @note Not use printf for this func maybe called very earlier. + * @param dev: + * @arg dev->Device will be registered. + * @retval 0 if success or other if failed. + */ +int pm_register_ops(struct soc_device *dev) +{ + struct list_head *hd; + struct soc_device *dev_c; + unsigned long flags; + unsigned int valid; + + if (!dev) + return -EINVAL; + + valid = dev->node[PM_OP_NORMAL].next || dev->node[PM_OP_NORMAL].prev; + if (valid) + PM_BUG_ON(!list_empty(&dev->node[PM_OP_NORMAL])); + valid = dev->node[PM_OP_NOIRQ].next || dev->node[PM_OP_NOIRQ].prev; + if (valid) + PM_BUG_ON((!list_empty(&dev->node[PM_OP_NOIRQ]))); + PM_BUG_ON(!dev->driver || + ((!dev->driver->suspend_noirq || !dev->driver->resume_noirq) && + (!dev->driver->suspend || !dev->driver->resume))); + + if (dev->driver->suspend || dev->driver->resume) { + PM_BUG_ON(!dev->driver->suspend || !dev->driver->resume); + list_for_each(hd, &dpm_list) { + dev_c = to_device(hd, PM_OP_NORMAL); + if (dev_c == dev) { + goto next; + } + } + + INIT_LIST_HEAD(&dev->node[PM_OP_NORMAL]); + flags = arch_irq_save(); + list_add(&dev->node[PM_OP_NORMAL], &dpm_list); + arch_irq_restore(flags); + } + +next: + if (dev->driver->suspend_noirq || dev->driver->resume_noirq) { + PM_BUG_ON(!dev->driver->suspend_noirq || !dev->driver->resume_noirq); + list_for_each(hd, &dpm_late_early_list) { + dev_c = to_device(hd, PM_OP_NOIRQ); + if (dev_c == dev) { + return -1; + } + } + + INIT_LIST_HEAD(&dev->node[PM_OP_NOIRQ]); + flags = arch_irq_save(); + list_add(&dev->node[PM_OP_NOIRQ], &dpm_late_early_list); + arch_irq_restore(flags); + } + + return 0; +} + +/** + * @brief Unregister a set of system core operations. + * @param dev: + * @arg dev->Device will be unregistered. + * @retval 0 if success or other if failed. + */ +int pm_unregister_ops(struct soc_device *dev) +{ + unsigned long flags; + + if (!dev) + return -EINVAL; + + if (dev->driver->suspend) { + PM_BUG_ON(!dev->node[PM_OP_NORMAL].next || !dev->node[PM_OP_NORMAL].prev); + PM_BUG_ON(list_empty(&dev->node[PM_OP_NORMAL])); + } + if (dev->driver->suspend_noirq) { + PM_BUG_ON(!dev->node[PM_OP_NOIRQ].next || !dev->node[PM_OP_NOIRQ].prev); + PM_BUG_ON(list_empty(&dev->node[PM_OP_NOIRQ])); + } + + flags = arch_irq_save(); + if (dev->driver->suspend) + list_del(&dev->node[PM_OP_NORMAL]); + if (dev->driver->suspend_noirq) + list_del(&dev->node[PM_OP_NOIRQ]); + arch_irq_restore(flags); + + return 0; +} + +void pm_select_mode(enum suspend_state_t state) +{ + user_sel_power_mode = state; +} + +#ifdef CONFIG_PM_DEBUG +/** @brief Show suspend statistic info. */ +void pm_stats_show(void) +{ + PM_LOGN("suspend state:\n" + " success:%d\n" + " fail:%d\n" + " failed_suspend:%d\n" + " failed_resume:%d\n" + " last_failed_step:%d\n" + " last_failed_device:%s\n" + " last_wakeup_event:%x\n", + suspend_stats.success, suspend_stats.fail, + suspend_stats.failed_suspend, suspend_stats.failed_resume, + suspend_stats.last_failed_step, suspend_stats.failed_devs, + HAL_Wakeup_GetEvent()); +} +#else +void pm_stats_show(void) +{ +} +#endif + +/** + * @brief Initialize the PM-related part of a device object. + * @note not use printf for this fun is called very earlier. + * @retval 0 if success or other if failed. + */ +int pm_init(void) +{ + HAL_Wakeup_Init(); + +#ifdef __CONFIG_ARCH_APP_CORE +#if 0 + HAL_PRCM_EnableSys2Power(); + pm_udelay(10000); + HAL_PRCM_DisableSys2Power(); + pm_udelay(10000); +#endif + + /* set prcm to default value for prcm keep it's last time value. */ + HAL_PRCM_SetSys1WakeupPowerFlags(0x0f); + HAL_PRCM_SetSys1SleepPowerFlags(0x0e); + HAL_PRCM_Start(); +#endif + + return 0; +} + +#ifdef __CONFIG_ARCH_APP_CORE +static pm_wlan_power_onoff pm_wlan_power_onoff_cb = NULL; +static int pm_wlan_mode_platform_config = PM_SUPPORT_HIBERNATION | PM_SUPPORT_POWEROFF; + +/** + * @brief Select wlan power modes when enter pm. + * @note Wlan power on/off calback will called when pm enter select modes. + * @param wlan_power_cb: + * @arg wlan_power_cb->Wlan power on/off calback. + * @param select: + * @arg select->The selected modes set. + * retval 0 if success or other if failed. + */ +int pm_register_wlan_power_onoff(pm_wlan_power_onoff wlan_power_cb, unsigned int select) +{ + if ((select & (PM_SUPPORT_HIBERNATION | PM_SUPPORT_POWEROFF)) != + (PM_SUPPORT_HIBERNATION | PM_SUPPORT_POWEROFF)) { + PM_LOGW("wlan should power off when hibernateion/poweroff!\n"); + return -1; + } + pm_wlan_power_onoff_cb = wlan_power_cb; + pm_wlan_mode_platform_config = select | PM_SUPPORT_HIBERNATION | PM_SUPPORT_POWEROFF; + PM_LOGN("wlan mode:%x\n", pm_wlan_mode_platform_config); + + return 0; +} + +/** @brief unregister wlan power on/off callback. */ +void pm_unregister_wlan_power_onoff(void) +{ + pm_wlan_power_onoff_cb = NULL; +} +#endif + +#ifdef __CONFIG_ARCH_APP_CORE +static int pm_mode_platform_config = PM_SUPPORT_SLEEP | PM_SUPPORT_STANDBY | PM_SUPPORT_POWEROFF; + +/** + * @brief Select pm modes used on this platform. + * @note Select modes at init for some modes are not used on some platforms. + * This will prevent enter unselect modes. + * @param select: + * @arg select->The selected modes set. + */ +void pm_mode_platform_select(unsigned int select) +{ + pm_mode_platform_config = select; + if (!(select & (PM_SUPPORT_SLEEP | PM_SUPPORT_STANDBY | \ + PM_SUPPORT_HIBERNATION | PM_SUPPORT_POWEROFF))) { + PM_LOGW("slect wrong mode!\n"); + return ; + } + PM_LOGN("mode select:%x\n", select); +} + +static int pm_wlan_alive_platform_config = PM_SUPPORT_SLEEP | PM_SUPPORT_STANDBY; + +int pm_wlan_alive_platform_select(unsigned int select) +{ + if (select & (PM_SUPPORT_HIBERNATION | PM_SUPPORT_POWEROFF)) { + PM_LOGW("net can't be alive when hiberantion or poweroff!\n"); + return -1; + } + pm_wlan_alive_platform_config = select; + PM_LOGN("net alive select:%x\n", select); + + return 0; +} + +/** + * @brief Set a magin to synchronize with net. + */ +void pm_set_sync_magic(void) +{ + PM_SetCPUBootArg(PM_SYNC_MAGIC); /* set flag to notify net to run */ +} +#endif + + +/** + * @brief Set system to a lowpower mode. + * @param state: + * @arg state->The lowpower mode will enter. + * @retval 0 if success or other if failed. + */ +int pm_enter_mode(enum suspend_state_t state) +{ + int err, record; + enum suspend_state_t state_use = state; +#ifdef __CONFIG_ARCH_APP_CORE + int net_alive, loop; + + if (!(pm_mode_platform_config & (1 << state))) { + for (loop = (1 << state_use); loop; loop >>= 1) { + if (pm_mode_platform_config & loop) { + break; + } + state_use--; + } + } +#endif + + PM_BUG_ON(state_use >= PM_MODE_MAX); + + if (state_use < PM_MODE_SLEEP) + return 0; + + pm_select_mode(state_use); + PM_LOGD(PM_SYS" enter mode: %s\n", pm_states[state_use]); + record = __get_last_record_step(); + if (record != PM_RESUME_COMPLETE) + PM_LOGN("last suspend record:%x\n", record); +#ifdef CONFIG_PM_DEBUG + parse_dpm_list(&dpm_list, PM_OP_NORMAL); /* debug info. */ + parse_dpm_list(&dpm_late_early_list, PM_OP_NOIRQ); +#endif +#ifdef __CONFIG_ARCH_APP_CORE + net_alive = HAL_PRCM_IsCPUNReleased(); + if (net_alive && (pm_wlan_mode_platform_config & (1 << state_use)) && pm_wlan_power_onoff_cb) { + pm_wlan_power_onoff_cb(0); + } +#endif + + err = suspend_devices_and_enter(state_use); + +#ifdef __CONFIG_ARCH_APP_CORE + pm_set_sync_magic(); + + if (net_alive && (pm_wlan_mode_platform_config & (1 << state_use)) && pm_wlan_power_onoff_cb) { + pm_wlan_power_onoff_cb(1); + } +#endif + + return err; +} + +//#define CONFIG_PM_TEST 1 + +#ifdef CONFIG_PM_TEST + +static int test_suspend_noirq(struct soc_device *dev, enum suspend_state_t state) +{ + PM_LOGD("%s %s okay\n", dev->name, __func__); + + //if (!strcmp(dev->name, "dev_test1")) + // return -1; + //else + return 0; +} + +static int test_resume_noirq(struct soc_device *dev, enum suspend_state_t state) +{ + PM_LOGD("%s %s okay\n", dev->name, __func__); + + return 0; +} + +static int test_suspend(struct soc_device *dev, enum suspend_state_t state) +{ + PM_LOGD("%s %s okay\n", dev->name, __func__); + + //if (!strcmp(dev->name, "dev_test1")) + // return -1; + //else + return 0; +} + +static int test_resume(struct soc_device *dev, enum suspend_state_t state) +{ + PM_LOGD("%s %s okay\n", dev->name, __func__); + + return 0; +} + +static struct soc_device_driver drv_test1 = { + .name = "drv_test1", + .suspend = &test_suspend, + .resume = &test_resume, +}; +static struct soc_device dev_test1 = { + .name = "dev_test1", + .driver = &drv_test1, +}; + +static struct soc_device_driver drv_test2 = { + .name = "drv_test2", + .suspend = &test_suspend, + .resume = &test_resume, +}; +static struct soc_device dev_test2 = { + .name = "dev_test2", + .driver = &drv_test2, +}; + +static struct soc_device_driver drv_test3 = { + .name = "drv_test3", + .suspend_noirq = &test_suspend_noirq, + .resume_noirq = &test_resume_noirq, + .suspend = &test_suspend, + .resume = &test_resume, +}; +static struct soc_device dev_test3 = { + .name = "dev_test3", + .driver = &drv_test3, +}; + +int pm_test(void) +{ + struct list_head *hd = &dpm_list; + struct soc_device *dev; + + pm_register_ops(&dev_test1); + pm_register_ops(&dev_test2); + pm_register_ops(&dev_test2); + pm_register_ops(&dev_test3); + + parse_dpm_list(&dpm_list, PM_OP_NORMAL); + parse_dpm_list(&dpm_late_early_list, PM_OP_NOIRQ); + + PM_LOGD("dpm_list:\n"); + while (!list_is_last(hd, &dpm_list)) { + dev = to_device(hd->next, PM_OP_NORMAL); + hd = hd->next; + PM_LOGD("name: %s\n", dev->name); + } + + PM_LOGD("dpm_late_early_list:\n"); + hd = &dpm_late_early_list; + while (!list_is_last(hd, &dpm_late_early_list)) { + dev = to_device(hd->next, PM_OP_NOIRQ); + hd = hd->next; + PM_LOGD("name: %s\n", dev->name); + } + + return 0; +} +#endif + +#endif /* CONFIG_PM */ diff --git a/platform/mcu/xr871/src/pm/pm.mk b/platform/mcu/xr871/src/pm/pm.mk new file mode 100644 index 0000000000..2861c76179 --- /dev/null +++ b/platform/mcu/xr871/src/pm/pm.mk @@ -0,0 +1,5 @@ +NAME := pm + +$(NAME)_TYPE := kernel +$(NAME)_SOURCES := pm.c \ + port.c diff --git a/platform/mcu/xr871/src/pm/pm_i.h b/platform/mcu/xr871/src/pm/pm_i.h new file mode 100644 index 0000000000..3c2a626548 --- /dev/null +++ b/platform/mcu/xr871/src/pm/pm_i.h @@ -0,0 +1,150 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __PM_H +#define __PM_H + +#include "pm/pm.h" + +#ifdef CONFIG_PM + +#define CONFIG_PM_DEBUG 1 + +#ifdef CONFIG_PM_DEBUG +#define PM_DBG(format, args...) do {printf(format, ##args);} while (0) +#define PM_LOGD(format, args...) do {printf("PM: "format, ##args);} while (0) +#define PM_LOGN(format, args...) do {printf("PM: "format, ##args);} while (0) +#define PM_LOGW(format, args...) do {printf("PM: WAR "format, ##args);} while (0) +#define PM_LOGE(format, args...) do {printf("PM: ERR "format, ##args);} while (0) +#define PM_LOGA(format, args...) do {printf("PM: "format, ##args);} while (0) +#else +#define PM_DBG(x...) +#define PM_LOGD(x...) +#define PM_LOGN(x...) +#define PM_LOGW(x...) +#define PM_LOGE(x...) +#define PM_LOGA(x...) +#endif +#define PM_BUG_ON(v) do {if(v) {printf("PM: BUG at %s:%d!\n", __func__, __LINE__); while (1);}} while (0) +#define PM_WARN_ON(v) do {if(v) {printf("PM: WARN at %s:%d!\n", __func__, __LINE__);}} while (0) + +#ifdef CONFIG_PM_DEBUG +#define MAX_DEV_NAME 40 +struct suspend_stats { + int success; + int fail; + int failed_suspend_noirq; + int failed_suspend; + int failed_resume; + int failed_resume_noirq; + char failed_devs[MAX_DEV_NAME]; + int last_failed_step; +}; +#endif + +#define PM_EARLY_SUSPEND (1<<16) +#define PM_SUSPEND_BEGIN (2<<16) +#define PM_SUSPEND_DEVICES (3<<16) +#define PM_SUSPEND_ENTER (4<<16) +#define PM_RESUME_SYSTEM (6<<16) +#define PM_RESUME_DEVICES (7<<16) +#define PM_RESUME_END (8<<16) +#define PM_RESUME_COMPLETE (9<<16) +#define PM_RESUME_ERROR (0x0e<<16) +#define PM_SUSPEND_FAIL_FLAG (0xFFFF) +#define PM_FIRST_BOOT_FLAG (0x0000) + +#define PM_POWEROFF (0x0a<<16) + +#define PM_SYNC_MAGIC (0x7FF2DCCD) + +/** + * @brief Data constructs for implementation in assembly. + * @note systick saved by timer subsys. + */ +struct arm_CMX_core_regs { + unsigned int msp; + unsigned int psp; + unsigned int psr; + unsigned int primask; + unsigned int faultmask; + unsigned int basepri; + unsigned int control; + unsigned int reg12[16]; /* used only for debug */ +}; + +/** + * @brief Callbacks for managing platform dependent system sleep states. + * + * @begin: Initialise a transition to given system sleep state. + * @begin() is executed right prior to suspending devices. The information + * conveyed to the platform code by @begin() should be disregarded by it as + * soon as @end() is executed. If @begin() fails (ie. returns nonzero), + * @prepare(), @enter() and @finish() will not be called. + * + * @prepare: Prepare the platform for entering the system sleep state indicated. + * @prepare() is called right after devices have been suspended (ie. the + * appropriate .suspend() method has been executed for each device) and + * before device drivers' late suspend callbacks are executed. It returns + * 0 on success or a negative error code otherwise, in which case the + * system cannot enter the desired sleep state. + * + * @enter: Enter the system sleep state indicated. + * It returns 0 on success or a negative error code otherwise, in which + * case the system cannot enter the desired sleep state. + * + * @wake: Called when the system has just left a sleep state, right after + * the CPU have been enabled and before device drivers' early resume + * callbacks are executed. + * It is always called after @enter(). + * + * @end: Called after resuming devices, to indicate to the platform that the + * system has returned to the working state or the transition to the sleep + * state has been aborted. + * Platforms implementing @begin() should also provide a @end() which + * cleans up transitions aborted before @enter(). + */ +struct platform_suspend_ops { + int (*begin)(enum suspend_state_t state); + int (*prepare)(enum suspend_state_t state); + void (*enter)(enum suspend_state_t state); + void (*wake)(enum suspend_state_t state); + void (*end)(enum suspend_state_t state); +}; + +#define to_device(ptr_module, idx) \ + __containerof(ptr_module, struct soc_device, node[idx]) +#define get_device(d) d->ref++ +#define put_device(d) d->ref-- + +extern void __cpu_sleep(enum suspend_state_t state); +extern void __cpu_suspend(enum suspend_state_t state); +#endif + +#endif diff --git a/platform/mcu/xr871/src/pm/port.c b/platform/mcu/xr871/src/pm/port.c new file mode 100644 index 0000000000..c91774fbd6 --- /dev/null +++ b/platform/mcu/xr871/src/pm/port.c @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include + +#include "sys/io.h" +#include "errno.h" +#include "sys/list.h" + +#include "driver/chip/chip.h" +#include "driver/chip/hal_wakeup.h" +#include "driver/chip/hal_prcm.h" +#include "driver/chip/hal_nvic.h" + +#include "pm/pm.h" +#include "pm_i.h" +#include "port.h" + +#ifdef CONFIG_PM + +/* It is better that all interrupts are managed by itself, the interrupt should + * be disable by it's driver if it is not used during suspend. We use this mask + * for a sample concentrated cometrue, 1: use this irq as wakeup source */ +unsigned int nvic_int_mask[] = { + NVIC_PERIPH_IRQ_MASK0, +#if (MAX_IRQn > 32) + NVIC_PERIPH_IRQ_MASK1, +#endif +}; + +ct_assert((sizeof(nvic_int_mask) + 3) / 4 >= (NVIC_PERIPH_IRQ_NUM + 31)/32); + +void debug_jtag_init(void) +{ +#ifdef CONFIG_PM_DEBUG + /* at this time gpio is reset state */ +/* + GPIO_InitParam param; + + param.mode = GPIOB_P2_F2_SWD_TMS; + param.driving = GPIO_DRIVING_LEVEL_1; + param.pull = GPIO_PULL_UP; + HAL_GPIO_Init(GPIO_PORT_B, GPIO_PIN_2, ¶m); + + param.mode = GPIOB_P3_F2_SWD_TCK; + param.driving = GPIO_DRIVING_LEVEL_1; + param.pull = GPIO_PULL_UP; + HAL_GPIO_Init(GPIO_PORT_B, GPIO_PIN_3, ¶m); +*/ +#endif +} + +void debug_jtag_deinit(void) +{ +#ifdef CONFIG_PM_DEBUG + //HAL_GPIO_DeInit(GPIO_PORT_B, GPIO_PIN_2); + //HAL_GPIO_DeInit(GPIO_PORT_B, GPIO_PIN_3); +#endif +} + +static NVIC_IRQHandler uart_rx_back; + +static void uart_rx_callback(void) +{ + HAL_NVIC_DisableIRQ(N_UART_IRQn); + HAL_NVIC_ClearPendingIRQ(N_UART_IRQn); +} + +int platform_prepare(enum suspend_state_t state) +{ + uart_rx_back = HAL_NVIC_GetIRQHandler(N_UART_IRQn); + + HAL_NVIC_SetIRQHandler(N_UART_IRQn, uart_rx_callback); + HAL_NVIC_EnableIRQ(N_UART_IRQn); + + return 0; +} + +void platform_wake(enum suspend_state_t state) +{ + HAL_NVIC_EnableIRQ(N_UART_IRQn); + __ISB(); + __NOP(); + HAL_NVIC_SetIRQHandler(N_UART_IRQn, uart_rx_back); +} + +#endif diff --git a/platform/mcu/xr871/src/pm/port.h b/platform/mcu/xr871/src/pm/port.h new file mode 100644 index 0000000000..2ac8ec6a99 --- /dev/null +++ b/platform/mcu/xr871/src/pm/port.h @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __PM_PORT_H +#define __PM_PORT_H + +#include "sys/interrupt.h" +#include "kernel/os/os_time.h" +#include "pm/pm.h" + +#include "driver/chip/hal_rtc.h" + +#ifdef CONFIG_PM + +#define ct_assert(e) extern char (*ct_assert(void)) [sizeof(char[1 - 2*!(e)])] + +#if defined(__CONFIG_CHIP_XR871) +#define ktime_t uint64_t +#define ktime_get() (HAL_RTC_GetFreeRunTime() / 1000) +#define ktime_to_msecs(t) (t) +#else +#define ktime_t uint32_t +#define ktime_get() OS_GetTicks() +#define ktime_to_msecs(t) OS_MSecsToJiffies(t) +#endif + +#define arch_suspend_disable_irqs __disable_irq +#define arch_suspend_enable_irqs __enable_irq + +extern unsigned int nvic_int_mask[]; + +extern void debug_jtag_init(void); +extern void debug_jtag_deinit(void); + +int platform_prepare(enum suspend_state_t state); +extern void platform_wake(enum suspend_state_t state); + +#endif + +#endif /* __PM_PORT_H */ diff --git a/platform/mcu/xr871/src/sys/ducc/ducc.c b/platform/mcu/xr871/src/sys/ducc/ducc.c new file mode 100644 index 0000000000..4b247be379 --- /dev/null +++ b/platform/mcu/xr871/src/sys/ducc/ducc.c @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "ducc_os.h" +#include "ducc_debug.h" +#include "ducc.h" + + +/* + * ducc semaphore is used to wait respond from another cpu core. + */ + +#define DUCC_SEM_NUM DUCC_TYPE_NUM + +static ducc_semaphore_t ducc_req_sem[DUCC_SEM_NUM]; +#ifdef __CONFIG_ARCH_APP_CORE +static ducc_semaphore_t *g_ducc_req_sem[DUCC_ID_NUM] = { + [DUCC_ID_APP2NET_NORMAL] = NULL, + [DUCC_ID_APP2NET_DATA] = NULL, + [DUCC_ID_NET2APP_NORMAL] = &ducc_req_sem[0], + [DUCC_ID_NET2APP_DATA] = &ducc_req_sem[1], +}; +#elif (defined(__CONFIG_ARCH_NET_CORE)) +static ducc_semaphore_t *g_ducc_req_sem[DUCC_ID_NUM] = { + [DUCC_ID_APP2NET_NORMAL] = &ducc_req_sem[0], + [DUCC_ID_APP2NET_DATA] = &ducc_req_sem[1], + [DUCC_ID_NET2APP_NORMAL] = NULL, + [DUCC_ID_NET2APP_DATA] = NULL, +}; +#endif /* __CONFIG_ARCH_APP_CORE */ + + +int ducc_req_init(uint32_t id) +{ + return ducc_semaphore_create(g_ducc_req_sem[id], 0); +} + +void ducc_req_deinit(uint32_t id) +{ + ducc_semaphore_delete(g_ducc_req_sem[id]); +} + +int ducc_req_wait(uint32_t id) +{ + return ducc_semaphore_wait(g_ducc_req_sem[id]); +} + +void ducc_req_release(uint32_t id) +{ + ducc_semaphore_release(g_ducc_req_sem[id]); +} diff --git a/platform/mcu/xr871/src/sys/ducc/ducc.h b/platform/mcu/xr871/src/sys/ducc/ducc.h new file mode 100644 index 0000000000..a197f7ddfe --- /dev/null +++ b/platform/mcu/xr871/src/sys/ducc/ducc.h @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _SYS_DUCC_DUCC_H_ +#define _SYS_DUCC_DUCC_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* simulate h/w msgbox using timers and registers or not */ +#define DUCC_SIMULATE_HW_MBOX 0 + +enum DUCC_ID { + DUCC_ID_APP2NET_NORMAL = 0, + DUCC_ID_APP2NET_DATA, + DUCC_ID_NET2APP_NORMAL, + DUCC_ID_NET2APP_DATA, + + DUCC_ID_NUM +}; + +#define DUCC_DIR_NUM 2 /* APP2NET, NET2APP */ +#define DUCC_TYPE_NUM 2 /* NORMAL, DATA */ + +struct ducc_req { +#if DUCC_SIMULATE_HW_MBOX + uint32_t id; +#endif + uint32_t cmd; + uint32_t param; + int result; /* @out */ +}; + +#define DUCC_RELEASE_REQ_MAGIC 0xfa050000 +#define DUCC_RELEASE_REQ_MASK 0xffff0000 +#define DUCC_RELEASE_REQ_VAL(id) \ + ((void *)(DUCC_RELEASE_REQ_MAGIC | (id))) +#define DUCC_IS_RELEASE_REQ(r) \ + ((((uint32_t)(r)) & DUCC_RELEASE_REQ_MASK) == DUCC_RELEASE_REQ_MAGIC) +#define DUCC_RELEASE_REQ_ID(r) \ + (((uint32_t)(r)) & ~DUCC_RELEASE_REQ_MASK) + +int ducc_req_init(uint32_t id); +void ducc_req_deinit(uint32_t id); +int ducc_req_wait(uint32_t id); +void ducc_req_release(uint32_t id); + +#ifdef __cplusplus +} +#endif + +#endif /* _SYS_DUCC_DUCC_H_ */ diff --git a/platform/mcu/xr871/src/sys/ducc/ducc_app.c b/platform/mcu/xr871/src/sys/ducc/ducc_app.c new file mode 100644 index 0000000000..75f4bc02c5 --- /dev/null +++ b/platform/mcu/xr871/src/sys/ducc/ducc_app.c @@ -0,0 +1,433 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#if (defined(__CONFIG_ARCH_DUAL_CORE) && defined(__CONFIG_ARCH_APP_CORE)) + +#include "ducc_os.h" +#include "sys/ducc/ducc_addr.h" +#include "sys/ducc/ducc_app.h" +#include "sys/ducc/ducc_net.h" +#include "sys/mbuf.h" +#include "sys/image.h" +#include "net/wlan/wlan.h" + +#include "ducc_debug.h" +#include "ducc_mbox.h" +#include "ducc.h" +#include "pm/pm.h" + + +/* resouce for normal functions */ +#define DUCC_APP_NORMAL_THREAD_PRIO OS_PRIORITY_ABOVE_NORMAL +#define DUCC_APP_NORMAL_THREAD_STACK_SIZE (8 * 1024) +static ducc_thread_t g_ducc_app_normal_thread; +static ducc_mutex_t g_ducc_app_normal_mutex; + +/* resouce for functions about TX/RX */ +#define DUCC_APP_DATA_THREAD_PRIO OS_PRIORITY_NORMAL +#define DUCC_APP_DATA_THREAD_STACK_SIZE (8 * 1024) +static ducc_thread_t g_ducc_app_data_thread; +static ducc_mutex_t g_ducc_app_data_mutex; + +/* marcos for request */ +#define DUCC_APP_REQ_SEND(id, r) ducc_mbox_send(id, r) +#define DUCC_APP_REQ_RECV(id) ducc_mbox_recv(id, DUCC_WAIT_FOREVER) + +#define DUCC_APP_REQ_WAIT(id) ducc_req_wait(id) +#define DUCC_APP_REQ_RELEASE(id) ducc_req_release(id) + +/* convert pointer from net core to app core */ +#define DUCC_APP_PTR(p) ((void *)DUCC_NETMEM_NET2APP(p)) + +static ducc_cb_func ducc_app_cb = NULL; + +#ifdef CONFIG_PM + +static int8_t g_ducc_hw_mbox_suspending = 0; + +static int ducc_hw_mbox_suspend(struct soc_device *dev, enum suspend_state_t state) +{ + g_ducc_hw_mbox_suspending = 1; + + switch (state) { + case PM_MODE_SLEEP: + break; + case PM_MODE_STANDBY: + case PM_MODE_HIBERNATION: + case PM_MODE_POWEROFF: + ducc_mbox_deinit(DUCC_ID_NET2APP_DATA, 0, 1); + ducc_mbox_deinit(DUCC_ID_APP2NET_DATA, 1, 1); + + ducc_mbox_deinit(DUCC_ID_NET2APP_NORMAL, 0, 1); + ducc_mbox_deinit(DUCC_ID_APP2NET_NORMAL, 1, 1); + DUCC_DBG("%s okay\n", __func__); + break; + default: + break; + } + + return 0; +} + +static int ducc_hw_mbox_resume(struct soc_device *dev, enum suspend_state_t state) +{ + switch (state) { + case PM_MODE_SLEEP: + break; + case PM_MODE_STANDBY: + case PM_MODE_HIBERNATION: + ducc_mbox_init(DUCC_ID_APP2NET_NORMAL, 1, 1); + ducc_mbox_init(DUCC_ID_NET2APP_NORMAL, 0, 1); + + ducc_mbox_init(DUCC_ID_APP2NET_DATA, 1, 1); + ducc_mbox_init(DUCC_ID_NET2APP_DATA, 0, 1); + + __asm(" dsb \n"); + __asm(" isb \n"); + DUCC_DBG("%s okay\n", __func__); + break; + default: + break; + } + + g_ducc_hw_mbox_suspending = 0; + + return 0; +} + +void hw_mbox_print_regs(void) +{ + //hex_dump_bytes(&hw_mbox_reg_store, sizeof(hw_mbox_reg_store)); +} + +static struct soc_device_driver ducc_hw_mbox_drv = { + .name = "hw_mbox", + .suspend = ducc_hw_mbox_suspend, + .resume = ducc_hw_mbox_resume, +}; + +static struct soc_device ducc_hw_mbox_dev = { + .name = "hw_mbox", + .driver = &ducc_hw_mbox_drv, +}; + +#define DUCC_HW_MBOX_DEV (&ducc_hw_mbox_dev) +#else +#define DUCC_HW_MBOX_DEV NULL +#endif + +int ducc_app_ioctl(enum ducc_app_cmd cmd, void *param) +{ + struct ducc_req req; + ducc_mutex_t *mutex; + uint32_t send_id, wait_id; + + DUCC_APP_DBG("send req %d\n", cmd); +#ifdef CONFIG_PM + if (g_ducc_hw_mbox_suspending) { + DUCC_ERR("send req %d when suspending\n", cmd); + return -1; + } +#endif + + if (DUCC_APP_IS_DATA_CMD(cmd)) { + mutex = &g_ducc_app_data_mutex; + send_id = DUCC_ID_APP2NET_DATA; + wait_id = DUCC_ID_NET2APP_DATA; + } else { + mutex = &g_ducc_app_normal_mutex; + send_id = DUCC_ID_APP2NET_NORMAL; + wait_id = DUCC_ID_NET2APP_NORMAL; + } + + ducc_mutex_lock(mutex); + +#if DUCC_SIMULATE_HW_MBOX + req.id = send_id; +#endif + req.cmd = (uint32_t)cmd; + req.param = (uint32_t)param; + req.result = -1; + + do { + if (DUCC_APP_REQ_SEND(send_id, &req) < 0) { + DUCC_WARN("send req %d failed\n", cmd); + break; + } + + DUCC_APP_DBG("wait req %d\n", cmd); + if (DUCC_APP_REQ_WAIT(wait_id) < 0) { + DUCC_WARN("wait req %d failed\n", cmd); + break; + } + } while (0); + + DUCC_APP_DBG("wait req %d done\n", cmd); + + ducc_mutex_unlock(mutex); + + return req.result; +} + +static volatile uint32_t ducc_app_normal_task_term; + +static void ducc_app_normal_task(void *arg) +{ + uint32_t recv_id = DUCC_ID_NET2APP_NORMAL; + uint32_t send_id = DUCC_ID_APP2NET_NORMAL; + void *net_req; + struct ducc_req *req; + struct ducc_param_efuse *efuse; + + while (1) { + net_req = DUCC_APP_REQ_RECV(recv_id); + + if (ducc_app_normal_task_term) + break; + + if (net_req == NULL) { + DUCC_WARN("invalid net req\n"); + continue; + } + + req = DUCC_APP_PTR(net_req); +#if DUCC_SIMULATE_HW_MBOX + if (req->id != recv_id) { + DUCC_WARN("invalid net req, id 0x%x\n", req->id); + continue; + } +#endif + DUCC_APP_DBG("exec req %d\n", req->cmd); + + switch (req->cmd) { +#if (__CONFIG_MBUF_IMPL_MODE == 1) + case DUCC_NET_CMD_MBUF_GET: + { + struct ducc_param_mbuf_get *p = DUCC_APP_PTR(req->param); + struct mbuf *m = mb_get(p->len, p->tx); + if (m) { + MBUF_APP2NET(m); + p->mbuf = m; + req->result = 0; + } else { + req->result = -1; + } + break; + } + case DUCC_NET_CMD_MBUF_FREE: + { + struct mbuf *m = (struct mbuf *)req->param; + MBUF_NET2APP(m); + mb_free(m); + req->result = 0; + break; + } +#endif /* (__CONFIG_MBUF_IMPL_MODE == 1) */ + case DUCC_NET_CMD_POWER_NOTIFY: + if (ducc_app_cb) + ducc_app_cb(req->cmd, req->param); + break; + case DUCC_NET_CMD_BIN_READ: + if (ducc_app_cb) { + struct ducc_param_wlan_bin *p = DUCC_APP_PTR(req->param); + p->buf = (void *)DUCC_NETMEM_NET2APP(p->buf); + req->result = ducc_app_cb(DUCC_NET_CMD_BIN_READ, (uint32_t)p); + p->buf = (void *)DUCC_NETMEM_APP2NET(p->buf); + } else { + req->result = 0; + } + break; + case DUCC_NET_CMD_EFUSE_READ: + efuse = DUCC_APP_PTR(req->param); + efuse->data = (void *)DUCC_NETMEM_NET2APP(efuse->data); + req->result = ducc_app_cb(DUCC_NET_CMD_EFUSE_READ, (uint32_t)efuse); + efuse->data = (void *)DUCC_NETMEM_APP2NET(efuse->data); + break; + case DUCC_NET_CMD_SYS_EVENT: + case DUCC_NET_CMD_WLAN_EVENT: + if (ducc_app_cb) + ducc_app_cb(req->cmd, req->param); + req->result = 0; + break; + default: + DUCC_WARN("invalid command %d\n", req->cmd); + break; + }; + + DUCC_APP_DBG("exec req %d done\n", req->cmd); + + DUCC_APP_REQ_SEND(send_id, DUCC_RELEASE_REQ_VAL(send_id)); + } + + ducc_app_normal_task_term = 0; + ducc_thread_exit(&g_ducc_app_normal_thread); +} + +static volatile uint32_t ducc_app_data_task_term; + +static void ducc_app_data_task(void *arg) +{ + uint32_t recv_id = DUCC_ID_NET2APP_DATA; + uint32_t send_id = DUCC_ID_APP2NET_DATA; + void *net_req; + struct ducc_req *req; + + while (1) { + net_req = DUCC_APP_REQ_RECV(recv_id); + + if (ducc_app_data_task_term) + break; + + if (net_req == NULL) { + DUCC_WARN("invalid net req\n"); + continue; + } + + req = DUCC_APP_PTR(net_req); +#if DUCC_SIMULATE_HW_MBOX + if (req->id != recv_id) { + DUCC_WARN("invalid net req, id 0x%x\n", req->id); + continue; + } +#endif + DUCC_APP_DBG("exec req %d\n", req->cmd); + + switch (req->cmd) { + case DUCC_NET_CMD_WLAN_INPUT: + { + struct ducc_param_wlan_input *p = DUCC_APP_PTR(req->param); +#if (__CONFIG_MBUF_IMPL_MODE == 0) + req->result = (ethernetif_raw_input(p->nif, + DUCC_APP_PTR(p->data), + p->len) == ERR_OK ? 0 : -1); +#elif (__CONFIG_MBUF_IMPL_MODE == 1) + struct mbuf *m; + struct pbuf *pb; + m = p->mbuf; + MBUF_NET2APP(m); + pb = mb_mbuf2pbuf(m); /* data including Ethernet header */ + mb_free(m); /* useless now, should be freed */ + req->result = (ethernetif_input(p->nif, pb) == ERR_OK ? 0 : -1); +#endif /* __CONFIG_MBUF_IMPL_MODE */ + break; + } + case DUCC_NET_CMD_WLAN_MONITOR_INPUT: + { + struct ducc_param_wlan_mon_input *p = DUCC_APP_PTR(req->param); + wlan_monitor_input(p->nif, DUCC_APP_PTR(p->data), p->len, + p->info ? DUCC_APP_PTR(p->info) : NULL); + req->result = 0; + break; + } + default: + DUCC_WARN("invalid command %d\n", req->cmd); + break; + }; + + DUCC_APP_DBG("exec req %d done\n", req->cmd); + + DUCC_APP_REQ_SEND(send_id, DUCC_RELEASE_REQ_VAL(send_id)); + } + + ducc_app_data_task_term = 0; + ducc_thread_exit(&g_ducc_app_data_thread); +} + +int ducc_app_start(struct ducc_app_param *param) +{ + ducc_app_cb = param->cb; + + ducc_mutex_create(&g_ducc_app_normal_mutex); + ducc_req_init(DUCC_ID_NET2APP_NORMAL); + ducc_mbox_init(DUCC_ID_APP2NET_NORMAL, 1, 0); + ducc_mbox_init(DUCC_ID_NET2APP_NORMAL, 0, 0); + + ducc_mutex_create(&g_ducc_app_data_mutex); + ducc_req_init(DUCC_ID_NET2APP_DATA); + ducc_mbox_init(DUCC_ID_APP2NET_DATA, 1, 0); + ducc_mbox_init(DUCC_ID_NET2APP_DATA, 0, 0); + + ducc_app_normal_task_term = 0; + if (ducc_thread_create(&g_ducc_app_normal_thread, + ducc_app_normal_task, + NULL, + DUCC_APP_NORMAL_THREAD_PRIO, + DUCC_APP_NORMAL_THREAD_STACK_SIZE) != 0) { + DUCC_ERR("create thread failed\n"); + return -1; + } + + ducc_app_data_task_term = 0; + if (ducc_thread_create(&g_ducc_app_data_thread, + ducc_app_data_task, + NULL, + DUCC_APP_DATA_THREAD_PRIO, + DUCC_APP_DATA_THREAD_STACK_SIZE) != 0) { + DUCC_ERR("create thread failed\n"); + return -1; + } +#ifdef CONFIG_PM + pm_register_ops(DUCC_HW_MBOX_DEV); +#endif + + return 0; +} + +int ducc_app_stop(void) +{ +#ifdef CONFIG_PM + pm_unregister_ops(DUCC_HW_MBOX_DEV); +#endif + + ducc_app_data_task_term = 1; + ducc_mbox_msg_callback(DUCC_ID_NET2APP_DATA, NULL); + while (ducc_app_data_task_term) + OS_MSleep(2); + + ducc_app_normal_task_term = 1; + ducc_mbox_msg_callback(DUCC_ID_NET2APP_NORMAL, NULL); + while (ducc_app_normal_task_term) + OS_MSleep(2); + + ducc_mbox_deinit(DUCC_ID_NET2APP_DATA, 0, 0); + ducc_mbox_deinit(DUCC_ID_APP2NET_DATA, 1, 0); + ducc_req_deinit(DUCC_ID_NET2APP_DATA); + ducc_mutex_delete(&g_ducc_app_data_mutex); + + ducc_mbox_deinit(DUCC_ID_NET2APP_NORMAL, 0, 0); + ducc_mbox_deinit(DUCC_ID_APP2NET_NORMAL, 1, 0); + ducc_req_deinit(DUCC_ID_NET2APP_NORMAL); + ducc_mutex_delete(&g_ducc_app_normal_mutex); + + ducc_app_cb = NULL; + + return 0; +} + +#endif /* (defined(__CONFIG_ARCH_DUAL_CORE) && defined(__CONFIG_ARCH_APP_CORE)) */ diff --git a/platform/mcu/xr871/src/sys/ducc/ducc_debug.h b/platform/mcu/xr871/src/sys/ducc/ducc_debug.h new file mode 100644 index 0000000000..50e7a7ee9a --- /dev/null +++ b/platform/mcu/xr871/src/sys/ducc/ducc_debug.h @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _SYS_DUCC_DUCC_DEBUG_H_ +#define _SYS_DUCC_DUCC_DEBUG_H_ + +#include +#include "sys/xr_util.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define DUCC_DEBUG_ON 0 +#define DUCC_WARN_ON 1 +#define DUCC_ERR_ON 1 +#define DUCC_ABORT_ON 0 + +#define DUCC_DBG_WLAN 1 +#define DUCC_DBG_NET 1 +#define DUCC_DBG_APP 1 +#define DUCC_DBG_MBOX 1 +#define DUCC_DBG_HW_MBOX 1 + +#define DUCC_SYSLOG printf +#define DUCC_ABORT() sys_abort() + +#define DUCC_LOG(flags, fmt, arg...) \ + do { \ + if (flags) \ + DUCC_SYSLOG(fmt, ##arg); \ + } while (0) + +#define DUCC_DBG(fmt, arg...) \ + DUCC_LOG(DUCC_DEBUG_ON, "[ducc] "fmt, ##arg) + +#define DUCC_WLAN_DBG(fmt, arg...) \ + DUCC_LOG(DUCC_DEBUG_ON && DUCC_DBG_WLAN, "[ducc wlan] "fmt, ##arg) + +#define DUCC_NET_DBG(fmt, arg...) \ + DUCC_LOG(DUCC_DEBUG_ON && DUCC_DBG_NET, "[ducc net] "fmt, ##arg) + +#define DUCC_APP_DBG(fmt, arg...) \ + DUCC_LOG(DUCC_DEBUG_ON && DUCC_DBG_APP, "[ducc app] "fmt, ##arg) + +#define DUCC_MBOX_DBG(fmt, arg...) \ + DUCC_LOG(DUCC_DEBUG_ON && DUCC_DBG_MBOX, "[ducc mbox] "fmt, ##arg) + +#define DUCC_HW_MBOX_DBG(fmt, arg...) \ + DUCC_LOG(DUCC_DEBUG_ON && DUCC_DBG_HW_MBOX, "[ducc hw-mbox] "fmt, ##arg) + +#define DUCC_WARN(fmt, arg...) \ + DUCC_LOG(DUCC_WARN_ON, "[ducc WARN] %s():%d, "fmt, __func__, __LINE__, ##arg) + +#define DUCC_ERR(fmt, arg...) \ + do { \ + DUCC_LOG(DUCC_ERR_ON, "[ducc ERR] %s():%d, "fmt, \ + __func__, __LINE__, ##arg); \ + if (DUCC_ABORT_ON) \ + DUCC_ABORT(); \ + } while (0) + +#ifdef __cplusplus +} +#endif + +#endif /* _SYS_DUCC_DUCC_DEBUG_H_ */ diff --git a/platform/mcu/xr871/src/sys/ducc/ducc_hw_mbox.c b/platform/mcu/xr871/src/sys/ducc/ducc_hw_mbox.c new file mode 100644 index 0000000000..a1efc91723 --- /dev/null +++ b/platform/mcu/xr871/src/sys/ducc/ducc_hw_mbox.c @@ -0,0 +1,270 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "driver/chip/hal_mbox.h" +#include "ducc_mbox.h" +#include "ducc_debug.h" + +#ifdef __CONFIG_ARCH_DUAL_CORE + +#define DUCC_OPT_HW_MBOX_PM_PATCH HAL_MBOX_PM_PATCH /* use MBOX_A only */ + +#if DUCC_OPT_HW_MBOX_PM_PATCH + +#define DUCC_HW_MBOX MBOX_A +#define DUCC_HW_MBOX_TX DUCC_HW_MBOX +#define DUCC_HW_MBOX_RX DUCC_HW_MBOX + +#ifdef __CONFIG_ARCH_APP_CORE + #define DUCC_HW_MBOX_SELF MBOX_USER0 + #define DUCC_HW_MBOX_OTHER MBOX_USER1 +#elif (defined(__CONFIG_ARCH_NET_CORE)) + #define DUCC_HW_MBOX_SELF MBOX_USER1 + #define DUCC_HW_MBOX_OTHER MBOX_USER0 +#endif /* __CONFIG_ARCH_APP_CORE */ + +uint8_t g_ducc_hw_mbox_init_cnt = 0; + +#ifdef __CONFIG_ARCH_APP_CORE + +int ducc_hw_mbox_init(uint32_t id, int is_tx) +{ + MBOX_Queue queue = (MBOX_Queue)id; + + if (g_ducc_hw_mbox_init_cnt++ == 0) { + HAL_MBOX_Init(DUCC_HW_MBOX); + HAL_MBOX_EnableIRQ(DUCC_HW_MBOX); + } + + if (is_tx) { + /* init sender and receiver of queue */ + HAL_MBOX_QueueInit(DUCC_HW_MBOX, DUCC_HW_MBOX_SELF, queue, MBOX_DIR_TX); + + /* enable receiver's irq */ + HAL_MBOX_QueueEnableIRQ(DUCC_HW_MBOX, DUCC_HW_MBOX_OTHER, queue, MBOX_DIR_RX); + } else { + /* init sender and receiver of queue */ + HAL_MBOX_QueueInit(DUCC_HW_MBOX, DUCC_HW_MBOX_SELF, queue, MBOX_DIR_RX); + + /* enable receiver's irq */ + HAL_MBOX_QueueEnableIRQ(DUCC_HW_MBOX, DUCC_HW_MBOX_SELF, queue, MBOX_DIR_RX); + } + + return 0; +} + +int ducc_hw_mbox_deinit(uint32_t id, int is_tx) +{ + MBOX_Queue queue = (MBOX_Queue)id; + + if (is_tx) { + HAL_MBOX_QueueDisableIRQ(DUCC_HW_MBOX, DUCC_HW_MBOX_OTHER, queue, MBOX_DIR_RX); + HAL_MBOX_QueueDeInit(DUCC_HW_MBOX, DUCC_HW_MBOX_SELF, queue, MBOX_DIR_TX); + + } else { + HAL_MBOX_QueueDisableIRQ(DUCC_HW_MBOX, DUCC_HW_MBOX_SELF, queue, MBOX_DIR_RX); + HAL_MBOX_QueueDeInit(DUCC_HW_MBOX, DUCC_HW_MBOX_SELF, queue, MBOX_DIR_RX); + } + + if ((g_ducc_hw_mbox_init_cnt > 0) && (--g_ducc_hw_mbox_init_cnt == 0)) { + HAL_MBOX_DisableIRQ(DUCC_HW_MBOX); + HAL_MBOX_DeInit(DUCC_HW_MBOX); + } + + return 0; +} + +#elif (defined(__CONFIG_ARCH_NET_CORE)) + +int ducc_hw_mbox_init(uint32_t id, int is_tx) +{ + if (g_ducc_hw_mbox_init_cnt++ == 0) { + HAL_MBOX_EnableIRQ(DUCC_HW_MBOX); + } + return 0; +} + +int ducc_hw_mbox_deinit(uint32_t id, int is_tx) +{ + if ((g_ducc_hw_mbox_init_cnt > 0) && (--g_ducc_hw_mbox_init_cnt == 0)) { + HAL_MBOX_DisableIRQ(DUCC_HW_MBOX); + } + return 0; +} + +#endif /* __CONFIG_ARCH_APP_CORE */ + +#else /* DUCC_OPT_HW_MBOX_PM_PATCH */ + +#ifdef __CONFIG_ARCH_APP_CORE + #define DUCC_HW_MBOX_TX MBOX_A + #define DUCC_HW_MBOX_TX_USER MBOX_USER0 + #define DUCC_HW_MBOX_RX MBOX_N + #define DUCC_HW_MBOX_RX_USER MBOX_USER1 +#elif (defined(__CONFIG_ARCH_NET_CORE)) + #define DUCC_HW_MBOX_TX MBOX_N + #define DUCC_HW_MBOX_TX_USER MBOX_USER0 + #define DUCC_HW_MBOX_RX MBOX_A + #define DUCC_HW_MBOX_RX_USER MBOX_USER1 +#endif /* __CONFIG_ARCH_APP_CORE */ + +uint8_t g_ducc_hw_mbox_enable = 0; + +int ducc_hw_mbox_init(uint32_t id, int is_tx) +{ + MBOX_T *mbox; + MBOX_User user; + MBOX_Queue queue; + MBOX_Direction dir; + + if (g_ducc_hw_mbox_enable == 0) { + HAL_MBOX_Init(DUCC_HW_MBOX_TX); + HAL_MBOX_Init(DUCC_HW_MBOX_RX); + g_ducc_hw_mbox_enable = 1; + } + + queue = (MBOX_Queue)id; + if (is_tx) { + mbox = DUCC_HW_MBOX_TX; + user = DUCC_HW_MBOX_TX_USER; + dir = MBOX_DIR_TX; + } else { + mbox = DUCC_HW_MBOX_RX; + user = DUCC_HW_MBOX_RX_USER; + dir = MBOX_DIR_RX; + } + HAL_MBOX_QueueInit(mbox, user, queue, dir); + if (!is_tx) { + HAL_MBOX_QueueEnableIRQ(mbox, user, queue, dir); + } + + return 0; +} + +int ducc_hw_mbox_deinit(uint32_t id, int is_tx) +{ + MBOX_T *mbox; + MBOX_User user; + MBOX_Queue queue; + MBOX_Direction dir; + + queue = (MBOX_Queue)id; + if (is_tx) { + mbox = DUCC_HW_MBOX_TX; + user = DUCC_HW_MBOX_TX_USER; + dir = MBOX_DIR_TX; + } else { + mbox = DUCC_HW_MBOX_RX; + user = DUCC_HW_MBOX_RX_USER; + dir = MBOX_DIR_RX; + } + if (!is_tx) { + HAL_MBOX_QueueDisableIRQ(mbox, user, queue, dir); + } + HAL_MBOX_QueueDeInit(mbox, user, queue, dir); + + if (g_ducc_hw_mbox_enable == 1) { + HAL_MBOX_DeInit(DUCC_HW_MBOX_TX); + HAL_MBOX_DeInit(DUCC_HW_MBOX_RX); + g_ducc_hw_mbox_enable = 0; + } + + return 0; +} + +#endif /* DUCC_OPT_HW_MBOX_PM_PATCH */ + +int ducc_hw_mbox_send(uint32_t id, void *msg) +{ + MBOX_T *mbox = DUCC_HW_MBOX_TX; + MBOX_Queue queue = (MBOX_Queue)id; +#if DUCC_WARN_ON + int i = 0; +#endif + + while (HAL_MBOX_QueueIsFull(mbox, queue)) { +#if DUCC_WARN_ON + if (++i > 1000) { + DUCC_WARN("h/w mbox %d (%p) is full\n", queue, mbox); + i = 0; + } +#endif + } + + HAL_MBOX_QueuePutMsg(mbox, queue, (uint32_t)msg); + return 0; +} + +#if 0 +int ducc_hw_mbox_recv(uint32_t id, void **msg) +{ + MBOX_T *mbox = DUCC_HW_MBOX_RX; + MBOX_Queue queue = (MBOX_Queue)id; + + if (HAL_MBOX_QueueGetMsgNum(mbox, queue) > 0) { + *msg = (void *)HAL_MBOX_QueueGetMsg(queue); + return 0; + } + + DUCC_WARN("h/w mbox %d (%p) is empty\n", queue, mbox); + return -1; +} +#endif + +void MBOX_IRQCallback(MBOX_T *mbox, MBOX_Queue queue, MBOX_Direction dir) +{ + uint32_t id = queue; + void *msg; + + DUCC_HW_MBOX_DBG("%s(), mbox %p, queue %d, dir %d\n", __func__, mbox, queue, dir); + + if (dir == MBOX_DIR_RX) { + if (mbox != DUCC_HW_MBOX_RX) { + DUCC_WARN("mbox %p != %p (rx mbox)\n", mbox, DUCC_HW_MBOX_RX); + return; + } + + if (HAL_MBOX_QueueGetMsgNum(mbox, queue) > 0) { + msg = (void *)HAL_MBOX_QueueGetMsg(mbox, queue); + } else { + DUCC_WARN("h/w mbox %d (%p) is empty\n", queue, mbox); + return; + } + +#if 0 // only for test + DUCC_HW_MBOX_DBG("%s(), queue %d, dir %d, msg %u\n", __func__, queue, dir, (uint32_t)msg); + return; +#endif + ducc_mbox_msg_callback(id, msg); + } else { + DUCC_WARN("mbox %p tx irq!\n", mbox); + } +} + +#endif /* __CONFIG_ARCH_DUAL_CORE */ diff --git a/platform/mcu/xr871/src/sys/ducc/ducc_hw_mbox.h b/platform/mcu/xr871/src/sys/ducc/ducc_hw_mbox.h new file mode 100644 index 0000000000..51d5f2069f --- /dev/null +++ b/platform/mcu/xr871/src/sys/ducc/ducc_hw_mbox.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _SYS_DUCC_DUCC_HW_MBOX_H_ +#define _SYS_DUCC_DUCC_HW_MBOX_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +int ducc_hw_mbox_init(uint32_t id, int is_tx); +int ducc_hw_mbox_deinit(uint32_t id, int is_tx); +int ducc_hw_mbox_send(uint32_t id, void *msg); + +#ifdef __cplusplus +} +#endif + +#endif /* _SYS_DUCC_DUCC_HW_MBOX_H_ */ diff --git a/platform/mcu/xr871/src/sys/ducc/ducc_mbox.c b/platform/mcu/xr871/src/sys/ducc/ducc_mbox.c new file mode 100644 index 0000000000..bdef4d9a5f --- /dev/null +++ b/platform/mcu/xr871/src/sys/ducc/ducc_mbox.c @@ -0,0 +1,142 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "ducc.h" +#include "ducc_os.h" +#include "ducc_debug.h" +#include "ducc_hw_mbox.h" + + +/* + * ducc mbox is used to buffer/receive message from another cpu core. + */ + +#define DUCC_MBOX_SIZE 8 +#define DUCC_MBOX_NUM DUCC_TYPE_NUM + +static ducc_msgqueue_t ducc_mbox[DUCC_MBOX_NUM]; + +#ifdef __CONFIG_ARCH_APP_CORE +static ducc_msgqueue_t *g_ducc_mbox[DUCC_ID_NUM] = { + [DUCC_ID_APP2NET_NORMAL] = NULL, + [DUCC_ID_APP2NET_DATA] = NULL, + [DUCC_ID_NET2APP_NORMAL] = &ducc_mbox[0], + [DUCC_ID_NET2APP_DATA] = &ducc_mbox[1], +}; +#elif (defined(__CONFIG_ARCH_NET_CORE)) +static ducc_msgqueue_t *g_ducc_mbox[DUCC_ID_NUM] = { + [DUCC_ID_APP2NET_NORMAL] = &ducc_mbox[0], + [DUCC_ID_APP2NET_DATA] = &ducc_mbox[1], + [DUCC_ID_NET2APP_NORMAL] = NULL, + [DUCC_ID_NET2APP_DATA] = NULL, +}; +#endif /* __CONFIG_ARCH_APP_CORE */ + +int ducc_mbox_init(uint32_t id, int is_tx, uint32_t suspending) +{ + if (id >= DUCC_ID_NUM) { + DUCC_ERR("invalid mbox id 0x%x\n", id); + return -1; + } + + if (ducc_hw_mbox_init(id, is_tx) != 0) { + return -1; + } + + if (is_tx || suspending) + return 0; + + ducc_msgqueue_t *mbox = g_ducc_mbox[id]; + + if (ducc_msgqueue_is_valid(mbox)) { + DUCC_MBOX_DBG("mbox 0x%x already inited\n", id); + return 0; + } + + if (ducc_msgqueue_create(mbox, DUCC_MBOX_SIZE) != 0) { + DUCC_ERR("ducc_msgqueue_create() failed\n"); + return -1; + } + + return 0; +} + +int ducc_mbox_deinit(uint32_t id, int is_tx, uint32_t suspending) +{ + if (id >= DUCC_ID_NUM) { + DUCC_ERR("invalid mbox id 0x%x\n", id); + return -1; + } + + if (ducc_hw_mbox_deinit(id, is_tx) != 0) { + return -1; + } + + if (is_tx || suspending) + return 0; + + ducc_msgqueue_t *mbox = g_ducc_mbox[id]; + + if (!ducc_msgqueue_is_valid(mbox)) { + DUCC_MBOX_DBG("mbox 0x%x already deinited\n", id); + return 0; + } + + ducc_msgqueue_delete(mbox); + + return 0; +} + +int ducc_mbox_send(uint32_t id, void *msg) +{ + if (ducc_hw_mbox_send(id, msg) != 0) { + DUCC_ERR("ducc_hw_mbox_send() failed, id 0x%x\n", id); + return -1; + } + return 0; +} + +void *ducc_mbox_recv(uint32_t id, uint32_t timeout) +{ + void *msg; + int ret = ducc_msgqueue_recv(g_ducc_mbox[id], &msg, timeout); + return (ret == 0 ? msg : NULL); +} + +void ducc_mbox_msg_callback(uint32_t id, void *msg) +{ + if (msg == DUCC_RELEASE_REQ_VAL(id)) { + ducc_req_release(id); + return; + } + + if (ducc_msgqueue_send(g_ducc_mbox[id], msg, 0) != 0) { + DUCC_ERR("ducc_msgqueue_send() failed, id %u\n", id); + } +} diff --git a/platform/mcu/xr871/src/sys/ducc/ducc_mbox.h b/platform/mcu/xr871/src/sys/ducc/ducc_mbox.h new file mode 100644 index 0000000000..388228810e --- /dev/null +++ b/platform/mcu/xr871/src/sys/ducc/ducc_mbox.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _SYS_DUCC_DUCC_MBOX_H_ +#define _SYS_DUCC_DUCC_MBOX_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +int ducc_mbox_init(uint32_t id, int is_tx, uint32_t suspending); +int ducc_mbox_deinit(uint32_t id, int is_tx, uint32_t suspending); +int ducc_mbox_send(uint32_t id, void *msg); +void *ducc_mbox_recv(uint32_t id, uint32_t timeout); +void ducc_mbox_msg_callback(uint32_t id, void *msg); + +#ifdef __cplusplus +} +#endif + +#endif /* _SYS_DUCC_DUCC_MBOX_H_ */ diff --git a/platform/mcu/xr871/src/sys/ducc/ducc_os.h b/platform/mcu/xr871/src/sys/ducc/ducc_os.h new file mode 100644 index 0000000000..4b82e29aa0 --- /dev/null +++ b/platform/mcu/xr871/src/sys/ducc/ducc_os.h @@ -0,0 +1,147 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _SYS_DUCC_DUCC_OS_H_ +#define _SYS_DUCC_DUCC_OS_H_ + +#include "kernel/os/os.h" +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define DUCC_WAIT_FOREVER OS_WAIT_FOREVER +#define DUCC_OS_OK OS_OK + +/* Semaphore */ +typedef OS_Semaphore_t ducc_semaphore_t; + +static __inline int ducc_semaphore_create(ducc_semaphore_t *sem, uint32_t initCount) +{ + return OS_SemaphoreCreate(sem, initCount, OS_SEMAPHORE_MAX_COUNT) == OS_OK ? 0 : -1; +} + +static __inline void ducc_semaphore_delete(ducc_semaphore_t *sem) +{ + OS_SemaphoreDelete(sem); +} + +static __inline int ducc_semaphore_wait(ducc_semaphore_t *sem) +{ + return OS_SemaphoreWait(sem, OS_WAIT_FOREVER) == OS_OK ? 0 : -1; +} + +static __inline void ducc_semaphore_release(ducc_semaphore_t *sem) +{ + OS_SemaphoreRelease(sem); +} + +/* Mutex */ +typedef OS_Mutex_t ducc_mutex_t; + +static __inline int ducc_mutex_create(ducc_mutex_t *mtx) +{ + return OS_MutexCreate(mtx) == OS_OK ? 0 : -1; +} + +static __inline void ducc_mutex_delete(ducc_mutex_t *mtx) +{ + OS_MutexDelete(mtx); +} + +static __inline void ducc_mutex_lock(ducc_mutex_t *mtx) +{ + OS_MutexLock(mtx, OS_WAIT_FOREVER); +} + +static __inline void ducc_mutex_unlock(ducc_mutex_t *mtx) +{ + OS_MutexUnlock(mtx); +} + +/* Message Queue */ +typedef OS_Queue_t ducc_msgqueue_t; + +static __inline int ducc_msgqueue_create(ducc_msgqueue_t *queue, uint32_t queueLen) +{ + return OS_MsgQueueCreate(queue, queueLen) == OS_OK ? 0 : -1; +} + +static __inline void ducc_msgqueue_delete(ducc_msgqueue_t *queue) +{ + OS_MsgQueueDelete(queue); +} + +static __inline int ducc_msgqueue_send(ducc_msgqueue_t *queue, void *msg, uint32_t waitMS) +{ + return OS_MsgQueueSend(queue, msg, waitMS) == OS_OK ? 0 : -1; +} + +static __inline int ducc_msgqueue_recv(ducc_msgqueue_t *queue, void **msg, uint32_t waitMS) +{ + return OS_MsgQueueReceive(queue, msg, waitMS) == OS_OK ? 0 : -1; +} + +static __inline int ducc_msgqueue_is_valid(ducc_msgqueue_t *queue) +{ + return OS_QueueIsValid(queue); +} + +/* Thread */ +typedef OS_Thread_t ducc_thread_t; +typedef OS_ThreadEntry_t ducc_thread_entry_t; + +static __inline int ducc_thread_create(ducc_thread_t *thread, + ducc_thread_entry_t entry, void *arg, + int priority, uint32_t stack_size) +{ + return OS_ThreadCreate(thread, "", entry, arg, priority, stack_size)== OS_OK ? 0 : -1; +} + +static __inline void ducc_thread_exit(ducc_thread_t *thread) +{ + OS_ThreadDelete(thread); +} + +/* memory */ +//#define ducc_malloc(l) malloc(l) +//#define ducc_free(p) free(p) +#define ducc_memcpy(d, s, l) memcpy(d, s, l) +#define ducc_memset(d, c, l) memset(d, c, l) +#define ducc_memcmp(a, b, l) memcmp(a, b, l) +#define ducc_memmove(d, s, n) memmove(d, s, n) +#define ducc_strcmp(a, b) strcmp(a, b) + + +#ifdef __cplusplus +} +#endif + +#endif /* _SYS_DUCC_DUCC_OS_H_ */ diff --git a/platform/mcu/xr871/src/sys/mbuf/mbuf_1.c b/platform/mcu/xr871/src/sys/mbuf/mbuf_1.c new file mode 100644 index 0000000000..ccf2e2a24d --- /dev/null +++ b/platform/mcu/xr871/src/sys/mbuf/mbuf_1.c @@ -0,0 +1,202 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#if (__CONFIG_MBUF_IMPL_MODE == 1) + +#include "sys/mbuf_1.h" +#include "mbuf_util.h" + +#if (defined(__CONFIG_ARCH_APP_CORE) || !defined(__CONFIG_ARCH_DUAL_CORE)) + +#include "lwip/pbuf.h" +#include "lwip/mem.h" +#include "lwip/memp.h" + +/* Init mbuf data info from pbuf, no sanity checks */ +static void mb_data_init(struct mbuf *m, struct pbuf *p) +{ + m->m_pbuf = p; + m->m_data = p->payload; + m->m_len = p->tot_len; + m->m_pkthdr.len = p->tot_len; + m->m_headspace = pbuf_head_space(p); + if (p->mb_flags & PBUF_FLAG_MBUF_SPACE) + m->m_tailspace = MBUF_TAIL_SPACE; + p->mb_flags |= PBUF_FLAG_MBUF_REF; +} + +/* + * Alloc a new mbuf without pbuf, zero the header. + */ +static struct mbuf *mb_alloc() +{ + struct mbuf *m = memp_malloc(MEMP_MBUF); + if (m) { + MB_MEMSET(m, 0, sizeof(struct mbuf)); + m->m_flags = M_PKTHDR; + } else { + MBUF_WRN("Out of memory in pbuf pool for mbuf\n"); + } + return m; +} + +/* + * @param tx + * - 1 means mbuf is used to do Tx, always alloc it from PBUF_RAM. + * - 0 means mbuf is used to do RX, try to alloc it from PBUF_POOL first + * @return a mbuf including @len data + */ +struct mbuf *mb_get(int len, int tx) +{ + if (len < 0) + return NULL; + + if (len > PBUF_POOL_BUFSIZE) { + MBUF_WRN("try to get large data, len %d\n", len); + } + + struct mbuf *m = mb_alloc(); + if (m == NULL) { + return NULL; + } + + struct pbuf *p; +#if LWIP_PBUF_POOL_SMALL + int pbuf_pool_small = 0; +#endif + int tot_len = len; + pbuf_type type = (tx || len > PBUF_POOL_BUFSIZE) ? PBUF_RAM : PBUF_POOL; + if (type == PBUF_RAM) { + /* space is not reserved after pbuf_alloc(), add more space */ + tot_len += MBUF_HEAD_SPACE + MBUF_TAIL_SPACE; + } +#if LWIP_PBUF_POOL_SMALL + else if (len <= PBUF_POOL_SMALL_BUFSIZE) { + pbuf_pool_small = 1; + } + +retry: + p = pbuf_alloc_ext(PBUF_MBUF_RAW, tot_len, type, pbuf_pool_small); + if (p == NULL) { + MBUF_DBG("pbuf_alloc_ext() failed, tot_len %d, type %d, small %d\n", + tot_len, type, pbuf_pool_small); + if (pbuf_pool_small) { + /* try to get pbuf from bigger pbuf pools */ + pbuf_pool_small = 0; + goto retry; + } + mb_free(m); + return NULL; + } +#else /* LWIP_PBUF_POOL_SMALL */ + p = pbuf_alloc(PBUF_MBUF_RAW, tot_len, type); + if (p == NULL) { + MBUF_DBG("pbuf_alloc() failed, tot_len %d, type %d\n", tot_len, type); + mb_free(m); + return NULL; + } +#endif /* LWIP_PBUF_POOL_SMALL */ + if (type == PBUF_RAM) { + /* reserved head and tail space of pbuf */ + pbuf_header(p, -MBUF_HEAD_SPACE); + p->len -= MBUF_TAIL_SPACE; + p->tot_len -= MBUF_TAIL_SPACE; + p->mb_flags |= PBUF_FLAG_MBUF_SPACE; + } + + mb_data_init(m, p); + return m; +} + +/* + * Free a mbuf. + */ +void mb_free(struct mbuf *m) +{ + if (m == NULL) { + MBUF_WRN("mb_free(), m is NULL\n"); + return; + } + + struct pbuf *p = m->m_pbuf; + if (p) { + p->mb_flags &= ~PBUF_FLAG_MBUF_REF; + pbuf_free(p); + } + memp_free(MEMP_MBUF, m); +} + +/* + * Create a new mbuf including all pbuf data. + */ +struct mbuf *mb_pbuf2mbuf(void *p) +{ + struct pbuf *pb = p; + struct mbuf *m; + + if ((pbuf_clen(pb) > 1) || ((pb->mb_flags & PBUF_FLAG_MBUF_SPACE) == 0)) { + /* copy all data from @pb to a new single pbuf */ + m = mb_get(pb->tot_len, 1); + if (m == NULL) + return NULL; + + if (pbuf_copy_partial(pb, m->m_data, pb->tot_len, 0) != pb->tot_len) { + mb_free(m); + return NULL; + } + } else { + /* no need to copy data, link @pb to a new mbuf header */ + m = mb_alloc(); + if (m == NULL) + return NULL; + + pbuf_ref(pb); /* @pb is referenced by @m now */ + mb_data_init(m, pb); + } + return m; +} + +/* + * Return a pbuf included in a mbuf + */ +void *mb_mbuf2pbuf(struct mbuf *m) +{ + struct pbuf *p = m->m_pbuf; + + p->payload = m->m_data; + p->tot_len = m->m_len; + p->len = m->m_len; + if (m->m_tailspace >= MBUF_TAIL_SPACE) + p->mb_flags |= PBUF_FLAG_MBUF_SPACE; + pbuf_ref(p); /* add reference to avoid freed from @m */ + return p; +} + +#endif /* (defined(__CONFIG_ARCH_APP_CORE) || !defined(__CONFIG_ARCH_DUAL_CORE)) */ +#endif /* (__CONFIG_MBUF_IMPL_MODE == 1) */ diff --git a/platform/mcu/xr871/src/sys/mbuf/mbuf_debug.h b/platform/mcu/xr871/src/sys/mbuf/mbuf_debug.h new file mode 100644 index 0000000000..f461474c79 --- /dev/null +++ b/platform/mcu/xr871/src/sys/mbuf/mbuf_debug.h @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _MBUF_DEBUG_H_ +#define _MBUF_DEBUG_H_ + +#include +#include "sys/xr_util.h" + +/* + * Debug + */ +#define MBUF_DBG_ON 0 +#define MBUF_WRN_ON 0 +#define MBUF_ERR_ON 1 +#define MBUF_ABORT_ON 0 + +#define MBUF_SYSLOG printf +#define MBUF_ABORT() sys_abort() + +#define MBUF_LOG(flags, fmt, arg...) \ + do { \ + if (flags) \ + MBUF_SYSLOG(fmt, ##arg); \ + } while (0) + +#define MBUF_DBG(fmt, arg...) MBUF_LOG(MBUF_DBG_ON, "[mbuf] "fmt, ##arg) +#define MBUF_WRN(fmt, arg...) MBUF_LOG(MBUF_WRN_ON, "[mbuf WRN] "fmt, ##arg) +#define MBUF_ERR(fmt, arg...) \ + do { \ + MBUF_LOG(MBUF_ERR_ON, "[mbuf ERR] %s():%d, "fmt, \ + __func__, __LINE__, ##arg); \ + if (MBUF_ABORT_ON) \ + MBUF_ABORT(); \ + } while (0) + +#endif /* _MBUF_DEBUG_H_ */ diff --git a/platform/mcu/xr871/src/sys/mbuf/mbuf_util.h b/platform/mcu/xr871/src/sys/mbuf/mbuf_util.h new file mode 100644 index 0000000000..6293fbebb6 --- /dev/null +++ b/platform/mcu/xr871/src/sys/mbuf/mbuf_util.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _MBUF_UTIL_H_ +#define _MBUF_UTIL_H_ + +#include +#include "mbuf_debug.h" + +/* + * Memory + */ +#define MB_MEMCPY(d, s, l) memcpy(d, s, l) +#define MB_MEMSET(d, c, l) memset(d, c, l) +#define MB_MEMCMP(a, b, l) memcmp(a, b, l) +#define MB_MEMMOVE(d, s, n) memmove(d, s, n) + +#endif /* _MBUF_UTIL_H_ */ diff --git a/platform/mcu/xr871/src/sys/sys.mk b/platform/mcu/xr871/src/sys/sys.mk new file mode 100644 index 0000000000..771fb0fafc --- /dev/null +++ b/platform/mcu/xr871/src/sys/sys.mk @@ -0,0 +1,11 @@ +NAME := sys + +$(NAME)_TYPE := kernel +$(NAME)_SOURCES := ducc/ducc.c \ + ducc/ducc_app.c \ + ducc/ducc_hw_mbox.c \ + ducc/ducc_mbox.c \ + mbuf/mbuf_1.c + + + diff --git a/platform/mcu/xr871/src/xz/decompress.c b/platform/mcu/xr871/src/xz/decompress.c new file mode 100644 index 0000000000..d1661d2036 --- /dev/null +++ b/platform/mcu/xr871/src/xz/decompress.c @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "xz/decompress.h" + +static struct xz_dec *s; + +int xz_uncompress_init(struct xz_buf *stream) +{ + xz_crc32_init(); + /* + * Support up to 32 KiB dictionary. The actually needed memory + * is allocated once the headers have been parsed. + */ + s = xz_dec_init(XZ_DYNALLOC, 1 << 15); + if (s == NULL) { + return -1; + } + + stream->in_pos = 0; + stream->in_size = 0; + stream->out_pos = 0; + stream->out_size = 0; + + return 1; +} + +int xz_uncompress_stream(struct xz_buf *stream, uint8_t *sbuf, uint32_t slen, + uint8_t *dbuf, uint32_t dlen, uint32_t *decomp_len) +{ + int status; + *decomp_len = 0; + + stream->in = sbuf; + stream->out = dbuf; + + if (stream->in_pos == stream->in_size) { + stream->in_size = slen; + stream->in_pos = 0; + } + + stream->out_size = dlen; + stream->out_pos = 0; + + status = xz_dec_run(s, stream); + + *decomp_len = stream->out_pos; + + return status; +} + +void xz_uncompress_end() +{ + xz_dec_end(s); +} + +static size_t xz_len_decode(const uint8_t buf[], size_t size_max, uint32_t *num) +{ + if (size_max == 0) + return 0; + + if (size_max > 9) + size_max = 9; + + *num = buf[0] & 0x7F; + size_t i = 0; + + while (buf[i++] & 0x80) { + if (i >= size_max || buf[i] == 0x00) + return 0; + + *num |= (uint32_t)(buf[i] & 0x7F) << (i * 7); + } + + return i; +} + +uint32_t xz_index_len(uint8_t *stream_footer) +{ + + uint32_t len = (stream_footer[7] << 24) + + (stream_footer[6] << 16) + + (stream_footer[5] << 8) + + stream_footer[4]; + return (len + 1) * 4; +} + +uint32_t xz_file_uncompress_size(uint8_t *index, uint32_t len) +{ + uint32_t num_len = 0; + uint32_t uncompress_size = 0; + uint8_t *temp = &index[1]; + + num_len += xz_len_decode(temp, len, &uncompress_size); + num_len += xz_len_decode(&temp[num_len], len - num_len, &uncompress_size); + xz_len_decode(&temp[num_len], len - num_len, &uncompress_size); + return uncompress_size; +} diff --git a/platform/mcu/xr871/src/xz/xz.h b/platform/mcu/xr871/src/xz/xz.h new file mode 100644 index 0000000000..d94d68bc42 --- /dev/null +++ b/platform/mcu/xr871/src/xz/xz.h @@ -0,0 +1,286 @@ +/* + * XZ decompressor + * + * Authors: Lasse Collin + * Igor Pavlov + * + * This file has been put into the public domain. + * You can do whatever you want with this file. + */ + +#ifndef XZ_H +#define XZ_H + +#ifdef __KERNEL__ +# include +# include +# include +#else +# include +# include +# include +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* In Linux, this is used to make extern functions static when needed. */ +#ifndef XZ_EXTERN +# define XZ_EXTERN extern +#endif + +/** + * enum xz_mode - Operation mode + * + * @XZ_SINGLE: Single-call mode. This uses less RAM than + * than multi-call modes, because the LZMA2 + * dictionary doesn't need to be allocated as + * part of the decoder state. All required data + * structures are allocated at initialization, + * so xz_dec_run() cannot return XZ_MEM_ERROR. + * @XZ_PREALLOC: Multi-call mode with preallocated LZMA2 + * dictionary buffer. All data structures are + * allocated at initialization, so xz_dec_run() + * cannot return XZ_MEM_ERROR. + * @XZ_DYNALLOC: Multi-call mode. The LZMA2 dictionary is + * allocated once the required size has been + * parsed from the stream headers. If the + * allocation fails, xz_dec_run() will return + * XZ_MEM_ERROR. + * + * It is possible to enable support only for a subset of the above + * modes at compile time by defining XZ_DEC_SINGLE, XZ_DEC_PREALLOC, + * or XZ_DEC_DYNALLOC. The xz_dec kernel module is always compiled + * with support for all operation modes, but the preboot code may + * be built with fewer features to minimize code size. + */ +enum xz_mode { + XZ_SINGLE, + XZ_PREALLOC, + XZ_DYNALLOC +}; + +/** + * enum xz_ret - Return codes + * @XZ_OK: Everything is OK so far. More input or more + * output space is required to continue. This + * return code is possible only in multi-call mode + * (XZ_PREALLOC or XZ_DYNALLOC). + * @XZ_STREAM_END: Operation finished successfully. + * @XZ_UNSUPPORTED_CHECK: Integrity check type is not supported. Decoding + * is still possible in multi-call mode by simply + * calling xz_dec_run() again. + * Note that this return value is used only if + * XZ_DEC_ANY_CHECK was defined at build time, + * which is not used in the kernel. Unsupported + * check types return XZ_OPTIONS_ERROR if + * XZ_DEC_ANY_CHECK was not defined at build time. + * @XZ_MEM_ERROR: Allocating memory failed. This return code is + * possible only if the decoder was initialized + * with XZ_DYNALLOC. The amount of memory that was + * tried to be allocated was no more than the + * dict_max argument given to xz_dec_init(). + * @XZ_MEMLIMIT_ERROR: A bigger LZMA2 dictionary would be needed than + * allowed by the dict_max argument given to + * xz_dec_init(). This return value is possible + * only in multi-call mode (XZ_PREALLOC or + * XZ_DYNALLOC); the single-call mode (XZ_SINGLE) + * ignores the dict_max argument. + * @XZ_FORMAT_ERROR: File format was not recognized (wrong magic + * bytes). + * @XZ_OPTIONS_ERROR: This implementation doesn't support the requested + * compression options. In the decoder this means + * that the header CRC32 matches, but the header + * itself specifies something that we don't support. + * @XZ_DATA_ERROR: Compressed data is corrupt. + * @XZ_BUF_ERROR: Cannot make any progress. Details are slightly + * different between multi-call and single-call + * mode; more information below. + * + * In multi-call mode, XZ_BUF_ERROR is returned when two consecutive calls + * to XZ code cannot consume any input and cannot produce any new output. + * This happens when there is no new input available, or the output buffer + * is full while at least one output byte is still pending. Assuming your + * code is not buggy, you can get this error only when decoding a compressed + * stream that is truncated or otherwise corrupt. + * + * In single-call mode, XZ_BUF_ERROR is returned only when the output buffer + * is too small or the compressed input is corrupt in a way that makes the + * decoder produce more output than the caller expected. When it is + * (relatively) clear that the compressed input is truncated, XZ_DATA_ERROR + * is used instead of XZ_BUF_ERROR. + */ +enum xz_ret { + XZ_OK, + XZ_STREAM_END, + XZ_UNSUPPORTED_CHECK, + XZ_MEM_ERROR, + XZ_MEMLIMIT_ERROR, + XZ_FORMAT_ERROR, + XZ_OPTIONS_ERROR, + XZ_DATA_ERROR, + XZ_BUF_ERROR +}; + +/** + * struct xz_buf - Passing input and output buffers to XZ code + * @in: Beginning of the input buffer. This may be NULL if and only + * if in_pos is equal to in_size. + * @in_pos: Current position in the input buffer. This must not exceed + * in_size. + * @in_size: Size of the input buffer + * @out: Beginning of the output buffer. This may be NULL if and only + * if out_pos is equal to out_size. + * @out_pos: Current position in the output buffer. This must not exceed + * out_size. + * @out_size: Size of the output buffer + * + * Only the contents of the output buffer from out[out_pos] onward, and + * the variables in_pos and out_pos are modified by the XZ code. + */ +struct xz_buf { + const uint8_t *in; + size_t in_pos; + size_t in_size; + + uint8_t *out; + size_t out_pos; + size_t out_size; +}; + +/** + * struct xz_dec - Opaque type to hold the XZ decoder state + */ +struct xz_dec; + +/** + * xz_dec_init() - Allocate and initialize a XZ decoder state + * @mode: Operation mode + * @dict_max: Maximum size of the LZMA2 dictionary (history buffer) for + * multi-call decoding. This is ignored in single-call mode + * (mode == XZ_SINGLE). LZMA2 dictionary is always 2^n bytes + * or 2^n + 2^(n-1) bytes (the latter sizes are less common + * in practice), so other values for dict_max don't make sense. + * In the kernel, dictionary sizes of 64 KiB, 128 KiB, 256 KiB, + * 512 KiB, and 1 MiB are probably the only reasonable values, + * except for kernel and initramfs images where a bigger + * dictionary can be fine and useful. + * + * Single-call mode (XZ_SINGLE): xz_dec_run() decodes the whole stream at + * once. The caller must provide enough output space or the decoding will + * fail. The output space is used as the dictionary buffer, which is why + * there is no need to allocate the dictionary as part of the decoder's + * internal state. + * + * Because the output buffer is used as the workspace, streams encoded using + * a big dictionary are not a problem in single-call mode. It is enough that + * the output buffer is big enough to hold the actual uncompressed data; it + * can be smaller than the dictionary size stored in the stream headers. + * + * Multi-call mode with preallocated dictionary (XZ_PREALLOC): dict_max bytes + * of memory is preallocated for the LZMA2 dictionary. This way there is no + * risk that xz_dec_run() could run out of memory, since xz_dec_run() will + * never allocate any memory. Instead, if the preallocated dictionary is too + * small for decoding the given input stream, xz_dec_run() will return + * XZ_MEMLIMIT_ERROR. Thus, it is important to know what kind of data will be + * decoded to avoid allocating excessive amount of memory for the dictionary. + * + * Multi-call mode with dynamically allocated dictionary (XZ_DYNALLOC): + * dict_max specifies the maximum allowed dictionary size that xz_dec_run() + * may allocate once it has parsed the dictionary size from the stream + * headers. This way excessive allocations can be avoided while still + * limiting the maximum memory usage to a sane value to prevent running the + * system out of memory when decompressing streams from untrusted sources. + * + * On success, xz_dec_init() returns a pointer to struct xz_dec, which is + * ready to be used with xz_dec_run(). If memory allocation fails, + * xz_dec_init() returns NULL. + */ +XZ_EXTERN struct xz_dec *xz_dec_init(enum xz_mode mode, uint32_t dict_max); + +/** + * xz_dec_run() - Run the XZ decoder + * @s: Decoder state allocated using xz_dec_init() + * @b: Input and output buffers + * + * The possible return values depend on build options and operation mode. + * See enum xz_ret for details. + * + * Note that if an error occurs in single-call mode (return value is not + * XZ_STREAM_END), b->in_pos and b->out_pos are not modified and the + * contents of the output buffer from b->out[b->out_pos] onward are + * undefined. This is true even after XZ_BUF_ERROR, because with some filter + * chains, there may be a second pass over the output buffer, and this pass + * cannot be properly done if the output buffer is truncated. Thus, you + * cannot give the single-call decoder a too small buffer and then expect to + * get that amount valid data from the beginning of the stream. You must use + * the multi-call decoder if you don't want to uncompress the whole stream. + */ +XZ_EXTERN enum xz_ret xz_dec_run(struct xz_dec *s, struct xz_buf *b); + +/** + * xz_dec_reset() - Reset an already allocated decoder state + * @s: Decoder state allocated using xz_dec_init() + * + * This function can be used to reset the multi-call decoder state without + * freeing and reallocating memory with xz_dec_end() and xz_dec_init(). + * + * In single-call mode, xz_dec_reset() is always called in the beginning of + * xz_dec_run(). Thus, explicit call to xz_dec_reset() is useful only in + * multi-call mode. + */ +XZ_EXTERN void xz_dec_reset(struct xz_dec *s); + +/** + * xz_dec_end() - Free the memory allocated for the decoder state + * @s: Decoder state allocated using xz_dec_init(). If s is NULL, + * this function does nothing. + */ +XZ_EXTERN void xz_dec_end(struct xz_dec *s); + +/* + * Standalone build (userspace build or in-kernel build for boot time use) + * needs a CRC32 implementation. For normal in-kernel use, kernel's own + * CRC32 module is used instead, and users of this module don't need to + * care about the functions below. + */ +#ifndef XZ_INTERNAL_CRC32 +# ifdef __KERNEL__ +# define XZ_INTERNAL_CRC32 0 +# else +# define XZ_INTERNAL_CRC32 1 +# endif +#endif + +#if XZ_INTERNAL_CRC32 +/* + * This must be called before any other xz_* function to initialize + * the CRC32 lookup table. + */ +XZ_EXTERN void xz_crc32_init(void); + +/* + * Update CRC32 value using the polynomial from IEEE-802.3. To start a new + * calculation, the third argument must be zero. To continue the calculation, + * the previously returned value is passed as the third argument. + */ +XZ_EXTERN uint32_t xz_crc32(const uint8_t *buf, size_t size, uint32_t crc); +#endif + +#ifdef __cplusplus +} +#endif + +/* This function verifies if the input buffer matches the xz header. + * If so, it returns 0 else -1 */ +static inline int verify_xz_header(uint8_t *buffer) +{ + const uint8_t xz_header[6] = { 0xFD, '7', 'z', 'X', 'Z', 0x00 }; + if (buffer) { + if (!memcmp(buffer, xz_header, 6)) + return 0; + } + return -1; +} +#endif diff --git a/platform/mcu/xr871/src/xz/xz.mk b/platform/mcu/xr871/src/xz/xz.mk new file mode 100644 index 0000000000..71229bdda5 --- /dev/null +++ b/platform/mcu/xr871/src/xz/xz.mk @@ -0,0 +1,7 @@ +NAME := xz + +$(NAME)_TYPE := kernel +$(NAME)_SOURCES := decompress.c \ + xz_crc32.c \ + xz_dec_lzma2.c \ + xz_dec_stream.c diff --git a/platform/mcu/xr871/src/xz/xz_config.h b/platform/mcu/xr871/src/xz/xz_config.h new file mode 100644 index 0000000000..c2cc774c0e --- /dev/null +++ b/platform/mcu/xr871/src/xz/xz_config.h @@ -0,0 +1,111 @@ +/* + * Private includes and definitions for userspace use of XZ Embedded + * + * Author: Lasse Collin + * + * This file has been put into the public domain. + * You can do whatever you want with this file. + */ + +#ifndef XZ_CONFIG_H +#define XZ_CONFIG_H + +/* Uncomment as needed to enable BCJ filter decoders. */ +/* #define XZ_DEC_X86 */ +/* #define XZ_DEC_POWERPC */ +/* #define XZ_DEC_IA64 */ +/* #define XZ_DEC_ARM */ +/* #define XZ_DEC_ARMTHUMB */ +/* #define XZ_DEC_SPARC */ + +#include +#include +#include +#include + +#include "xz/xz.h" + + +#define kmalloc(size, flags) malloc(size) +#define kfree(ptr) free(ptr) +#define vmalloc(size) malloc(size) +#define vfree(ptr) free(ptr) + +#define memeq(a, b, size) (memcmp(a, b, size) == 0) +#define memzero(buf, size) memset(buf, 0, size) + +#ifndef min +# define min(x, y) ((x) < (y) ? (x) : (y)) +#endif +#define min_t(type, x, y) min(x, y) + +/* + * Some functions have been marked with __always_inline to keep the + * performance reasonable even when the compiler is optimizing for + * small code size. You may be able to save a few bytes by #defining + * __always_inline to plain inline, but don't complain if the code + * becomes slow. + * + * NOTE: System headers on GNU/Linux may #define this macro already, + * so if you want to change it, you need to #undef it first. + */ +#ifndef __always_inline +# ifdef __GNUC__ +# define __always_inline \ + inline __attribute__((__always_inline__)) +# else +# define __always_inline inline +# endif +#endif + +/* Inline functions to access unaligned unsigned 32-bit integers */ +#ifndef get_unaligned_le32 +static inline uint32_t get_unaligned_le32(const uint8_t *buf) +{ + return (uint32_t)buf[0] + | ((uint32_t)buf[1] << 8) + | ((uint32_t)buf[2] << 16) + | ((uint32_t)buf[3] << 24); +} +#endif + +#ifndef get_unaligned_be32 +static inline uint32_t get_unaligned_be32(const uint8_t *buf) +{ + return (uint32_t)(buf[0] << 24) + | ((uint32_t)buf[1] << 16) + | ((uint32_t)buf[2] << 8) + | (uint32_t)buf[3]; +} +#endif + +#ifndef put_unaligned_le32 +static inline void put_unaligned_le32(uint32_t val, uint8_t *buf) +{ + buf[0] = (uint8_t)val; + buf[1] = (uint8_t)(val >> 8); + buf[2] = (uint8_t)(val >> 16); + buf[3] = (uint8_t)(val >> 24); +} +#endif + +#ifndef put_unaligned_be32 +static inline void put_unaligned_be32(uint32_t val, uint8_t *buf) +{ + buf[0] = (uint8_t)(val >> 24); + buf[1] = (uint8_t)(val >> 16); + buf[2] = (uint8_t)(val >> 8); + buf[3] = (uint8_t)val; +} +#endif + +/* + * Use get_unaligned_le32() also for aligned access for simplicity. On + * little endian systems, #define get_le32(ptr) (*(const uint32_t *)(ptr)) + * could save a few bytes in code size. + */ +#ifndef get_le32 +# define get_le32 get_unaligned_le32 +#endif + +#endif diff --git a/platform/mcu/xr871/src/xz/xz_crc32.c b/platform/mcu/xr871/src/xz/xz_crc32.c new file mode 100644 index 0000000000..e48ab95f24 --- /dev/null +++ b/platform/mcu/xr871/src/xz/xz_crc32.c @@ -0,0 +1,89 @@ +/* + * CRC32 using the polynomial from IEEE-802.3 + * + * Authors: Lasse Collin + * Igor Pavlov + * + * This file has been put into the public domain. + * You can do whatever you want with this file. + */ + +/* + * This is not the fastest implementation, but it is pretty compact. + * The fastest versions of xz_crc32() on modern CPUs without hardware + * accelerated CRC instruction are 3-5 times as fast as this version, + * but they are bigger and use more memory for the lookup table. + */ + +#include "xz_private.h" + +/* + * STATIC_RW_DATA is used in the pre-boot environment on some architectures. + * See for details. + */ +#ifndef STATIC_RW_DATA +# define STATIC_RW_DATA static +#endif + +static const uint32_t xz_crc32_table[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 +}; + +XZ_EXTERN void xz_crc32_init(void) +{ + return; +} + +XZ_EXTERN uint32_t xz_crc32(const uint8_t *buf, size_t size, uint32_t crc) +{ + crc = ~crc; + + while (size != 0) { + crc = xz_crc32_table[*buf++ ^ (crc & 0xFF)] ^ (crc >> 8); + --size; + } + + return ~crc; +} diff --git a/platform/mcu/xr871/src/xz/xz_dec_lzma2.c b/platform/mcu/xr871/src/xz/xz_dec_lzma2.c new file mode 100644 index 0000000000..0c58f16949 --- /dev/null +++ b/platform/mcu/xr871/src/xz/xz_dec_lzma2.c @@ -0,0 +1,1172 @@ +/* + * LZMA2 decoder + * + * Authors: Lasse Collin + * Igor Pavlov + * + * This file has been put into the public domain. + * You can do whatever you want with this file. + */ + +#include "xz_private.h" +#include "xz_lzma2.h" +#include "compiler.h" + +/* + * Range decoder initialization eats the first five bytes of each LZMA chunk. + */ +#define RC_INIT_BYTES 5 + +/* + * Minimum number of usable input buffer to safely decode one LZMA symbol. + * The worst case is that we decode 22 bits using probabilities and 26 + * direct bits. This may decode at maximum of 20 bytes of input. However, + * lzma_main() does an extra normalization before returning, thus we + * need to put 21 here. + */ +#define LZMA_IN_REQUIRED 21 + +/* + * Dictionary (history buffer) + * + * These are always true: + * start <= pos <= full <= end + * pos <= limit <= end + * + * In multi-call mode, also these are true: + * end == size + * size <= size_max + * allocated <= size + * + * Most of these variables are size_t to support single-call mode, + * in which the dictionary variables address the actual output + * buffer directly. + */ +struct dictionary { + /* Beginning of the history buffer */ + uint8_t *buf; + + /* Old position in buf (before decoding more data) */ + size_t start; + + /* Position in buf */ + size_t pos; + + /* + * How full dictionary is. This is used to detect corrupt input that + * would read beyond the beginning of the uncompressed stream. + */ + size_t full; + + /* Write limit; we don't write to buf[limit] or later bytes. */ + size_t limit; + + /* + * End of the dictionary buffer. In multi-call mode, this is + * the same as the dictionary size. In single-call mode, this + * indicates the size of the output buffer. + */ + size_t end; + + /* + * Size of the dictionary as specified in Block Header. This is used + * together with "full" to detect corrupt input that would make us + * read beyond the beginning of the uncompressed stream. + */ + uint32_t size; + + /* + * Maximum allowed dictionary size in multi-call mode. + * This is ignored in single-call mode. + */ + uint32_t size_max; + + /* + * Amount of memory currently allocated for the dictionary. + * This is used only with XZ_DYNALLOC. (With XZ_PREALLOC, + * size_max is always the same as the allocated size.) + */ + uint32_t allocated; + + /* Operation mode */ + enum xz_mode mode; +}; + +/* Range decoder */ +struct rc_dec { + uint32_t range; + uint32_t code; + + /* + * Number of initializing bytes remaining to be read + * by rc_read_init(). + */ + uint32_t init_bytes_left; + + /* + * Buffer from which we read our input. It can be either + * temp.buf or the caller-provided input buffer. + */ + const uint8_t *in; + size_t in_pos; + size_t in_limit; +}; + +/* Probabilities for a length decoder. */ +struct lzma_len_dec { + /* Probability of match length being at least 10 */ + uint16_t choice; + + /* Probability of match length being at least 18 */ + uint16_t choice2; + + /* Probabilities for match lengths 2-9 */ + uint16_t low[POS_STATES_MAX][LEN_LOW_SYMBOLS]; + + /* Probabilities for match lengths 10-17 */ + uint16_t mid[POS_STATES_MAX][LEN_MID_SYMBOLS]; + + /* Probabilities for match lengths 18-273 */ + uint16_t high[LEN_HIGH_SYMBOLS]; +}; + +struct lzma_dec { + /* Distances of latest four matches */ + uint32_t rep0; + uint32_t rep1; + uint32_t rep2; + uint32_t rep3; + + /* Types of the most recently seen LZMA symbols */ + enum lzma_state state; + + /* + * Length of a match. This is updated so that dict_repeat can + * be called again to finish repeating the whole match. + */ + uint32_t len; + + /* + * LZMA properties or related bit masks (number of literal + * context bits, a mask dervied from the number of literal + * position bits, and a mask dervied from the number + * position bits) + */ + uint32_t lc; + uint32_t literal_pos_mask; /* (1 << lp) - 1 */ + uint32_t pos_mask; /* (1 << pb) - 1 */ + + /* If 1, it's a match. Otherwise it's a single 8-bit literal. */ + uint16_t is_match[STATES][POS_STATES_MAX]; + + /* If 1, it's a repeated match. The distance is one of rep0 .. rep3. */ + uint16_t is_rep[STATES]; + + /* + * If 0, distance of a repeated match is rep0. + * Otherwise check is_rep1. + */ + uint16_t is_rep0[STATES]; + + /* + * If 0, distance of a repeated match is rep1. + * Otherwise check is_rep2. + */ + uint16_t is_rep1[STATES]; + + /* If 0, distance of a repeated match is rep2. Otherwise it is rep3. */ + uint16_t is_rep2[STATES]; + + /* + * If 1, the repeated match has length of one byte. Otherwise + * the length is decoded from rep_len_decoder. + */ + uint16_t is_rep0_long[STATES][POS_STATES_MAX]; + + /* + * Probability tree for the highest two bits of the match + * distance. There is a separate probability tree for match + * lengths of 2 (i.e. MATCH_LEN_MIN), 3, 4, and [5, 273]. + */ + uint16_t dist_slot[DIST_STATES][DIST_SLOTS]; + + /* + * Probility trees for additional bits for match distance + * when the distance is in the range [4, 127]. + */ + uint16_t dist_special[FULL_DISTANCES - DIST_MODEL_END]; + + /* + * Probability tree for the lowest four bits of a match + * distance that is equal to or greater than 128. + */ + uint16_t dist_align[ALIGN_SIZE]; + + /* Length of a normal match */ + struct lzma_len_dec match_len_dec; + + /* Length of a repeated match */ + struct lzma_len_dec rep_len_dec; + + /* Probabilities of literals */ + uint16_t literal[LITERAL_CODERS_MAX][LITERAL_CODER_SIZE]; +}; + +struct lzma2_dec { + /* Position in xz_dec_lzma2_run(). */ + enum lzma2_seq { + SEQ_CONTROL, + SEQ_UNCOMPRESSED_1, + SEQ_UNCOMPRESSED_2, + SEQ_COMPRESSED_0, + SEQ_COMPRESSED_1, + SEQ_PROPERTIES, + SEQ_LZMA_PREPARE, + SEQ_LZMA_RUN, + SEQ_COPY + } sequence; + + /* Next position after decoding the compressed size of the chunk. */ + enum lzma2_seq next_sequence; + + /* Uncompressed size of LZMA chunk (2 MiB at maximum) */ + uint32_t uncompressed; + + /* + * Compressed size of LZMA chunk or compressed/uncompressed + * size of uncompressed chunk (64 KiB at maximum) + */ + uint32_t compressed; + + /* + * True if dictionary reset is needed. This is false before + * the first chunk (LZMA or uncompressed). + */ + bool need_dict_reset; + + /* + * True if new LZMA properties are needed. This is false + * before the first LZMA chunk. + */ + bool need_props; +}; + +struct xz_dec_lzma2 { + /* + * The order below is important on x86 to reduce code size and + * it shouldn't hurt on other platforms. Everything up to and + * including lzma.pos_mask are in the first 128 bytes on x86-32, + * which allows using smaller instructions to access those + * variables. On x86-64, fewer variables fit into the first 128 + * bytes, but this is still the best order without sacrificing + * the readability by splitting the structures. + */ + struct rc_dec rc; + struct dictionary dict; + struct lzma2_dec lzma2; + struct lzma_dec lzma; + + /* + * Temporary buffer which holds small number of input bytes between + * decoder calls. See lzma2_lzma() for details. + */ + struct { + uint32_t size; + uint8_t buf[3 * LZMA_IN_REQUIRED]; + } temp; +}; + +/************** + * Dictionary * + **************/ + +/* + * Reset the dictionary state. When in single-call mode, set up the beginning + * of the dictionary to point to the actual output buffer. + */ +static void dict_reset(struct dictionary *dict, struct xz_buf *b) +{ + if (DEC_IS_SINGLE(dict->mode)) { + dict->buf = b->out + b->out_pos; + dict->end = b->out_size - b->out_pos; + } + + dict->start = 0; + dict->pos = 0; + dict->limit = 0; + dict->full = 0; +} + +/* Set dictionary write limit */ +static void dict_limit(struct dictionary *dict, size_t out_max) +{ + if (dict->end - dict->pos <= out_max) + dict->limit = dict->end; + else + dict->limit = dict->pos + out_max; +} + +/* Return true if at least one byte can be written into the dictionary. */ +static inline bool dict_has_space(const struct dictionary *dict) +{ + return dict->pos < dict->limit; +} + +/* + * Get a byte from the dictionary at the given distance. The distance is + * assumed to valid, or as a special case, zero when the dictionary is + * still empty. This special case is needed for single-call decoding to + * avoid writing a '\0' to the end of the destination buffer. + */ +static inline uint32_t dict_get(const struct dictionary *dict, uint32_t dist) +{ + size_t offset = dict->pos - dist - 1; + + if (dist >= dict->pos) + offset += dict->end; + + return dict->full > 0 ? dict->buf[offset] : 0; +} + +/* + * Put one byte into the dictionary. It is assumed that there is space for it. + */ +static inline void dict_put(struct dictionary *dict, uint8_t byte) +{ + dict->buf[dict->pos++] = byte; + + if (dict->full < dict->pos) + dict->full = dict->pos; +} + +/* + * Repeat given number of bytes from the given distance. If the distance is + * invalid, false is returned. On success, true is returned and *len is + * updated to indicate how many bytes were left to be repeated. + */ +static bool dict_repeat(struct dictionary *dict, uint32_t *len, uint32_t dist) +{ + size_t back; + uint32_t left; + + if (dist >= dict->full || dist >= dict->size) + return false; + + left = min_t(size_t, dict->limit - dict->pos, *len); + *len -= left; + + back = dict->pos - dist - 1; + if (dist >= dict->pos) + back += dict->end; + + do { + dict->buf[dict->pos++] = dict->buf[back++]; + if (back == dict->end) + back = 0; + } while (--left > 0); + + if (dict->full < dict->pos) + dict->full = dict->pos; + + return true; +} + +/* Copy uncompressed data as is from input to dictionary and output buffers. */ +static void dict_uncompressed(struct dictionary *dict, struct xz_buf *b, + uint32_t *left) +{ + size_t copy_size; + + while (*left > 0 && b->in_pos < b->in_size + && b->out_pos < b->out_size) { + copy_size = min(b->in_size - b->in_pos, + b->out_size - b->out_pos); + if (copy_size > dict->end - dict->pos) + copy_size = dict->end - dict->pos; + if (copy_size > *left) + copy_size = *left; + + *left -= copy_size; + + memcpy(dict->buf + dict->pos, b->in + b->in_pos, copy_size); + dict->pos += copy_size; + + if (dict->full < dict->pos) + dict->full = dict->pos; + + if (DEC_IS_MULTI(dict->mode)) { + if (dict->pos == dict->end) + dict->pos = 0; + + memcpy(b->out + b->out_pos, b->in + b->in_pos, + copy_size); + } + + dict->start = dict->pos; + + b->out_pos += copy_size; + b->in_pos += copy_size; + } +} + +/* + * Flush pending data from dictionary to b->out. It is assumed that there is + * enough space in b->out. This is guaranteed because caller uses dict_limit() + * before decoding data into the dictionary. + */ +static uint32_t dict_flush(struct dictionary *dict, struct xz_buf *b) +{ + size_t copy_size = dict->pos - dict->start; + + if (DEC_IS_MULTI(dict->mode)) { + if (dict->pos == dict->end) + dict->pos = 0; + + memcpy(b->out + b->out_pos, dict->buf + dict->start, + copy_size); + } + + dict->start = dict->pos; + b->out_pos += copy_size; + return copy_size; +} + +/***************** + * Range decoder * + *****************/ + +/* Reset the range decoder. */ +static void rc_reset(struct rc_dec *rc) +{ + rc->range = (uint32_t)-1; + rc->code = 0; + rc->init_bytes_left = RC_INIT_BYTES; +} + +/* + * Read the first five initial bytes into rc->code if they haven't been + * read already. (Yes, the first byte gets completely ignored.) + */ +static bool rc_read_init(struct rc_dec *rc, struct xz_buf *b) +{ + while (rc->init_bytes_left > 0) { + if (b->in_pos == b->in_size) + return false; + + rc->code = (rc->code << 8) + b->in[b->in_pos++]; + --rc->init_bytes_left; + } + + return true; +} + +/* Return true if there may not be enough input for the next decoding loop. */ +static inline bool rc_limit_exceeded(const struct rc_dec *rc) +{ + return rc->in_pos > rc->in_limit; +} + +/* + * Return true if it is possible (from point of view of range decoder) that + * we have reached the end of the LZMA chunk. + */ +static inline bool rc_is_finished(const struct rc_dec *rc) +{ + return rc->code == 0; +} + +/* Read the next input byte if needed. */ +static __always_inline void rc_normalize(struct rc_dec *rc) +{ + if (rc->range < RC_TOP_VALUE) { + rc->range <<= RC_SHIFT_BITS; + rc->code = (rc->code << RC_SHIFT_BITS) + rc->in[rc->in_pos++]; + } +} + +/* + * Decode one bit. In some versions, this function has been splitted in three + * functions so that the compiler is supposed to be able to more easily avoid + * an extra branch. In this particular version of the LZMA decoder, this + * doesn't seem to be a good idea (tested with GCC 3.3.6, 3.4.6, and 4.3.3 + * on x86). Using a non-splitted version results in nicer looking code too. + * + * NOTE: This must return an int. Do not make it return a bool or the speed + * of the code generated by GCC 3.x decreases 10-15 %. (GCC 4.3 doesn't care, + * and it generates 10-20 % faster code than GCC 3.x from this file anyway.) + */ +static __always_inline int rc_bit(struct rc_dec *rc, uint16_t *prob) +{ + uint32_t bound; + int bit; + + rc_normalize(rc); + bound = (rc->range >> RC_BIT_MODEL_TOTAL_BITS) * *prob; + if (rc->code < bound) { + rc->range = bound; + *prob += (RC_BIT_MODEL_TOTAL - *prob) >> RC_MOVE_BITS; + bit = 0; + } else { + rc->range -= bound; + rc->code -= bound; + *prob -= *prob >> RC_MOVE_BITS; + bit = 1; + } + + return bit; +} + +/* Decode a bittree starting from the most significant bit. */ +static __always_inline uint32_t rc_bittree(struct rc_dec *rc, + uint16_t *probs, uint32_t limit) +{ + uint32_t symbol = 1; + + do { + if (rc_bit(rc, &probs[symbol])) + symbol = (symbol << 1) + 1; + else + symbol <<= 1; + } while (symbol < limit); + + return symbol; +} + +/* Decode a bittree starting from the least significant bit. */ +static __always_inline void rc_bittree_reverse(struct rc_dec *rc, + uint16_t *probs, + uint32_t *dest, uint32_t limit) +{ + uint32_t symbol = 1; + uint32_t i = 0; + + do { + if (rc_bit(rc, &probs[symbol])) { + symbol = (symbol << 1) + 1; + *dest += 1 << i; + } else { + symbol <<= 1; + } + } while (++i < limit); +} + +/* Decode direct bits (fixed fifty-fifty probability) */ +static inline void rc_direct(struct rc_dec *rc, uint32_t *dest, uint32_t limit) +{ + uint32_t mask; + + do { + rc_normalize(rc); + rc->range >>= 1; + rc->code -= rc->range; + mask = (uint32_t)0 - (rc->code >> 31); + rc->code += rc->range & mask; + *dest = (*dest << 1) + (mask + 1); + } while (--limit > 0); +} + +/******** + * LZMA * + ********/ + +/* Get pointer to literal coder probability array. */ +static uint16_t *lzma_literal_probs(struct xz_dec_lzma2 *s) +{ + uint32_t prev_byte = dict_get(&s->dict, 0); + uint32_t low = prev_byte >> (8 - s->lzma.lc); + uint32_t high = (s->dict.pos & s->lzma.literal_pos_mask) << s->lzma.lc; + return s->lzma.literal[low + high]; +} + +/* Decode a literal (one 8-bit byte) */ +static void lzma_literal(struct xz_dec_lzma2 *s) +{ + uint16_t *probs; + uint32_t symbol; + uint32_t match_byte; + uint32_t match_bit; + uint32_t offset; + uint32_t i; + + probs = lzma_literal_probs(s); + + if (lzma_state_is_literal(s->lzma.state)) { + symbol = rc_bittree(&s->rc, probs, 0x100); + } else { + symbol = 1; + match_byte = dict_get(&s->dict, s->lzma.rep0) << 1; + offset = 0x100; + + do { + match_bit = match_byte & offset; + match_byte <<= 1; + i = offset + match_bit + symbol; + + if (rc_bit(&s->rc, &probs[i])) { + symbol = (symbol << 1) + 1; + offset &= match_bit; + } else { + symbol <<= 1; + offset &= ~match_bit; + } + } while (symbol < 0x100); + } + + dict_put(&s->dict, (uint8_t)symbol); + lzma_state_literal(&s->lzma.state); +} + +/* Decode the length of the match into s->lzma.len. */ +static void lzma_len(struct xz_dec_lzma2 *s, struct lzma_len_dec *l, + uint32_t pos_state) +{ + uint16_t *probs; + uint32_t limit; + + if (!rc_bit(&s->rc, &l->choice)) { + probs = l->low[pos_state]; + limit = LEN_LOW_SYMBOLS; + s->lzma.len = MATCH_LEN_MIN; + } else { + if (!rc_bit(&s->rc, &l->choice2)) { + probs = l->mid[pos_state]; + limit = LEN_MID_SYMBOLS; + s->lzma.len = MATCH_LEN_MIN + LEN_LOW_SYMBOLS; + } else { + probs = l->high; + limit = LEN_HIGH_SYMBOLS; + s->lzma.len = MATCH_LEN_MIN + LEN_LOW_SYMBOLS + + LEN_MID_SYMBOLS; + } + } + + s->lzma.len += rc_bittree(&s->rc, probs, limit) - limit; +} + +/* Decode a match. The distance will be stored in s->lzma.rep0. */ +static void lzma_match(struct xz_dec_lzma2 *s, uint32_t pos_state) +{ + uint16_t *probs; + uint32_t dist_slot; + uint32_t limit; + + lzma_state_match(&s->lzma.state); + + s->lzma.rep3 = s->lzma.rep2; + s->lzma.rep2 = s->lzma.rep1; + s->lzma.rep1 = s->lzma.rep0; + + lzma_len(s, &s->lzma.match_len_dec, pos_state); + + probs = s->lzma.dist_slot[lzma_get_dist_state(s->lzma.len)]; + dist_slot = rc_bittree(&s->rc, probs, DIST_SLOTS) - DIST_SLOTS; + + if (dist_slot < DIST_MODEL_START) { + s->lzma.rep0 = dist_slot; + } else { + limit = (dist_slot >> 1) - 1; + s->lzma.rep0 = 2 + (dist_slot & 1); + + if (dist_slot < DIST_MODEL_END) { + s->lzma.rep0 <<= limit; + probs = s->lzma.dist_special + s->lzma.rep0 + - dist_slot - 1; + rc_bittree_reverse(&s->rc, probs, + &s->lzma.rep0, limit); + } else { + rc_direct(&s->rc, &s->lzma.rep0, limit - ALIGN_BITS); + s->lzma.rep0 <<= ALIGN_BITS; + rc_bittree_reverse(&s->rc, s->lzma.dist_align, + &s->lzma.rep0, ALIGN_BITS); + } + } +} + +/* + * Decode a repeated match. The distance is one of the four most recently + * seen matches. The distance will be stored in s->lzma.rep0. + */ +static void lzma_rep_match(struct xz_dec_lzma2 *s, uint32_t pos_state) +{ + uint32_t tmp; + + if (!rc_bit(&s->rc, &s->lzma.is_rep0[s->lzma.state])) { + if (!rc_bit(&s->rc, &s->lzma.is_rep0_long[ + s->lzma.state][pos_state])) { + lzma_state_short_rep(&s->lzma.state); + s->lzma.len = 1; + return; + } + } else { + if (!rc_bit(&s->rc, &s->lzma.is_rep1[s->lzma.state])) { + tmp = s->lzma.rep1; + } else { + if (!rc_bit(&s->rc, &s->lzma.is_rep2[s->lzma.state])) { + tmp = s->lzma.rep2; + } else { + tmp = s->lzma.rep3; + s->lzma.rep3 = s->lzma.rep2; + } + + s->lzma.rep2 = s->lzma.rep1; + } + + s->lzma.rep1 = s->lzma.rep0; + s->lzma.rep0 = tmp; + } + + lzma_state_long_rep(&s->lzma.state); + lzma_len(s, &s->lzma.rep_len_dec, pos_state); +} + +/* LZMA decoder core */ +static bool lzma_main(struct xz_dec_lzma2 *s) +{ + uint32_t pos_state; + + /* + * If the dictionary was reached during the previous call, try to + * finish the possibly pending repeat in the dictionary. + */ + if (dict_has_space(&s->dict) && s->lzma.len > 0) + dict_repeat(&s->dict, &s->lzma.len, s->lzma.rep0); + + /* + * Decode more LZMA symbols. One iteration may consume up to + * LZMA_IN_REQUIRED - 1 bytes. + */ + while (dict_has_space(&s->dict) && !rc_limit_exceeded(&s->rc)) { + pos_state = s->dict.pos & s->lzma.pos_mask; + + if (!rc_bit(&s->rc, &s->lzma.is_match[ + s->lzma.state][pos_state])) { + lzma_literal(s); + } else { + if (rc_bit(&s->rc, &s->lzma.is_rep[s->lzma.state])) + lzma_rep_match(s, pos_state); + else + lzma_match(s, pos_state); + + if (!dict_repeat(&s->dict, &s->lzma.len, s->lzma.rep0)) + return false; + } + } + + /* + * Having the range decoder always normalized when we are outside + * this function makes it easier to correctly handle end of the chunk. + */ + rc_normalize(&s->rc); + + return true; +} + +/* + * Reset the LZMA decoder and range decoder state. Dictionary is nore reset + * here, because LZMA state may be reset without resetting the dictionary. + */ +static void lzma_reset(struct xz_dec_lzma2 *s) +{ + uint16_t *probs; + size_t i; + + s->lzma.state = STATE_LIT_LIT; + s->lzma.rep0 = 0; + s->lzma.rep1 = 0; + s->lzma.rep2 = 0; + s->lzma.rep3 = 0; + + /* + * All probabilities are initialized to the same value. This hack + * makes the code smaller by avoiding a separate loop for each + * probability array. + * + * This could be optimized so that only that part of literal + * probabilities that are actually required. In the common case + * we would write 12 KiB less. + */ + probs = s->lzma.is_match[0]; + for (i = 0; i < PROBS_TOTAL; ++i) + probs[i] = RC_BIT_MODEL_TOTAL / 2; + + rc_reset(&s->rc); +} + +/* + * Decode and validate LZMA properties (lc/lp/pb) and calculate the bit masks + * from the decoded lp and pb values. On success, the LZMA decoder state is + * reset and true is returned. + */ +static bool lzma_props(struct xz_dec_lzma2 *s, uint8_t props) +{ + if (props > (4 * 5 + 4) * 9 + 8) + return false; + + s->lzma.pos_mask = 0; + while (props >= 9 * 5) { + props -= 9 * 5; + ++s->lzma.pos_mask; + } + + s->lzma.pos_mask = (1 << s->lzma.pos_mask) - 1; + + s->lzma.literal_pos_mask = 0; + while (props >= 9) { + props -= 9; + ++s->lzma.literal_pos_mask; + } + + s->lzma.lc = props; + + if (s->lzma.lc + s->lzma.literal_pos_mask > 4) + return false; + + s->lzma.literal_pos_mask = (1 << s->lzma.literal_pos_mask) - 1; + + lzma_reset(s); + + return true; +} + +/********* + * LZMA2 * + *********/ + +/* + * The LZMA decoder assumes that if the input limit (s->rc.in_limit) hasn't + * been exceeded, it is safe to read up to LZMA_IN_REQUIRED bytes. This + * wrapper function takes care of making the LZMA decoder's assumption safe. + * + * As long as there is plenty of input left to be decoded in the current LZMA + * chunk, we decode directly from the caller-supplied input buffer until + * there's LZMA_IN_REQUIRED bytes left. Those remaining bytes are copied into + * s->temp.buf, which (hopefully) gets filled on the next call to this + * function. We decode a few bytes from the temporary buffer so that we can + * continue decoding from the caller-supplied input buffer again. + */ +static bool lzma2_lzma(struct xz_dec_lzma2 *s, struct xz_buf *b) +{ + size_t in_avail; + uint32_t tmp; + + in_avail = b->in_size - b->in_pos; + if (s->temp.size > 0 || s->lzma2.compressed == 0) { + tmp = 2 * LZMA_IN_REQUIRED - s->temp.size; + if (tmp > s->lzma2.compressed - s->temp.size) + tmp = s->lzma2.compressed - s->temp.size; + if (tmp > in_avail) + tmp = in_avail; + + memcpy(s->temp.buf + s->temp.size, b->in + b->in_pos, tmp); + + if (s->temp.size + tmp == s->lzma2.compressed) { + memzero(s->temp.buf + s->temp.size + tmp, + sizeof(s->temp.buf) + - s->temp.size - tmp); + s->rc.in_limit = s->temp.size + tmp; + } else if (s->temp.size + tmp < LZMA_IN_REQUIRED) { + s->temp.size += tmp; + b->in_pos += tmp; + return true; + } else { + s->rc.in_limit = s->temp.size + tmp - LZMA_IN_REQUIRED; + } + + s->rc.in = s->temp.buf; + s->rc.in_pos = 0; + + if (!lzma_main(s) || s->rc.in_pos > s->temp.size + tmp) + return false; + + s->lzma2.compressed -= s->rc.in_pos; + + if (s->rc.in_pos < s->temp.size) { + s->temp.size -= s->rc.in_pos; + memmove(s->temp.buf, s->temp.buf + s->rc.in_pos, + s->temp.size); + return true; + } + + b->in_pos += s->rc.in_pos - s->temp.size; + s->temp.size = 0; + } + + in_avail = b->in_size - b->in_pos; + if (in_avail >= LZMA_IN_REQUIRED) { + s->rc.in = b->in; + s->rc.in_pos = b->in_pos; + + if (in_avail >= s->lzma2.compressed + LZMA_IN_REQUIRED) + s->rc.in_limit = b->in_pos + s->lzma2.compressed; + else + s->rc.in_limit = b->in_size - LZMA_IN_REQUIRED; + + if (!lzma_main(s)) + return false; + + in_avail = s->rc.in_pos - b->in_pos; + if (in_avail > s->lzma2.compressed) + return false; + + s->lzma2.compressed -= in_avail; + b->in_pos = s->rc.in_pos; + } + + in_avail = b->in_size - b->in_pos; + if (in_avail < LZMA_IN_REQUIRED) { + if (in_avail > s->lzma2.compressed) + in_avail = s->lzma2.compressed; + + memcpy(s->temp.buf, b->in + b->in_pos, in_avail); + s->temp.size = in_avail; + b->in_pos += in_avail; + } + + return true; +} + +/* + * Take care of the LZMA2 control layer, and forward the job of actual LZMA + * decoding or copying of uncompressed chunks to other functions. + */ +XZ_EXTERN enum xz_ret xz_dec_lzma2_run(struct xz_dec_lzma2 *s, + struct xz_buf *b) +{ + uint32_t tmp; + + while (b->in_pos < b->in_size || s->lzma2.sequence == SEQ_LZMA_RUN) { + switch (s->lzma2.sequence) { + case SEQ_CONTROL: + /* + * LZMA2 control byte + * + * Exact values: + * 0x00 End marker + * 0x01 Dictionary reset followed by + * an uncompressed chunk + * 0x02 Uncompressed chunk (no dictionary reset) + * + * Highest three bits (s->control & 0xE0): + * 0xE0 Dictionary reset, new properties and state + * reset, followed by LZMA compressed chunk + * 0xC0 New properties and state reset, followed + * by LZMA compressed chunk (no dictionary + * reset) + * 0xA0 State reset using old properties, + * followed by LZMA compressed chunk (no + * dictionary reset) + * 0x80 LZMA chunk (no dictionary or state reset) + * + * For LZMA compressed chunks, the lowest five bits + * (s->control & 1F) are the highest bits of the + * uncompressed size (bits 16-20). + * + * A new LZMA2 stream must begin with a dictionary + * reset. The first LZMA chunk must set new + * properties and reset the LZMA state. + * + * Values that don't match anything described above + * are invalid and we return XZ_DATA_ERROR. + */ + tmp = b->in[b->in_pos++]; + + if (tmp == 0x00) + return XZ_STREAM_END; + + if (tmp >= 0xE0 || tmp == 0x01) { + s->lzma2.need_props = true; + s->lzma2.need_dict_reset = false; + dict_reset(&s->dict, b); + } else if (s->lzma2.need_dict_reset) { + return XZ_DATA_ERROR; + } + + if (tmp >= 0x80) { + s->lzma2.uncompressed = (tmp & 0x1F) << 16; + s->lzma2.sequence = SEQ_UNCOMPRESSED_1; + + if (tmp >= 0xC0) { + /* + * When there are new properties, + * state reset is done at + * SEQ_PROPERTIES. + */ + s->lzma2.need_props = false; + s->lzma2.next_sequence + = SEQ_PROPERTIES; + + } else if (s->lzma2.need_props) { + return XZ_DATA_ERROR; + + } else { + s->lzma2.next_sequence + = SEQ_LZMA_PREPARE; + if (tmp >= 0xA0) + lzma_reset(s); + } + } else { + if (tmp > 0x02) + return XZ_DATA_ERROR; + + s->lzma2.sequence = SEQ_COMPRESSED_0; + s->lzma2.next_sequence = SEQ_COPY; + } + + break; + + case SEQ_UNCOMPRESSED_1: + s->lzma2.uncompressed + += (uint32_t)b->in[b->in_pos++] << 8; + s->lzma2.sequence = SEQ_UNCOMPRESSED_2; + break; + + case SEQ_UNCOMPRESSED_2: + s->lzma2.uncompressed + += (uint32_t)b->in[b->in_pos++] + 1; + s->lzma2.sequence = SEQ_COMPRESSED_0; + break; + + case SEQ_COMPRESSED_0: + s->lzma2.compressed + = (uint32_t)b->in[b->in_pos++] << 8; + s->lzma2.sequence = SEQ_COMPRESSED_1; + break; + + case SEQ_COMPRESSED_1: + s->lzma2.compressed + += (uint32_t)b->in[b->in_pos++] + 1; + s->lzma2.sequence = s->lzma2.next_sequence; + break; + + case SEQ_PROPERTIES: + if (!lzma_props(s, b->in[b->in_pos++])) + return XZ_DATA_ERROR; + + s->lzma2.sequence = SEQ_LZMA_PREPARE; + + case SEQ_LZMA_PREPARE: + if (s->lzma2.compressed < RC_INIT_BYTES) + return XZ_DATA_ERROR; + + if (!rc_read_init(&s->rc, b)) + return XZ_OK; + + s->lzma2.compressed -= RC_INIT_BYTES; + s->lzma2.sequence = SEQ_LZMA_RUN; + + case SEQ_LZMA_RUN: + /* + * Set dictionary limit to indicate how much we want + * to be encoded at maximum. Decode new data into the + * dictionary. Flush the new data from dictionary to + * b->out. Check if we finished decoding this chunk. + * In case the dictionary got full but we didn't fill + * the output buffer yet, we may run this loop + * multiple times without changing s->lzma2.sequence. + */ + dict_limit(&s->dict, min_t(size_t, + b->out_size - b->out_pos, + s->lzma2.uncompressed)); + if (!lzma2_lzma(s, b)) + return XZ_DATA_ERROR; + + s->lzma2.uncompressed -= dict_flush(&s->dict, b); + + if (s->lzma2.uncompressed == 0) { + if (s->lzma2.compressed > 0 || s->lzma.len > 0 + || !rc_is_finished(&s->rc)) + return XZ_DATA_ERROR; + + rc_reset(&s->rc); + s->lzma2.sequence = SEQ_CONTROL; + + } else if (b->out_pos == b->out_size + || (b->in_pos == b->in_size + && s->temp.size + < s->lzma2.compressed)) { + return XZ_OK; + } + + break; + + case SEQ_COPY: + dict_uncompressed(&s->dict, b, &s->lzma2.compressed); + if (s->lzma2.compressed > 0) + return XZ_OK; + + s->lzma2.sequence = SEQ_CONTROL; + break; + } + } + + return XZ_OK; +} + +XZ_EXTERN struct xz_dec_lzma2 *xz_dec_lzma2_create(enum xz_mode mode, + uint32_t dict_max) +{ + struct xz_dec_lzma2 *s = kmalloc(sizeof(*s), GFP_KERNEL); + if (s == NULL) + return NULL; + + s->dict.mode = mode; + s->dict.size_max = dict_max; + + if (DEC_IS_PREALLOC(mode)) { + s->dict.buf = vmalloc(dict_max); + if (s->dict.buf == NULL) { + kfree(s); + return NULL; + } + } else if (DEC_IS_DYNALLOC(mode)) { + s->dict.buf = NULL; + s->dict.allocated = 0; + } + + return s; +} + +XZ_EXTERN enum xz_ret xz_dec_lzma2_reset(struct xz_dec_lzma2 *s, uint8_t props) +{ + /* This limits dictionary size to 3 GiB to keep parsing simpler. */ + if (props > 39) + return XZ_OPTIONS_ERROR; + + s->dict.size = 2 + (props & 1); + s->dict.size <<= (props >> 1) + 11; + + if (DEC_IS_MULTI(s->dict.mode)) { + if (s->dict.size > s->dict.size_max) + return XZ_MEMLIMIT_ERROR; + + s->dict.end = s->dict.size; + + if (DEC_IS_DYNALLOC(s->dict.mode)) { + if (s->dict.allocated < s->dict.size) { + vfree(s->dict.buf); + s->dict.buf = vmalloc(s->dict.size); + if (s->dict.buf == NULL) { + s->dict.allocated = 0; + return XZ_MEM_ERROR; + } + } + } + } + + s->lzma.len = 0; + + s->lzma2.sequence = SEQ_CONTROL; + s->lzma2.need_dict_reset = true; + + s->temp.size = 0; + + return XZ_OK; +} + +XZ_EXTERN void xz_dec_lzma2_end(struct xz_dec_lzma2 *s) +{ + if (DEC_IS_MULTI(s->dict.mode)) + vfree(s->dict.buf); + + kfree(s); +} diff --git a/platform/mcu/xr871/src/xz/xz_dec_stream.c b/platform/mcu/xr871/src/xz/xz_dec_stream.c new file mode 100644 index 0000000000..ac809b1e64 --- /dev/null +++ b/platform/mcu/xr871/src/xz/xz_dec_stream.c @@ -0,0 +1,821 @@ +/* + * .xz Stream decoder + * + * Author: Lasse Collin + * + * This file has been put into the public domain. + * You can do whatever you want with this file. + */ + +#include "xz_private.h" +#include "xz_stream.h" + +/* Hash used to validate the Index field */ +struct xz_dec_hash { + vli_type unpadded; + vli_type uncompressed; + uint32_t crc32; +}; + +struct xz_dec { + /* Position in dec_main() */ + enum { + SEQ_STREAM_HEADER, + SEQ_BLOCK_START, + SEQ_BLOCK_HEADER, + SEQ_BLOCK_UNCOMPRESS, + SEQ_BLOCK_PADDING, + SEQ_BLOCK_CHECK, + SEQ_INDEX, + SEQ_INDEX_PADDING, + SEQ_INDEX_CRC32, + SEQ_STREAM_FOOTER + } sequence; + + /* Position in variable-length integers and Check fields */ + uint32_t pos; + + /* Variable-length integer decoded by dec_vli() */ + vli_type vli; + + /* Saved in_pos and out_pos */ + size_t in_start; + size_t out_start; + + /* CRC32 value in Block or Index */ + uint32_t crc32; + + /* Type of the integrity check calculated from uncompressed data */ + enum xz_check check_type; + + /* Operation mode */ + enum xz_mode mode; + + /* + * True if the next call to xz_dec_run() is allowed to return + * XZ_BUF_ERROR. + */ + bool allow_buf_error; + + /* Information stored in Block Header */ + struct { + /* + * Value stored in the Compressed Size field, or + * VLI_UNKNOWN if Compressed Size is not present. + */ + vli_type compressed; + + /* + * Value stored in the Uncompressed Size field, or + * VLI_UNKNOWN if Uncompressed Size is not present. + */ + vli_type uncompressed; + + /* Size of the Block Header field */ + uint32_t size; + } block_header; + + /* Information collected when decoding Blocks */ + struct { + /* Observed compressed size of the current Block */ + vli_type compressed; + + /* Observed uncompressed size of the current Block */ + vli_type uncompressed; + + /* Number of Blocks decoded so far */ + vli_type count; + + /* + * Hash calculated from the Block sizes. This is used to + * validate the Index field. + */ + struct xz_dec_hash hash; + } block; + + /* Variables needed when verifying the Index field */ + struct { + /* Position in dec_index() */ + enum { + SEQ_INDEX_COUNT, + SEQ_INDEX_UNPADDED, + SEQ_INDEX_UNCOMPRESSED + } sequence; + + /* Size of the Index in bytes */ + vli_type size; + + /* Number of Records (matches block.count in valid files) */ + vli_type count; + + /* + * Hash calculated from the Records (matches block.hash in + * valid files). + */ + struct xz_dec_hash hash; + } index; + + /* + * Temporary buffer needed to hold Stream Header, Block Header, + * and Stream Footer. The Block Header is the biggest (1 KiB) + * so we reserve space according to that. buf[] has to be aligned + * to a multiple of four bytes; the size_t variables before it + * should guarantee this. + */ + struct { + size_t pos; + size_t size; + uint8_t buf[1024]; + } temp; + + struct xz_dec_lzma2 *lzma2; + +#ifdef XZ_DEC_BCJ + struct xz_dec_bcj *bcj; + bool bcj_active; +#endif +}; + +#ifdef XZ_DEC_ANY_CHECK +/* Sizes of the Check field with different Check IDs */ +static const uint8_t check_sizes[16] = { + 0, + 4, 4, 4, + 8, 8, 8, + 16, 16, 16, + 32, 32, 32, + 64, 64, 64 +}; +#endif + +/* + * Fill s->temp by copying data starting from b->in[b->in_pos]. Caller + * must have set s->temp.pos to indicate how much data we are supposed + * to copy into s->temp.buf. Return true once s->temp.pos has reached + * s->temp.size. + */ +static bool fill_temp(struct xz_dec *s, struct xz_buf *b) +{ + size_t copy_size = min_t(size_t, + b->in_size - b->in_pos, s->temp.size - s->temp.pos); + + memcpy(s->temp.buf + s->temp.pos, b->in + b->in_pos, copy_size); + b->in_pos += copy_size; + s->temp.pos += copy_size; + + if (s->temp.pos == s->temp.size) { + s->temp.pos = 0; + return true; + } + + return false; +} + +/* Decode a variable-length integer (little-endian base-128 encoding) */ +static enum xz_ret dec_vli(struct xz_dec *s, const uint8_t *in, + size_t *in_pos, size_t in_size) +{ + uint8_t byte; + + if (s->pos == 0) + s->vli = 0; + + while (*in_pos < in_size) { + byte = in[*in_pos]; + ++*in_pos; + + s->vli |= (vli_type)(byte & 0x7F) << s->pos; + + if ((byte & 0x80) == 0) { + /* Don't allow non-minimal encodings. */ + if (byte == 0 && s->pos != 0) + return XZ_DATA_ERROR; + + s->pos = 0; + return XZ_STREAM_END; + } + + s->pos += 7; + if (s->pos == 7 * VLI_BYTES_MAX) + return XZ_DATA_ERROR; + } + + return XZ_OK; +} + +/* + * Decode the Compressed Data field from a Block. Update and validate + * the observed compressed and uncompressed sizes of the Block so that + * they don't exceed the values possibly stored in the Block Header + * (validation assumes that no integer overflow occurs, since vli_type + * is normally uint64_t). Update the CRC32 if presence of the CRC32 + * field was indicated in Stream Header. + * + * Once the decoding is finished, validate that the observed sizes match + * the sizes possibly stored in the Block Header. Update the hash and + * Block count, which are later used to validate the Index field. + */ +static enum xz_ret dec_block(struct xz_dec *s, struct xz_buf *b) +{ + enum xz_ret ret; + + s->in_start = b->in_pos; + s->out_start = b->out_pos; + +#ifdef XZ_DEC_BCJ + if (s->bcj_active) + ret = xz_dec_bcj_run(s->bcj, s->lzma2, b); + else +#endif + ret = xz_dec_lzma2_run(s->lzma2, b); + + s->block.compressed += b->in_pos - s->in_start; + s->block.uncompressed += b->out_pos - s->out_start; + + /* + * There is no need to separately check for VLI_UNKNOWN, since + * the observed sizes are always smaller than VLI_UNKNOWN. + */ + if (s->block.compressed > s->block_header.compressed + || s->block.uncompressed + > s->block_header.uncompressed) + return XZ_DATA_ERROR; + + if (s->check_type == XZ_CHECK_CRC32) + s->crc32 = xz_crc32(b->out + s->out_start, + b->out_pos - s->out_start, s->crc32); + + if (ret == XZ_STREAM_END) { + if (s->block_header.compressed != VLI_UNKNOWN + && s->block_header.compressed + != s->block.compressed) + return XZ_DATA_ERROR; + + if (s->block_header.uncompressed != VLI_UNKNOWN + && s->block_header.uncompressed + != s->block.uncompressed) + return XZ_DATA_ERROR; + + s->block.hash.unpadded += s->block_header.size + + s->block.compressed; + +#ifdef XZ_DEC_ANY_CHECK + s->block.hash.unpadded += check_sizes[s->check_type]; +#else + if (s->check_type == XZ_CHECK_CRC32) + s->block.hash.unpadded += 4; +#endif + + s->block.hash.uncompressed += s->block.uncompressed; + s->block.hash.crc32 = xz_crc32( + (const uint8_t *)&s->block.hash, + sizeof(s->block.hash), s->block.hash.crc32); + + ++s->block.count; + } + + return ret; +} + +/* Update the Index size and the CRC32 value. */ +static void index_update(struct xz_dec *s, const struct xz_buf *b) +{ + size_t in_used = b->in_pos - s->in_start; + s->index.size += in_used; + s->crc32 = xz_crc32(b->in + s->in_start, in_used, s->crc32); +} + +/* + * Decode the Number of Records, Unpadded Size, and Uncompressed Size + * fields from the Index field. That is, Index Padding and CRC32 are not + * decoded by this function. + * + * This can return XZ_OK (more input needed), XZ_STREAM_END (everything + * successfully decoded), or XZ_DATA_ERROR (input is corrupt). + */ +static enum xz_ret dec_index(struct xz_dec *s, struct xz_buf *b) +{ + enum xz_ret ret; + + do { + ret = dec_vli(s, b->in, &b->in_pos, b->in_size); + if (ret != XZ_STREAM_END) { + index_update(s, b); + return ret; + } + + switch (s->index.sequence) { + case SEQ_INDEX_COUNT: + s->index.count = s->vli; + + /* + * Validate that the Number of Records field + * indicates the same number of Records as + * there were Blocks in the Stream. + */ + if (s->index.count != s->block.count) + return XZ_DATA_ERROR; + + s->index.sequence = SEQ_INDEX_UNPADDED; + break; + + case SEQ_INDEX_UNPADDED: + s->index.hash.unpadded += s->vli; + s->index.sequence = SEQ_INDEX_UNCOMPRESSED; + break; + + case SEQ_INDEX_UNCOMPRESSED: + s->index.hash.uncompressed += s->vli; + s->index.hash.crc32 = xz_crc32( + (const uint8_t *)&s->index.hash, + sizeof(s->index.hash), + s->index.hash.crc32); + --s->index.count; + s->index.sequence = SEQ_INDEX_UNPADDED; + break; + } + } while (s->index.count > 0); + + return XZ_STREAM_END; +} + +/* + * Validate that the next four input bytes match the value of s->crc32. + * s->pos must be zero when starting to validate the first byte. + */ +static enum xz_ret crc32_validate(struct xz_dec *s, struct xz_buf *b) +{ + do { + if (b->in_pos == b->in_size) + return XZ_OK; + + if (((s->crc32 >> s->pos) & 0xFF) != b->in[b->in_pos++]) + return XZ_DATA_ERROR; + + s->pos += 8; + + } while (s->pos < 32); + + s->crc32 = 0; + s->pos = 0; + + return XZ_STREAM_END; +} + +#ifdef XZ_DEC_ANY_CHECK +/* + * Skip over the Check field when the Check ID is not supported. + * Returns true once the whole Check field has been skipped over. + */ +static bool check_skip(struct xz_dec *s, struct xz_buf *b) +{ + while (s->pos < check_sizes[s->check_type]) { + if (b->in_pos == b->in_size) + return false; + + ++b->in_pos; + ++s->pos; + } + + s->pos = 0; + + return true; +} +#endif + +/* Decode the Stream Header field (the first 12 bytes of the .xz Stream). */ +static enum xz_ret dec_stream_header(struct xz_dec *s) +{ + if (!memeq(s->temp.buf, HEADER_MAGIC, HEADER_MAGIC_SIZE)) + return XZ_FORMAT_ERROR; + + if (xz_crc32(s->temp.buf + HEADER_MAGIC_SIZE, 2, 0) + != get_le32(s->temp.buf + HEADER_MAGIC_SIZE + 2)) + return XZ_DATA_ERROR; + + if (s->temp.buf[HEADER_MAGIC_SIZE] != 0) + return XZ_OPTIONS_ERROR; + + /* + * Of integrity checks, we support only none (Check ID = 0) and + * CRC32 (Check ID = 1). However, if XZ_DEC_ANY_CHECK is defined, + * we will accept other check types too, but then the check won't + * be verified and a warning (XZ_UNSUPPORTED_CHECK) will be given. + */ + s->check_type = s->temp.buf[HEADER_MAGIC_SIZE + 1]; + +#ifdef XZ_DEC_ANY_CHECK + if (s->check_type > XZ_CHECK_MAX) + return XZ_OPTIONS_ERROR; + + if (s->check_type > XZ_CHECK_CRC32) + return XZ_UNSUPPORTED_CHECK; +#else + if (s->check_type > XZ_CHECK_CRC32) + return XZ_OPTIONS_ERROR; +#endif + + return XZ_OK; +} + +/* Decode the Stream Footer field (the last 12 bytes of the .xz Stream) */ +static enum xz_ret dec_stream_footer(struct xz_dec *s) +{ + if (!memeq(s->temp.buf + 10, FOOTER_MAGIC, FOOTER_MAGIC_SIZE)) + return XZ_DATA_ERROR; + + if (xz_crc32(s->temp.buf + 4, 6, 0) != get_le32(s->temp.buf)) + return XZ_DATA_ERROR; + + /* + * Validate Backward Size. Note that we never added the size of the + * Index CRC32 field to s->index.size, thus we use s->index.size / 4 + * instead of s->index.size / 4 - 1. + */ + if ((s->index.size >> 2) != get_le32(s->temp.buf + 4)) + return XZ_DATA_ERROR; + + if (s->temp.buf[8] != 0 || s->temp.buf[9] != s->check_type) + return XZ_DATA_ERROR; + + /* + * Use XZ_STREAM_END instead of XZ_OK to be more convenient + * for the caller. + */ + return XZ_STREAM_END; +} + +/* Decode the Block Header and initialize the filter chain. */ +static enum xz_ret dec_block_header(struct xz_dec *s) +{ + enum xz_ret ret; + + /* + * Validate the CRC32. We know that the temp buffer is at least + * eight bytes so this is safe. + */ + s->temp.size -= 4; + if (xz_crc32(s->temp.buf, s->temp.size, 0) + != get_le32(s->temp.buf + s->temp.size)) + return XZ_DATA_ERROR; + + s->temp.pos = 2; + + /* + * Catch unsupported Block Flags. We support only one or two filters + * in the chain, so we catch that with the same test. + */ +#ifdef XZ_DEC_BCJ + if (s->temp.buf[1] & 0x3E) +#else + if (s->temp.buf[1] & 0x3F) +#endif + return XZ_OPTIONS_ERROR; + + /* Compressed Size */ + if (s->temp.buf[1] & 0x40) { + if (dec_vli(s, s->temp.buf, &s->temp.pos, s->temp.size) + != XZ_STREAM_END) + return XZ_DATA_ERROR; + + s->block_header.compressed = s->vli; + } else { + s->block_header.compressed = VLI_UNKNOWN; + } + + /* Uncompressed Size */ + if (s->temp.buf[1] & 0x80) { + if (dec_vli(s, s->temp.buf, &s->temp.pos, s->temp.size) + != XZ_STREAM_END) + return XZ_DATA_ERROR; + + s->block_header.uncompressed = s->vli; + } else { + s->block_header.uncompressed = VLI_UNKNOWN; + } + +#ifdef XZ_DEC_BCJ + /* If there are two filters, the first one must be a BCJ filter. */ + s->bcj_active = s->temp.buf[1] & 0x01; + if (s->bcj_active) { + if (s->temp.size - s->temp.pos < 2) + return XZ_OPTIONS_ERROR; + + ret = xz_dec_bcj_reset(s->bcj, s->temp.buf[s->temp.pos++]); + if (ret != XZ_OK) + return ret; + + /* + * We don't support custom start offset, + * so Size of Properties must be zero. + */ + if (s->temp.buf[s->temp.pos++] != 0x00) + return XZ_OPTIONS_ERROR; + } +#endif + + /* Valid Filter Flags always take at least two bytes. */ + if (s->temp.size - s->temp.pos < 2) + return XZ_DATA_ERROR; + + /* Filter ID = LZMA2 */ + if (s->temp.buf[s->temp.pos++] != 0x21) + return XZ_OPTIONS_ERROR; + + /* Size of Properties = 1-byte Filter Properties */ + if (s->temp.buf[s->temp.pos++] != 0x01) + return XZ_OPTIONS_ERROR; + + /* Filter Properties contains LZMA2 dictionary size. */ + if (s->temp.size - s->temp.pos < 1) + return XZ_DATA_ERROR; + + ret = xz_dec_lzma2_reset(s->lzma2, s->temp.buf[s->temp.pos++]); + if (ret != XZ_OK) + return ret; + + /* The rest must be Header Padding. */ + while (s->temp.pos < s->temp.size) + if (s->temp.buf[s->temp.pos++] != 0x00) + return XZ_OPTIONS_ERROR; + + s->temp.pos = 0; + s->block.compressed = 0; + s->block.uncompressed = 0; + + return XZ_OK; +} + +static enum xz_ret dec_main(struct xz_dec *s, struct xz_buf *b) +{ + enum xz_ret ret; + + /* + * Store the start position for the case when we are in the middle + * of the Index field. + */ + s->in_start = b->in_pos; + + while (true) { + switch (s->sequence) { + case SEQ_STREAM_HEADER: + /* + * Stream Header is copied to s->temp, and then + * decoded from there. This way if the caller + * gives us only little input at a time, we can + * still keep the Stream Header decoding code + * simple. Similar approach is used in many places + * in this file. + */ + if (!fill_temp(s, b)) + return XZ_OK; + + /* + * If dec_stream_header() returns + * XZ_UNSUPPORTED_CHECK, it is still possible + * to continue decoding if working in multi-call + * mode. Thus, update s->sequence before calling + * dec_stream_header(). + */ + s->sequence = SEQ_BLOCK_START; + + ret = dec_stream_header(s); + if (ret != XZ_OK) + return ret; + + case SEQ_BLOCK_START: + /* We need one byte of input to continue. */ + if (b->in_pos == b->in_size) + return XZ_OK; + + /* See if this is the beginning of the Index field. */ + if (b->in[b->in_pos] == 0) { + s->in_start = b->in_pos++; + s->sequence = SEQ_INDEX; + break; + } + + /* + * Calculate the size of the Block Header and + * prepare to decode it. + */ + s->block_header.size + = ((uint32_t)b->in[b->in_pos] + 1) * 4; + + s->temp.size = s->block_header.size; + s->temp.pos = 0; + s->sequence = SEQ_BLOCK_HEADER; + + case SEQ_BLOCK_HEADER: + if (!fill_temp(s, b)) + return XZ_OK; + + ret = dec_block_header(s); + if (ret != XZ_OK) + return ret; + + s->sequence = SEQ_BLOCK_UNCOMPRESS; + + case SEQ_BLOCK_UNCOMPRESS: + ret = dec_block(s, b); + if (ret != XZ_STREAM_END) + return ret; + + s->sequence = SEQ_BLOCK_PADDING; + + case SEQ_BLOCK_PADDING: + /* + * Size of Compressed Data + Block Padding + * must be a multiple of four. We don't need + * s->block.compressed for anything else + * anymore, so we use it here to test the size + * of the Block Padding field. + */ + while (s->block.compressed & 3) { + if (b->in_pos == b->in_size) + return XZ_OK; + + if (b->in[b->in_pos++] != 0) + return XZ_DATA_ERROR; + + ++s->block.compressed; + } + + s->sequence = SEQ_BLOCK_CHECK; + + case SEQ_BLOCK_CHECK: + if (s->check_type == XZ_CHECK_CRC32) { + ret = crc32_validate(s, b); + if (ret != XZ_STREAM_END) + return ret; + } +#ifdef XZ_DEC_ANY_CHECK + else if (!check_skip(s, b)) { + return XZ_OK; + } +#endif + + s->sequence = SEQ_BLOCK_START; + break; + + case SEQ_INDEX: + ret = dec_index(s, b); + if (ret != XZ_STREAM_END) + return ret; + + s->sequence = SEQ_INDEX_PADDING; + + case SEQ_INDEX_PADDING: + while ((s->index.size + (b->in_pos - s->in_start)) + & 3) { + if (b->in_pos == b->in_size) { + index_update(s, b); + return XZ_OK; + } + + if (b->in[b->in_pos++] != 0) + return XZ_DATA_ERROR; + } + + /* Finish the CRC32 value and Index size. */ + index_update(s, b); + + /* Compare the hashes to validate the Index field. */ + if (!memeq(&s->block.hash, &s->index.hash, + sizeof(s->block.hash))) + return XZ_DATA_ERROR; + + s->sequence = SEQ_INDEX_CRC32; + + case SEQ_INDEX_CRC32: + ret = crc32_validate(s, b); + if (ret != XZ_STREAM_END) + return ret; + + s->temp.size = STREAM_HEADER_SIZE; + s->sequence = SEQ_STREAM_FOOTER; + + case SEQ_STREAM_FOOTER: + if (!fill_temp(s, b)) + return XZ_OK; + + return dec_stream_footer(s); + } + } + + /* Never reached */ +} + +/* + * xz_dec_run() is a wrapper for dec_main() to handle some special cases in + * multi-call and single-call decoding. + * + * In multi-call mode, we must return XZ_BUF_ERROR when it seems clear that we + * are not going to make any progress anymore. This is to prevent the caller + * from calling us infinitely when the input file is truncated or otherwise + * corrupt. Since zlib-style API allows that the caller fills the input buffer + * only when the decoder doesn't produce any new output, we have to be careful + * to avoid returning XZ_BUF_ERROR too easily: XZ_BUF_ERROR is returned only + * after the second consecutive call to xz_dec_run() that makes no progress. + * + * In single-call mode, if we couldn't decode everything and no error + * occurred, either the input is truncated or the output buffer is too small. + * Since we know that the last input byte never produces any output, we know + * that if all the input was consumed and decoding wasn't finished, the file + * must be corrupt. Otherwise the output buffer has to be too small or the + * file is corrupt in a way that decoding it produces too big output. + * + * If single-call decoding fails, we reset b->in_pos and b->out_pos back to + * their original values. This is because with some filter chains there won't + * be any valid uncompressed data in the output buffer unless the decoding + * actually succeeds (that's the price to pay of using the output buffer as + * the workspace). + */ +XZ_EXTERN enum xz_ret xz_dec_run(struct xz_dec *s, struct xz_buf *b) +{ + size_t in_start; + size_t out_start; + enum xz_ret ret; + + if (DEC_IS_SINGLE(s->mode)) + xz_dec_reset(s); + + in_start = b->in_pos; + out_start = b->out_pos; + ret = dec_main(s, b); + + if (DEC_IS_SINGLE(s->mode)) { + if (ret == XZ_OK) + ret = b->in_pos == b->in_size + ? XZ_DATA_ERROR : XZ_BUF_ERROR; + + if (ret != XZ_STREAM_END) { + b->in_pos = in_start; + b->out_pos = out_start; + } + + } else if (ret == XZ_OK && in_start == b->in_pos + && out_start == b->out_pos) { + if (s->allow_buf_error) + ret = XZ_BUF_ERROR; + + s->allow_buf_error = true; + } else { + s->allow_buf_error = false; + } + + return ret; +} + +XZ_EXTERN struct xz_dec *xz_dec_init(enum xz_mode mode, uint32_t dict_max) +{ + struct xz_dec *s = kmalloc(sizeof(*s), GFP_KERNEL); + if (s == NULL) + return NULL; + + s->mode = mode; + +#ifdef XZ_DEC_BCJ + s->bcj = xz_dec_bcj_create(DEC_IS_SINGLE(mode)); + if (s->bcj == NULL) + goto error_bcj; +#endif + + s->lzma2 = xz_dec_lzma2_create(mode, dict_max); + if (s->lzma2 == NULL) + goto error_lzma2; + + xz_dec_reset(s); + return s; + +error_lzma2: +#ifdef XZ_DEC_BCJ + xz_dec_bcj_end(s->bcj); +error_bcj: +#endif + kfree(s); + return NULL; +} + +XZ_EXTERN void xz_dec_reset(struct xz_dec *s) +{ + s->sequence = SEQ_STREAM_HEADER; + s->allow_buf_error = false; + s->pos = 0; + s->crc32 = 0; + memzero(&s->block, sizeof(s->block)); + memzero(&s->index, sizeof(s->index)); + s->temp.pos = 0; + s->temp.size = STREAM_HEADER_SIZE; +} + +XZ_EXTERN void xz_dec_end(struct xz_dec *s) +{ + if (s != NULL) { + xz_dec_lzma2_end(s->lzma2); +#ifdef XZ_DEC_BCJ + xz_dec_bcj_end(s->bcj); +#endif + kfree(s); + } +} diff --git a/platform/mcu/xr871/src/xz/xz_lzma2.h b/platform/mcu/xr871/src/xz/xz_lzma2.h new file mode 100644 index 0000000000..071d67bee9 --- /dev/null +++ b/platform/mcu/xr871/src/xz/xz_lzma2.h @@ -0,0 +1,204 @@ +/* + * LZMA2 definitions + * + * Authors: Lasse Collin + * Igor Pavlov + * + * This file has been put into the public domain. + * You can do whatever you want with this file. + */ + +#ifndef XZ_LZMA2_H +#define XZ_LZMA2_H + +/* Range coder constants */ +#define RC_SHIFT_BITS 8 +#define RC_TOP_BITS 24 +#define RC_TOP_VALUE (1 << RC_TOP_BITS) +#define RC_BIT_MODEL_TOTAL_BITS 11 +#define RC_BIT_MODEL_TOTAL (1 << RC_BIT_MODEL_TOTAL_BITS) +#define RC_MOVE_BITS 5 + +/* + * Maximum number of position states. A position state is the lowest pb + * number of bits of the current uncompressed offset. In some places there + * are different sets of probabilities for different position states. + */ +#define POS_STATES_MAX (1 << 4) + +/* + * This enum is used to track which LZMA symbols have occurred most recently + * and in which order. This information is used to predict the next symbol. + * + * Symbols: + * - Literal: One 8-bit byte + * - Match: Repeat a chunk of data at some distance + * - Long repeat: Multi-byte match at a recently seen distance + * - Short repeat: One-byte repeat at a recently seen distance + * + * The symbol names are in from STATE_oldest_older_previous. REP means + * either short or long repeated match, and NONLIT means any non-literal. + */ +enum lzma_state { + STATE_LIT_LIT, + STATE_MATCH_LIT_LIT, + STATE_REP_LIT_LIT, + STATE_SHORTREP_LIT_LIT, + STATE_MATCH_LIT, + STATE_REP_LIT, + STATE_SHORTREP_LIT, + STATE_LIT_MATCH, + STATE_LIT_LONGREP, + STATE_LIT_SHORTREP, + STATE_NONLIT_MATCH, + STATE_NONLIT_REP +}; + +/* Total number of states */ +#define STATES 12 + +/* The lowest 7 states indicate that the previous state was a literal. */ +#define LIT_STATES 7 + +/* Indicate that the latest symbol was a literal. */ +static inline void lzma_state_literal(enum lzma_state *state) +{ + if (*state <= STATE_SHORTREP_LIT_LIT) + *state = STATE_LIT_LIT; + else if (*state <= STATE_LIT_SHORTREP) + *state -= 3; + else + *state -= 6; +} + +/* Indicate that the latest symbol was a match. */ +static inline void lzma_state_match(enum lzma_state *state) +{ + *state = *state < LIT_STATES ? STATE_LIT_MATCH : STATE_NONLIT_MATCH; +} + +/* Indicate that the latest state was a long repeated match. */ +static inline void lzma_state_long_rep(enum lzma_state *state) +{ + *state = *state < LIT_STATES ? STATE_LIT_LONGREP : STATE_NONLIT_REP; +} + +/* Indicate that the latest symbol was a short match. */ +static inline void lzma_state_short_rep(enum lzma_state *state) +{ + *state = *state < LIT_STATES ? STATE_LIT_SHORTREP : STATE_NONLIT_REP; +} + +/* Test if the previous symbol was a literal. */ +static inline bool lzma_state_is_literal(enum lzma_state state) +{ + return state < LIT_STATES; +} + +/* Each literal coder is divided in three sections: + * - 0x001-0x0FF: Without match byte + * - 0x101-0x1FF: With match byte; match bit is 0 + * - 0x201-0x2FF: With match byte; match bit is 1 + * + * Match byte is used when the previous LZMA symbol was something else than + * a literal (that is, it was some kind of match). + */ +#define LITERAL_CODER_SIZE 0x300 + +/* Maximum number of literal coders */ +#define LITERAL_CODERS_MAX (1 << 4) + +/* Minimum length of a match is two bytes. */ +#define MATCH_LEN_MIN 2 + +/* Match length is encoded with 4, 5, or 10 bits. + * + * Length Bits + * 2-9 4 = Choice=0 + 3 bits + * 10-17 5 = Choice=1 + Choice2=0 + 3 bits + * 18-273 10 = Choice=1 + Choice2=1 + 8 bits + */ +#define LEN_LOW_BITS 3 +#define LEN_LOW_SYMBOLS (1 << LEN_LOW_BITS) +#define LEN_MID_BITS 3 +#define LEN_MID_SYMBOLS (1 << LEN_MID_BITS) +#define LEN_HIGH_BITS 8 +#define LEN_HIGH_SYMBOLS (1 << LEN_HIGH_BITS) +#define LEN_SYMBOLS (LEN_LOW_SYMBOLS + LEN_MID_SYMBOLS + LEN_HIGH_SYMBOLS) + +/* + * Maximum length of a match is 273 which is a result of the encoding + * described above. + */ +#define MATCH_LEN_MAX (MATCH_LEN_MIN + LEN_SYMBOLS - 1) + +/* + * Different sets of probabilities are used for match distances that have + * very short match length: Lengths of 2, 3, and 4 bytes have a separate + * set of probabilities for each length. The matches with longer length + * use a shared set of probabilities. + */ +#define DIST_STATES 4 + +/* + * Get the index of the appropriate probability array for decoding + * the distance slot. + */ +static inline uint32_t lzma_get_dist_state(uint32_t len) +{ + return len < DIST_STATES + MATCH_LEN_MIN + ? len - MATCH_LEN_MIN : DIST_STATES - 1; +} + +/* + * The highest two bits of a 32-bit match distance are encoded using six bits. + * This six-bit value is called a distance slot. This way encoding a 32-bit + * value takes 6-36 bits, larger values taking more bits. + */ +#define DIST_SLOT_BITS 6 +#define DIST_SLOTS (1 << DIST_SLOT_BITS) + +/* Match distances up to 127 are fully encoded using probabilities. Since + * the highest two bits (distance slot) are always encoded using six bits, + * the distances 0-3 don't need any additional bits to encode, since the + * distance slot itself is the same as the actual distance. DIST_MODEL_START + * indicates the first distance slot where at least one additional bit is + * needed. + */ +#define DIST_MODEL_START 4 + +/* + * Match distances greater than 127 are encoded in three pieces: + * - distance slot: the highest two bits + * - direct bits: 2-26 bits below the highest two bits + * - alignment bits: four lowest bits + * + * Direct bits don't use any probabilities. + * + * The distance slot value of 14 is for distances 128-191. + */ +#define DIST_MODEL_END 14 + +/* Distance slots that indicate a distance <= 127. */ +#define FULL_DISTANCES_BITS (DIST_MODEL_END / 2) +#define FULL_DISTANCES (1 << FULL_DISTANCES_BITS) + +/* + * For match distances greater than 127, only the highest two bits and the + * lowest four bits (alignment) is encoded using probabilities. + */ +#define ALIGN_BITS 4 +#define ALIGN_SIZE (1 << ALIGN_BITS) +#define ALIGN_MASK (ALIGN_SIZE - 1) + +/* Total number of all probability variables */ +#define PROBS_TOTAL (1846 + LITERAL_CODERS_MAX * LITERAL_CODER_SIZE) + +/* + * LZMA remembers the four most recent match distances. Reusing these + * distances tends to take less space than re-encoding the actual + * distance value. + */ +#define REPS 4 + +#endif diff --git a/platform/mcu/xr871/src/xz/xz_private.h b/platform/mcu/xr871/src/xz/xz_private.h new file mode 100644 index 0000000000..482b90f363 --- /dev/null +++ b/platform/mcu/xr871/src/xz/xz_private.h @@ -0,0 +1,156 @@ +/* + * Private includes and definitions + * + * Author: Lasse Collin + * + * This file has been put into the public domain. + * You can do whatever you want with this file. + */ + +#ifndef XZ_PRIVATE_H +#define XZ_PRIVATE_H + +#ifdef __KERNEL__ +# include +# include +# include + /* XZ_PREBOOT may be defined only via decompress_unxz.c. */ +# ifndef XZ_PREBOOT +# include +# include +# include +# ifdef CONFIG_XZ_DEC_X86 +# define XZ_DEC_X86 +# endif +# ifdef CONFIG_XZ_DEC_POWERPC +# define XZ_DEC_POWERPC +# endif +# ifdef CONFIG_XZ_DEC_IA64 +# define XZ_DEC_IA64 +# endif +# ifdef CONFIG_XZ_DEC_ARM +# define XZ_DEC_ARM +# endif +# ifdef CONFIG_XZ_DEC_ARMTHUMB +# define XZ_DEC_ARMTHUMB +# endif +# ifdef CONFIG_XZ_DEC_SPARC +# define XZ_DEC_SPARC +# endif +# define memeq(a, b, size) (memcmp(a, b, size) == 0) +# define memzero(buf, size) memset(buf, 0, size) +# endif +# define get_le32(p) le32_to_cpup((const uint32_t *)(p)) +#else + /* + * For userspace builds, use a separate header to define the required + * macros and functions. This makes it easier to adapt the code into + * different environments and avoids clutter in the Linux kernel tree. + */ +# include "xz_config.h" +#endif + +/* If no specific decoding mode is requested, enable support for all modes. */ +#if !defined(XZ_DEC_SINGLE) && !defined(XZ_DEC_PREALLOC) \ + && !defined(XZ_DEC_DYNALLOC) +# define XZ_DEC_SINGLE +# define XZ_DEC_PREALLOC +# define XZ_DEC_DYNALLOC +#endif + +/* + * The DEC_IS_foo(mode) macros are used in "if" statements. If only some + * of the supported modes are enabled, these macros will evaluate to true or + * false at compile time and thus allow the compiler to omit unneeded code. + */ +#ifdef XZ_DEC_SINGLE +# define DEC_IS_SINGLE(mode) ((mode) == XZ_SINGLE) +#else +# define DEC_IS_SINGLE(mode) (false) +#endif + +#ifdef XZ_DEC_PREALLOC +# define DEC_IS_PREALLOC(mode) ((mode) == XZ_PREALLOC) +#else +# define DEC_IS_PREALLOC(mode) (false) +#endif + +#ifdef XZ_DEC_DYNALLOC +# define DEC_IS_DYNALLOC(mode) ((mode) == XZ_DYNALLOC) +#else +# define DEC_IS_DYNALLOC(mode) (false) +#endif + +#if !defined(XZ_DEC_SINGLE) +# define DEC_IS_MULTI(mode) (true) +#elif defined(XZ_DEC_PREALLOC) || defined(XZ_DEC_DYNALLOC) +# define DEC_IS_MULTI(mode) ((mode) != XZ_SINGLE) +#else +# define DEC_IS_MULTI(mode) (false) +#endif + +/* + * If any of the BCJ filter decoders are wanted, define XZ_DEC_BCJ. + * XZ_DEC_BCJ is used to enable generic support for BCJ decoders. + */ +#ifndef XZ_DEC_BCJ +# if defined(XZ_DEC_X86) || defined(XZ_DEC_POWERPC) \ + || defined(XZ_DEC_IA64) || defined(XZ_DEC_ARM) \ + || defined(XZ_DEC_ARM) || defined(XZ_DEC_ARMTHUMB) \ + || defined(XZ_DEC_SPARC) +# define XZ_DEC_BCJ +# endif +#endif + +/* + * Allocate memory for LZMA2 decoder. xz_dec_lzma2_reset() must be used + * before calling xz_dec_lzma2_run(). + */ +XZ_EXTERN struct xz_dec_lzma2 *xz_dec_lzma2_create(enum xz_mode mode, + uint32_t dict_max); + +/* + * Decode the LZMA2 properties (one byte) and reset the decoder. Return + * XZ_OK on success, XZ_MEMLIMIT_ERROR if the preallocated dictionary is not + * big enough, and XZ_OPTIONS_ERROR if props indicates something that this + * decoder doesn't support. + */ +XZ_EXTERN enum xz_ret xz_dec_lzma2_reset(struct xz_dec_lzma2 *s, + uint8_t props); + +/* Decode raw LZMA2 stream from b->in to b->out. */ +XZ_EXTERN enum xz_ret xz_dec_lzma2_run(struct xz_dec_lzma2 *s, + struct xz_buf *b); + +/* Free the memory allocated for the LZMA2 decoder. */ +XZ_EXTERN void xz_dec_lzma2_end(struct xz_dec_lzma2 *s); + +#ifdef XZ_DEC_BCJ +/* + * Allocate memory for BCJ decoders. xz_dec_bcj_reset() must be used before + * calling xz_dec_bcj_run(). + */ +XZ_EXTERN struct xz_dec_bcj *xz_dec_bcj_create(bool single_call); + +/* + * Decode the Filter ID of a BCJ filter. This implementation doesn't + * support custom start offsets, so no decoding of Filter Properties + * is needed. Returns XZ_OK if the given Filter ID is supported. + * Otherwise XZ_OPTIONS_ERROR is returned. + */ +XZ_EXTERN enum xz_ret xz_dec_bcj_reset(struct xz_dec_bcj *s, uint8_t id); + +/* + * Decode raw BCJ + LZMA2 stream. This must be used only if there actually is + * a BCJ filter in the chain. If the chain has only LZMA2, xz_dec_lzma2_run() + * must be called directly. + */ +XZ_EXTERN enum xz_ret xz_dec_bcj_run(struct xz_dec_bcj *s, + struct xz_dec_lzma2 *lzma2, + struct xz_buf *b); + +/* Free the memory allocated for the BCJ filters. */ +#define xz_dec_bcj_end(s) kfree(s) +#endif + +#endif diff --git a/platform/mcu/xr871/src/xz/xz_stream.h b/platform/mcu/xr871/src/xz/xz_stream.h new file mode 100644 index 0000000000..66cb5a7055 --- /dev/null +++ b/platform/mcu/xr871/src/xz/xz_stream.h @@ -0,0 +1,62 @@ +/* + * Definitions for handling the .xz file format + * + * Author: Lasse Collin + * + * This file has been put into the public domain. + * You can do whatever you want with this file. + */ + +#ifndef XZ_STREAM_H +#define XZ_STREAM_H + +#if defined(__KERNEL__) && !XZ_INTERNAL_CRC32 +# include +# undef crc32 +# define xz_crc32(buf, size, crc) \ + (~crc32_le(~(uint32_t)(crc), buf, size)) +#endif + +/* + * See the .xz file format specification at + * http://tukaani.org/xz/xz-file-format.txt + * to understand the container format. + */ + +#define STREAM_HEADER_SIZE 12 + +#define HEADER_MAGIC "\3757zXZ" +#define HEADER_MAGIC_SIZE 6 + +#define FOOTER_MAGIC "YZ" +#define FOOTER_MAGIC_SIZE 2 + +/* + * Variable-length integer can hold a 63-bit unsigned integer or a special + * value indicating that the value is unknown. + * + * Experimental: vli_type can be defined to uint32_t to save a few bytes + * in code size (no effect on speed). Doing so limits the uncompressed and + * compressed size of the file to less than 256 MiB and may also weaken + * error detection slightly. + */ +typedef uint64_t vli_type; + +#define VLI_MAX ((vli_type)-1 / 2) +#define VLI_UNKNOWN ((vli_type)-1) + +/* Maximum encoded size of a VLI */ +#define VLI_BYTES_MAX (sizeof(vli_type) * 8 / 7) + +/* Integrity Check types */ +enum xz_check { + XZ_CHECK_NONE = 0, + XZ_CHECK_CRC32 = 1, + XZ_CHECK_CRC64 = 4, + XZ_CHECK_SHA256 = 10 +}; + +/* Maximum possible Check ID */ +#define XZ_CHECK_MAX 15 + +#endif diff --git a/platform/mcu/xr871/tools/calc_flash_offs b/platform/mcu/xr871/tools/calc_flash_offs new file mode 100644 index 0000000000..8fccf4a4c5 --- /dev/null +++ b/platform/mcu/xr871/tools/calc_flash_offs @@ -0,0 +1,43 @@ +#!/bin/bash + +XZ="" +if [ "$1" == "-z" ];then +XZ=".xz" +fi + +BOOT=boot.bin +APP=app.bin +XIP=app-xip.bin +NET=net.bin${XZ} +NETAP=net_ap.bin${XZ} +WLANBL=wlan_bl.bin +WLANFW=wlan_fw.bin +WLANSDD=wlan_sdd.bin + +BOOT_SZ=`ls -l ${BOOT} | awk '{print $5}'` +APP_SZ=`ls -l ${APP} | awk '{print $5}'` +XIP_SZ=`ls -l ${XIP} | awk '{print $5}'` +NET_SZ=`ls -l ${NET} | awk '{print $5}'` +NETAP_SZ=`ls -l ${NETAP} | awk '{print $5}'` +WLANBL_SZ=`ls -l ${WLANBL} | awk '{print $5}'` +WLANFW_SZ=`ls -l ${WLANFW} | awk '{print $5}'` +WLANSDD_SZ=`ls -l ${WLANSDD} | awk '{print $5}'` + +BOOT_OFFS=0 +#APP_OFFS=$[BOOT_OFFS + BOOT_SZ + 64 + $[1024 - $[$[BOOT_SZ + 64] & 1023]]] +APP_OFFS=32768 +XIP_OFFS=$[APP_OFFS + APP_SZ + 64 + $[1024 - $[$[APP_SZ + 64] & 1023]]] +NET_OFFS=$[XIP_OFFS + XIP_SZ + 64 + $[1024 - $[$[XIP_SZ + 64] & 1023]]] +NETAP_OFFS=$[NET_OFFS + NET_SZ + 64 + $[1024 - $[$[NET_SZ + 64] & 1023]]] +WLANBL_OFFS=$[NETAP_OFFS + NETAP_SZ + 64 + $[1024 - $[$[NETAP_SZ + 64] & 1023]]] +WLANFW_OFFS=$[WLANBL_OFFS + WLANBL_SZ + 64 + $[1024 - $[$[WLANBL_SZ + 64] & 1023]]] +WLANSDD_OFFS=$[WLANFW_OFFS + WLANFW_SZ + 64 + $[1024 - $[$[WLANFW_SZ + 64] & 1023]]] + +printf "%-16s sz: %-8d offs: %-dK\n" $BOOT $BOOT_SZ $[BOOT_OFFS/1024] +printf "%-16s sz: %-8d offs: %-dK\n" $APP $APP_SZ $[APP_OFFS/1024] +printf "%-16s sz: %-8d offs: %-dK\n" $XIP $XIP_SZ $[XIP_OFFS/1024] +printf "%-16s sz: %-8d offs: %-dK\n" $NET $NET_SZ $[NET_OFFS/1024] +printf "%-16s sz: %-8d offs: %-dK\n" $NETAP $NETAP_SZ $[NETAP_OFFS/1024] +printf "%-16s sz: %-8d offs: %-dK\n" $WLANBL $WLANBL_SZ $[WLANBL_OFFS/1024] +printf "%-16s sz: %-8d offs: %-dK\n" $WLANFW $WLANFW_SZ $[WLANFW_OFFS/1024] +printf "%-16s sz: %-8d offs: %-dK\n" $WLANSDD $WLANSDD_SZ $[WLANSDD_OFFS/1024] diff --git a/platform/mcu/xr871/tools/mkimage b/platform/mcu/xr871/tools/mkimage new file mode 100644 index 0000000000000000000000000000000000000000..e9464de8a014331dc6f5e689e3d68c10a0c6113e GIT binary patch literal 45880 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
hPqVJfv$^FOMpqqrfJ+ejb z(7ho{E8vvN^c~3v=(WC0?a97mto{t>;@uGi1ubLdKK<2^Z|mPAXYEdU&zJLtZ_~$U zk%bmV`t%fLy-x&nvUhjV4wYmUl2A(JmZY;6gxq)0z;iPOfRWzak!z*IET`_>C1jc# z#XY`E`g_S)9WioH(~kJOcM0BqH@v%&IPIM6?YO}W3D6BU^s1-nPsGJ0M+a1|hvORk zdL2%yp}Vgetc~1kxNgNa45Dke z8X0#kx0{7e-8CJbzMyL`zUZ%MEswwCjk?Q2b+8fbgpDdYBIrs6fCd)0Nhk?-6lC2g zRQLI{f`X_!5fjvp0HJ#v*vx!MR?=o>Bd$prYHfN|#qPC$=&Npl(B;^)T*>2UEe)<2 zdNqy0)Ad>&WP`4JNy~68#TDrHF^p$eTHai%q%y&AVyYgdTuv=u5W0AF3Mt2+wd;qus>KHymoXuEUk?I)b46dU0KWRkx7q zzP^T9ypPYy{g`o+HyQnQGVncoPTcGtWb|v}@@Q`Acf%G?I~ZDv-N`^?l+o{Tu6B#+ zfriV)l>Y)~#BI2~KLoU!lXTZtxG-EHe4g#SWz`hJ^#Lv|zbl8!ioQF+aBYdMp*vZu zDTZ@0iRE==e=}TbfUUbuVJUP$-d*)4(!_ObG@-_I@EMYR$Hz4HDZ9#WT?`x+a3(H{ zejlzRF+ad3lt01f_a>Lep$_H4P37;T+f)OfJJ&wO*k!Qz!NAj9cM{Zli|)GJy!g5g zQH|ge%9ovF^jpadS8zi=8ZO0^?s^FbJZgu(J(T2cKsmmN>Y{6G6HP^~12t&ZN6>nZ zX%$inOhZHxg1wQ1`n`CyR6dh9lWD`(F?MPc{S;s2Msjh@Kg@6qiK^r~jiUD){evYs ziRD8E@aeAQ;AK(yJ#2h%lyXvq$)o(2Rc@pR(){xb*E4a@dI)D2Z@AXRsU=r&HzLh) zAc;BYBiLU@+(i42yJ|e$3kZ7GLv=0a4T-N^;;S7pV5oCFxdM{v-sXr483Xzl-ewkn zqL42qpMy;sr(?ba_GB=$s}Fj262b88_`sOk43dkOD_8B(+*JnfDp!TD1h2pqWa!PA47MMDE7dz`l>)y4GuK2^ zH9jn2jR%O(B`~JX7`U{Ilyqg&7s7BHq=$PX`*hbVfDBh_FX@m?xCpv7;*07{#-x>$a7W$sh97(N55WS4 zYd+dQ>#Eg2HeBgMVXdz+lq83D=mVCVeK4+IYK1LsLs2vn4I0~2MGHSfYB*0I_DB9Z zC=Nw-&{p7pJejKxsHE#V8nvHO$t=QR!$1fYI}@@3Wek16Vx2oJiw)o!(yzbn`V067 zD>Y!jM(m+BC-u#&SrO_f@s2cHxg=6_CTJ>^hM$4j@E^I&6kNh`XBn<>)FjF-iWSaB zb>mo7_@&gb#=vVp6~dR$7lfZ`3%`(Sr10;#2#v!PrkOkmVLAyCAkA~X)1nu|P8Gz4 zNn+o=Z&~6<0#))7So0qWN;C{(#ix_wqr*~DXv{p`Di~O0&hD?Lu%W{`lV?%Ft8i^5 zpEUR}31&|9%2i+R4}o5Hl<)`~oouGiE_}RXgPR6$vQSUqayN77ZIt4(Aa^_LEU>U0 zl_Y5!&MVaE!x%Y0Rp+&N|i_h^-Itd{M}$}E#TO51r`sA6L1l9 zorzES6Wwv?LDU_tyMgU~CZ#584<~uubqfI+QB)J}c)ClYd)J}CGH!tRN*U$_A0|r< zZ{q?%y-xEVgqseN%KU~a2oU<~Vzlw04fWkg?#7uQ>_s0?z`P5L#=oj6d=GDk40U2X zNmx&+Sg!+%?i}l`C8!hX+)N{Y;ax|bcZs|DBEC5Sfo0U6?XEtTKtXSSOc3<$5MSHH zR~vmnqCXYO$(Wz!lNBiWbk~*mGFDtF%Rw#PJ(D6m@i~RG3qK2i4cGOA79HpB6iH#OU?Q#{%0As&2Mg3e;#L%S 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 QM*i&P64Ge&sC@DN0OgJ^Z2$lO literal 0 HcmV?d00001 diff --git a/platform/mcu/xr871/tools/xz64 b/platform/mcu/xr871/tools/xz64 new file mode 100644 index 0000000000000000000000000000000000000000..e75401d3e10ee4cae19e4a985fa56b489bff6253 GIT binary patch literal 68648 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 FLASH + + .text : + { + __text_start__ = .; + KEEP(*(.isr_vector)) + KEEP(*(.text.Reset_Handler)) + KEEP(*(.cpu_text)) + *(.text*) + KEEP(*(.init)) + KEEP(*(.fini)) + + /* .ctors */ + *crtbegin.o(.ctors) + *crtbegin?.o(.ctors) + *(EXCLUDE_FILE(*crtend?.o *crtend.o) .ctors) + *(SORT(.ctors.*)) + *(.ctors) + + /* .dtors */ + *crtbegin.o(.dtors) + *crtbegin?.o(.dtors) + *(EXCLUDE_FILE(*crtend?.o *crtend.o) .dtors) + *(SORT(.dtors.*)) + *(.dtors) + + *(.rodata*) + + KEEP(*(.eh_frame*)) + /* All text end */ + __text_end__ = .; + } > RAM + + .ARM.extab : + { + *(.ARM.extab* .gnu.linkonce.armextab.*) + } > RAM + + __exidx_start = .; + .ARM.exidx : + { + *(.ARM.exidx* .gnu.linkonce.armexidx.*) + } > RAM + __exidx_end = .; + + __etext = .; + _sidata = .; + + .data : + { + __data_start__ = .; + _sdata = .; + *(vtable) + *(.data*) + + . = ALIGN(4); + /* preinit data */ + PROVIDE_HIDDEN (__preinit_array_start = .); + KEEP(*(.preinit_array)) + PROVIDE_HIDDEN (__preinit_array_end = .); + + . = ALIGN(4); + /* init data */ + PROVIDE_HIDDEN (__init_array_start = .); + KEEP(*(SORT(.init_array.*))) + KEEP(*(.init_array)) + PROVIDE_HIDDEN (__init_array_end = .); + + + . = ALIGN(4); + /* finit data */ + PROVIDE_HIDDEN (__fini_array_start = .); + KEEP(*(SORT(.fini_array.*))) + KEEP(*(.fini_array)) + PROVIDE_HIDDEN (__fini_array_end = .); + + KEEP(*(.jcr*)) + . = ALIGN(4); + /* All data end */ + __data_end__ = .; + _edata = .; + + } > RAM + + .bss : + { + . = ALIGN(4); + __bss_start__ = .; + _sbss = .; + *(.bss*) + *(COMMON) + . = ALIGN(4); + __bss_end__ = .; + _ebss = .; + } > RAM + + .heap (COPY): + { + __end__ = .; + end = __end__; + *(.heap*) + __HeapLimit = .; + } > RAM + + /* .stack_dummy section doesn't contains any symbols. It is only + * used for linker to calculate size of stack sections, and assign + * values to stack symbols later */ + .stack_dummy (COPY): + { + *(.stack*) + } > RAM + + /* Set stack top to end of RAM, and stack limit move down by + * size of stack_dummy section */ + __StackTop = ORIGIN(RAM) + LENGTH(RAM); + __StackBottom = __StackTop - 0x400; + _estack = __StackTop; + __StackLimit = __StackTop - SIZEOF(.stack_dummy); + PROVIDE(__stack = __StackTop); + PROVIDE(heap_start = __end__); + PROVIDE(heap_end = __StackBottom); + PROVIDE(heap_len = heap_end - heap_start); + + /* Check if data + heap + stack exceeds RAM limit */ + ASSERT(__StackLimit >= __HeapLimit, "region RAM overflowed with stack") +} diff --git a/platform/mcu/xr871/xr871.ld b/platform/mcu/xr871/xr871.ld new file mode 100644 index 0000000000..30ccba7cf6 --- /dev/null +++ b/platform/mcu/xr871/xr871.ld @@ -0,0 +1,170 @@ +/* +***************************************************************************** +** +** File : XR871.ld +** +** Abstract : Linker script for XR871 Device with +** 2048KByte FLASH, 448KByte RAM +** +** Set heap size, stack size and stack location according +** to application requirements. +** +** Set memory bank area and size if external memory is used. +** +** Target : Xradio Technology XR871 +** +** Environment : Gcc +** +** Distribution: The file is distributed as is, without any warranty +** of any kind. +** +** +***************************************************************************** +*/ + +/* Specify the memory areas */ +MEMORY +{ + RAM (rwx) : ORIGIN = 0x00010000, LENGTH = 448K + FLASH (rx) : ORIGIN = 0x10000000, LENGTH = 256K +} + +__RAM_BASE = ORIGIN(RAM); + +ENTRY(Reset_Handler) + +SECTIONS +{ +/* + .xip : + { + __xip_start__ = .; + /* All xip end * / + __xip_end__ = .; + } > FLASH +*/ + + .text : + { + __text_start__ = .; + KEEP(*(.isr_vector)) + KEEP(*(.text.Reset_Handler)) + KEEP(*(.cpu_text)) + *(.text*) + KEEP(*(.init)) + KEEP(*(.fini)) + + /* .ctors */ + *crtbegin.o(.ctors) + *crtbegin?.o(.ctors) + *(EXCLUDE_FILE(*crtend?.o *crtend.o) .ctors) + *(SORT(.ctors.*)) + *(.ctors) + + /* .dtors */ + *crtbegin.o(.dtors) + *crtbegin?.o(.dtors) + *(EXCLUDE_FILE(*crtend?.o *crtend.o) .dtors) + *(SORT(.dtors.*)) + *(.dtors) + + *(.rodata*) + + KEEP(*(.eh_frame*)) + /* All text end */ + __text_end__ = .; + } > RAM + + .ARM.extab : + { + *(.ARM.extab* .gnu.linkonce.armextab.*) + } > RAM + + __exidx_start = .; + .ARM.exidx : + { + *(.ARM.exidx* .gnu.linkonce.armexidx.*) + } > RAM + __exidx_end = .; + + __etext = .; + _sidata = .; + + .data : + { + __data_start__ = .; + _sdata = .; + *(vtable) + *(.data*) + + . = ALIGN(4); + /* preinit data */ + PROVIDE_HIDDEN (__preinit_array_start = .); + KEEP(*(.preinit_array)) + PROVIDE_HIDDEN (__preinit_array_end = .); + + . = ALIGN(4); + /* init data */ + PROVIDE_HIDDEN (__init_array_start = .); + KEEP(*(SORT(.init_array.*))) + KEEP(*(.init_array)) + PROVIDE_HIDDEN (__init_array_end = .); + + + . = ALIGN(4); + /* finit data */ + PROVIDE_HIDDEN (__fini_array_start = .); + KEEP(*(SORT(.fini_array.*))) + KEEP(*(.fini_array)) + PROVIDE_HIDDEN (__fini_array_end = .); + + KEEP(*(.jcr*)) + . = ALIGN(4); + /* All data end */ + __data_end__ = .; + _edata = .; + + } > RAM + + .bss : + { + . = ALIGN(4); + __bss_start__ = .; + _sbss = .; + *(.bss*) + *(COMMON) + . = ALIGN(4); + __bss_end__ = .; + _ebss = .; + } > RAM + + .heap (COPY): + { + __end__ = .; + end = __end__; + *(.heap*) + __HeapLimit = .; + } > RAM + + /* .stack_dummy section doesn't contains any symbols. It is only + * used for linker to calculate size of stack sections, and assign + * values to stack symbols later */ + .stack_dummy (COPY): + { + *(.stack*) + } > RAM + + /* Set stack top to end of RAM, and stack limit move down by + * size of stack_dummy section */ + __StackTop = ORIGIN(RAM) + LENGTH(RAM); + __StackBottom = __StackTop - 0x400; + _estack = __StackTop; + __StackLimit = __StackTop - SIZEOF(.stack_dummy); + PROVIDE(__stack = __StackTop); + PROVIDE(heap_start = __end__); + PROVIDE(heap_end = __StackBottom); + PROVIDE(heap_len = heap_end - heap_start); + + /* Check if data + heap + stack exceeds RAM limit */ + ASSERT(__StackLimit >= __HeapLimit, "region RAM overflowed with stack") +} diff --git a/platform/mcu/xr871/xr871.mk b/platform/mcu/xr871/xr871.mk new file mode 100644 index 0000000000..1e24570bfe --- /dev/null +++ b/platform/mcu/xr871/xr871.mk @@ -0,0 +1,106 @@ +NAME := xr871 + +HOST_OPENOCD := xr871 + +$(NAME)_TYPE := kernel + +no_with_lwip := 0 +no_with_xip := 0 +no_with_image_compress := 0 +no_with_ota := 0 + +GLOBAL_DEFINES += CONFIG_AOS_KV_MULTIPTN_MODE +GLOBAL_DEFINES += CONFIG_AOS_KV_PTN=6 +GLOBAL_DEFINES += CONFIG_AOS_KV_SECOND_PTN=7 +GLOBAL_DEFINES += CONFIG_AOS_KV_PTN_SIZE=4096 +GLOBAL_DEFINES += CONFIG_AOS_KV_BUFFER_SIZE=8192 +GLOBAL_DEFINES += CONFIG_AOS_FATFS_SUPPORT_MMC +GLOBAL_DEFINES += CONFIG_AOS_FATFS_SUPPORT + +$(NAME)_INCLUDES += . +GLOBAL_INCLUDES += aos +GLOBAL_INCLUDES += include +GLOBAL_INCLUDES += include/net/lwip +GLOBAL_INCLUDES += include/driver/cmsis +GLOBAL_INCLUDES += project +GLOBAL_INCLUDES += project/main +GLOBAL_INCLUDES += project/common/framework +GLOBAL_INCLUDES += project/common/board/xr871_evb_main + +$(NAME)_COMPONENTS += platform/arch/arm/armv7m +$(NAME)_COMPONENTS += rhino hal protocols.net yloop framework.common netmgr mbedtls modules.fs.fatfs vcall libc digest_algorithm +$(NAME)_COMPONENTS += platform/mcu/xr871/src/driver/chip +$(NAME)_COMPONENTS += platform/mcu/xr871/src/image +$(NAME)_COMPONENTS += platform/mcu/xr871/src/net/udhcp +$(NAME)_COMPONENTS += platform/mcu/xr871/src/net/wlan +$(NAME)_COMPONENTS += platform/mcu/xr871/src/ota +$(NAME)_COMPONENTS += platform/mcu/xr871/src/pm +$(NAME)_COMPONENTS += platform/mcu/xr871/src/efpg +$(NAME)_COMPONENTS += platform/mcu/xr871/src/sys +$(NAME)_COMPONENTS += platform/mcu/xr871/src/xz +$(NAME)_COMPONENTS += platform/mcu/xr871/src/net/lwip +$(NAME)_COMPONENTS += platform/mcu/xr871/src/console +$(NAME)_COMPONENTS += platform/mcu/xr871/project +$(NAME)_COMPONENTS += platform/mcu/xr871/aos + +$(NAME)_COMPONENTS += platform/mcu/xr871/src/audio/audio_manager +$(NAME)_COMPONENTS += platform/mcu/xr871/src/audio/audio_pcm +$(NAME)_COMPONENTS += platform/mcu/xr871/src/cedarx +$(NAME)_COMPONENTS += platform/mcu/xr871/lib/libmp3 +#$(NAME)_COMPONENTS += platform/mcu/xr871/lib/libamr +#$(NAME)_COMPONENTS += platform/mcu/xr871/lib/libamren + +GLOBAL_ASMFLAGS += -mcpu=cortex-m4 \ + -mthumb \ + -mfpu=fpv4-sp-d16 \ + -mfloat-abi=softfp + +GLOBAL_CFLAGS += -mcpu=cortex-m4 \ + -mthumb \ + -mfpu=fpv4-sp-d16 \ + -mfloat-abi=softfp + +GLOBAL_CFLAGS += -include prj_config.h +GLOBAL_CFLAGS += -w +GLOBAL_CFLAGS += -fno-common \ + -fmessage-length=0 \ + -fno-exceptions \ + -ffunction-sections \ + -fdata-sections \ + -fomit-frame-pointer \ + -Wall \ + -Werror \ + -Wno-error=unused-function \ + -MMD -MP \ + -Os -DNDEBUG + +GLOBAL_CFLAGS += -D__CONFIG_OS_USE_YUNOS +GLOBAL_CFLAGS += -D__CONFIG_CHIP_XR871 +GLOBAL_CFLAGS += -D__CONFIG_CPU_CM4F +GLOBAL_CFLAGS += -D__CONFIG_ARCH_DUAL_CORE +GLOBAL_CFLAGS += -D__CONFIG_ARCH_APP_CORE +GLOBAL_CFLAGS += -D__CONFIG_LIBC_REDEFINE_GCC_INT32_TYPE +GLOBAL_CFLAGS += -D__CONFIG_LIBC_PRINTF_FLOAT +GLOBAL_CFLAGS += -D__CONFIG_LIBC_SCANF_FLOAT +GLOBAL_CFLAGS += -D__CONFIG_MALLOC_USE_STDLIB +GLOBAL_CFLAGS += -D__CONFIG_MBUF_IMPL_MODE=0 +GLOBAL_CFLAGS += -D__PRJ_CONFIG_WLAN_STA_AP + +ifneq ($(no_with_xip),1) +GLOBAL_CFLAGS += -D__PRJ_CONFIG_XIP +endif + +ifneq ($(no_with_image_compress),1) +GLOBAL_CFLAGS += -D__PRJ_CONFIG_IMG_COMPRESS +endif + +GLOBAL_LDFLAGS += -mcpu=cortex-m4 -mthumb -mfpu=fpv4-sp-d16 -mfloat-abi=softfp +GLOBAL_LDFLAGS += -Wl,--gc-sections --specs=nano.specs +GLOBAL_LDFLAGS += -Wl,--wrap,main +GLOBAL_LDFLAGS += -lstdc++ -lsupc++ -lm -lc -lgcc -lnosys + +ifneq ($(no_with_xip),1) +GLOBAL_LINK_SCRIPT += platform/mcu/xr871/xr871-xip.ld +else +GLOBAL_LINK_SCRIPT += platform/mcu/xr871/xr871.ld +endif