first submission

This commit is contained in:
qiyongshuang
2023-02-01 14:19:13 +08:00
parent d534aad004
commit b79c6ac80d
3711 changed files with 745432 additions and 2 deletions
@@ -0,0 +1,171 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<?fileVersion 4.0.0?><cproject storage_type_id="org.eclipse.cdt.core.XmlProjectDescriptionStorage">
<storageModule moduleId="org.eclipse.cdt.core.settings">
<cconfiguration id="ilg.gnumcueclipse.managedbuild.cross.riscv.config.elf.release.1008047074">
<storageModule buildSystemId="org.eclipse.cdt.managedbuilder.core.configurationDataProvider" id="ilg.gnumcueclipse.managedbuild.cross.riscv.config.elf.release.1008047074" moduleId="org.eclipse.cdt.core.settings" name="obj">
<externalSettings/>
<extensions>
<extension id="org.eclipse.cdt.core.ELF" point="org.eclipse.cdt.core.BinaryParser"/>
<extension id="org.eclipse.cdt.core.GASErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
<extension id="org.eclipse.cdt.core.GmakeErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
<extension id="org.eclipse.cdt.core.GLDErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
<extension id="org.eclipse.cdt.core.CWDLocator" point="org.eclipse.cdt.core.ErrorParser"/>
<extension id="org.eclipse.cdt.core.GCCErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
</extensions>
</storageModule>
<storageModule moduleId="cdtBuildSystem" version="4.0.0">
<configuration artifactName="${ProjName}" buildArtefactType="org.eclipse.cdt.build.core.buildArtefactType.exe" buildProperties="org.eclipse.cdt.build.core.buildArtefactType=org.eclipse.cdt.build.core.buildArtefactType.exe,org.eclipse.cdt.build.core.buildType=org.eclipse.cdt.build.core.buildType.release" cleanCommand="${cross_rm} -rf" description="" id="ilg.gnumcueclipse.managedbuild.cross.riscv.config.elf.release.1008047074" name="obj" parent="ilg.gnumcueclipse.managedbuild.cross.riscv.config.elf.release">
<folderInfo id="ilg.gnumcueclipse.managedbuild.cross.riscv.config.elf.release.1008047074." name="/" resourcePath="">
<toolChain id="ilg.gnumcueclipse.managedbuild.cross.riscv.toolchain.elf.release.231146001" name="RISC-V Cross GCC" superClass="ilg.gnumcueclipse.managedbuild.cross.riscv.toolchain.elf.release">
<option id="ilg.gnumcueclipse.managedbuild.cross.riscv.option.addtools.createflash.1311852988" name="Create flash image" superClass="ilg.gnumcueclipse.managedbuild.cross.riscv.option.addtools.createflash" useByScannerDiscovery="false" value="true" valueType="boolean"/>
<option id="ilg.gnumcueclipse.managedbuild.cross.riscv.option.addtools.createlisting.1983282875" name="Create extended listing" superClass="ilg.gnumcueclipse.managedbuild.cross.riscv.option.addtools.createlisting" useByScannerDiscovery="false" value="true" valueType="boolean"/>
<option id="ilg.gnumcueclipse.managedbuild.cross.riscv.option.addtools.printsize.1000761142" name="Print size" superClass="ilg.gnumcueclipse.managedbuild.cross.riscv.option.addtools.printsize" useByScannerDiscovery="false" value="true" valueType="boolean"/>
<option id="ilg.gnumcueclipse.managedbuild.cross.riscv.option.optimization.level.514997414" name="Optimization Level" superClass="ilg.gnumcueclipse.managedbuild.cross.riscv.option.optimization.level" useByScannerDiscovery="true" value="ilg.gnumcueclipse.managedbuild.cross.riscv.option.optimization.level.size" valueType="enumerated"/>
<option id="ilg.gnumcueclipse.managedbuild.cross.riscv.option.optimization.messagelength.1008570639" name="Message length (-fmessage-length=0)" superClass="ilg.gnumcueclipse.managedbuild.cross.riscv.option.optimization.messagelength" useByScannerDiscovery="true" value="true" valueType="boolean"/>
<option id="ilg.gnumcueclipse.managedbuild.cross.riscv.option.optimization.signedchar.467272439" name="'char' is signed (-fsigned-char)" superClass="ilg.gnumcueclipse.managedbuild.cross.riscv.option.optimization.signedchar" useByScannerDiscovery="true" value="true" valueType="boolean"/>
<option id="ilg.gnumcueclipse.managedbuild.cross.riscv.option.optimization.functionsections.2047756949" name="Function sections (-ffunction-sections)" superClass="ilg.gnumcueclipse.managedbuild.cross.riscv.option.optimization.functionsections" useByScannerDiscovery="true" value="true" valueType="boolean"/>
<option id="ilg.gnumcueclipse.managedbuild.cross.riscv.option.optimization.datasections.207613650" name="Data sections (-fdata-sections)" superClass="ilg.gnumcueclipse.managedbuild.cross.riscv.option.optimization.datasections" useByScannerDiscovery="true" value="true" valueType="boolean"/>
<option id="ilg.gnumcueclipse.managedbuild.cross.riscv.option.debugging.level.1204865254" name="Debug level" superClass="ilg.gnumcueclipse.managedbuild.cross.riscv.option.debugging.level" useByScannerDiscovery="true"/>
<option id="ilg.gnumcueclipse.managedbuild.cross.riscv.option.debugging.format.867779652" name="Debug format" superClass="ilg.gnumcueclipse.managedbuild.cross.riscv.option.debugging.format" useByScannerDiscovery="true"/>
<option id="ilg.gnumcueclipse.managedbuild.cross.riscv.option.target.isa.base.1900297968" name="Architecture" superClass="ilg.gnumcueclipse.managedbuild.cross.riscv.option.target.isa.base" useByScannerDiscovery="false" value="ilg.gnumcueclipse.managedbuild.cross.riscv.option.target.arch.rv32i" valueType="enumerated"/>
<option id="ilg.gnumcueclipse.managedbuild.cross.riscv.option.target.abi.integer.387605487" name="Integer ABI" superClass="ilg.gnumcueclipse.managedbuild.cross.riscv.option.target.abi.integer" useByScannerDiscovery="false" value="ilg.gnumcueclipse.managedbuild.cross.riscv.option.abi.integer.ilp32" valueType="enumerated"/>
<option id="ilg.gnumcueclipse.managedbuild.cross.riscv.option.target.isa.multiply.1509705449" name="Multiply extension (RVM)" superClass="ilg.gnumcueclipse.managedbuild.cross.riscv.option.target.isa.multiply" useByScannerDiscovery="false" value="true" valueType="boolean"/>
<option id="ilg.gnumcueclipse.managedbuild.cross.riscv.option.target.isa.compressed.1038505275" name="Compressed extension (RVC)" superClass="ilg.gnumcueclipse.managedbuild.cross.riscv.option.target.isa.compressed" useByScannerDiscovery="false" value="true" valueType="boolean"/>
<option id="ilg.gnumcueclipse.managedbuild.cross.riscv.option.toolchain.name.1218760634" superClass="ilg.gnumcueclipse.managedbuild.cross.riscv.option.toolchain.name" useByScannerDiscovery="false" value="GNU MCU RISC-V GCC" valueType="string"/>
<option id="ilg.gnumcueclipse.managedbuild.cross.riscv.option.command.prefix.103341323" name="Prefix" superClass="ilg.gnumcueclipse.managedbuild.cross.riscv.option.command.prefix" useByScannerDiscovery="false" value="riscv-none-embed-" valueType="string"/>
<option id="ilg.gnumcueclipse.managedbuild.cross.riscv.option.command.c.487601824" name="C compiler" superClass="ilg.gnumcueclipse.managedbuild.cross.riscv.option.command.c" useByScannerDiscovery="false" value="gcc" valueType="string"/>
<option id="ilg.gnumcueclipse.managedbuild.cross.riscv.option.command.cpp.1062130429" name="C++ compiler" superClass="ilg.gnumcueclipse.managedbuild.cross.riscv.option.command.cpp" useByScannerDiscovery="false" value="g++" valueType="string"/>
<option id="ilg.gnumcueclipse.managedbuild.cross.riscv.option.command.ar.1194282993" name="Archiver" superClass="ilg.gnumcueclipse.managedbuild.cross.riscv.option.command.ar" useByScannerDiscovery="false" value="ar" valueType="string"/>
<option id="ilg.gnumcueclipse.managedbuild.cross.riscv.option.command.objcopy.1529355265" name="Hex/Bin converter" superClass="ilg.gnumcueclipse.managedbuild.cross.riscv.option.command.objcopy" useByScannerDiscovery="false" value="objcopy" valueType="string"/>
<option id="ilg.gnumcueclipse.managedbuild.cross.riscv.option.command.objdump.1053750745" name="Listing generator" superClass="ilg.gnumcueclipse.managedbuild.cross.riscv.option.command.objdump" useByScannerDiscovery="false" value="objdump" valueType="string"/>
<option id="ilg.gnumcueclipse.managedbuild.cross.riscv.option.command.size.1441326233" name="Size command" superClass="ilg.gnumcueclipse.managedbuild.cross.riscv.option.command.size" useByScannerDiscovery="false" value="size" valueType="string"/>
<option id="ilg.gnumcueclipse.managedbuild.cross.riscv.option.command.make.550105535" name="Build command" superClass="ilg.gnumcueclipse.managedbuild.cross.riscv.option.command.make" useByScannerDiscovery="false" value="make" valueType="string"/>
<option id="ilg.gnumcueclipse.managedbuild.cross.riscv.option.command.rm.719280496" name="Remove command" superClass="ilg.gnumcueclipse.managedbuild.cross.riscv.option.command.rm" useByScannerDiscovery="false" value="rm" valueType="string"/>
<option id="ilg.gnumcueclipse.managedbuild.cross.riscv.option.toolchain.id.226017994" superClass="ilg.gnumcueclipse.managedbuild.cross.riscv.option.toolchain.id" useByScannerDiscovery="false" value="512258282" valueType="string"/>
<option id="ilg.gnumcueclipse.managedbuild.cross.riscv.option.target.abi.fp.962468442" name="Floating point ABI" superClass="ilg.gnumcueclipse.managedbuild.cross.riscv.option.target.abi.fp" useByScannerDiscovery="false" value="ilg.gnumcueclipse.managedbuild.cross.riscv.option.abi.fp.none" valueType="enumerated"/>
<option id="ilg.gnumcueclipse.managedbuild.cross.riscv.option.target.codemodel.1234945999" name="Code model" superClass="ilg.gnumcueclipse.managedbuild.cross.riscv.option.target.codemodel" useByScannerDiscovery="false" value="ilg.gnumcueclipse.managedbuild.cross.riscv.option.target.codemodel.any" valueType="enumerated"/>
<option id="ilg.gnumcueclipse.managedbuild.cross.riscv.option.target.isa.atomic.985553432" name="Atomic extension (RVA)" superClass="ilg.gnumcueclipse.managedbuild.cross.riscv.option.target.isa.atomic" useByScannerDiscovery="false" value="true" valueType="boolean"/>
<option id="ilg.gnumcueclipse.managedbuild.cross.riscv.option.optimization.nocommon.1028939601" name="No common unitialized (-fno-common)" superClass="ilg.gnumcueclipse.managedbuild.cross.riscv.option.optimization.nocommon" value="true" valueType="boolean"/>
<option id="ilg.gnumcueclipse.managedbuild.cross.riscv.option.target.isa.xw.776646130" name="Extra Compressed extension (RVXW)" superClass="ilg.gnumcueclipse.managedbuild.cross.riscv.option.target.isa.xw" value="true" valueType="boolean"/>
<targetPlatform archList="all" binaryParser="org.eclipse.cdt.core.ELF" id="ilg.gnumcueclipse.managedbuild.cross.riscv.targetPlatform.1944008784" isAbstract="false" osList="all" superClass="ilg.gnumcueclipse.managedbuild.cross.riscv.targetPlatform"/>
<builder buildPath="${workspace_loc:/Peripheral}/obj" id="ilg.gnumcueclipse.managedbuild.cross.riscv.builder.1421508906" keepEnvironmentInBuildfile="false" managedBuildOn="true" name="Gnu Make Builder" parallelBuildOn="true" parallelizationNumber="optimal" superClass="ilg.gnumcueclipse.managedbuild.cross.riscv.builder"/>
<tool id="ilg.gnumcueclipse.managedbuild.cross.riscv.tool.assembler.1244756189" name="GNU RISC-V Cross Assembler" superClass="ilg.gnumcueclipse.managedbuild.cross.riscv.tool.assembler">
<option id="ilg.gnumcueclipse.managedbuild.cross.riscv.option.assembler.usepreprocessor.1692176068" name="Use preprocessor" superClass="ilg.gnumcueclipse.managedbuild.cross.riscv.option.assembler.usepreprocessor" useByScannerDiscovery="false" value="true" valueType="boolean"/>
<option IS_BUILTIN_EMPTY="false" IS_VALUE_EMPTY="true" id="ilg.gnumcueclipse.managedbuild.cross.riscv.option.assembler.include.paths.1034038285" name="Include paths (-I)" superClass="ilg.gnumcueclipse.managedbuild.cross.riscv.option.assembler.include.paths" useByScannerDiscovery="true" valueType="includePath"/>
<inputType id="ilg.gnumcueclipse.managedbuild.cross.riscv.tool.assembler.input.126366858" superClass="ilg.gnumcueclipse.managedbuild.cross.riscv.tool.assembler.input"/>
</tool>
<tool id="ilg.gnumcueclipse.managedbuild.cross.riscv.tool.c.compiler.1731377187" name="GNU RISC-V Cross C Compiler" superClass="ilg.gnumcueclipse.managedbuild.cross.riscv.tool.c.compiler">
<option IS_BUILTIN_EMPTY="false" IS_VALUE_EMPTY="false" id="ilg.gnumcueclipse.managedbuild.cross.riscv.option.c.compiler.include.paths.1567947810" name="Include paths (-I)" superClass="ilg.gnumcueclipse.managedbuild.cross.riscv.option.c.compiler.include.paths" useByScannerDiscovery="true" valueType="includePath">
<listOptionValue builtIn="false" value="&quot;${workspace_loc:/${ProjName}/app/include}&quot;"/>
<listOptionValue builtIn="false" value="&quot;${workspace_loc:/${ProjName}/includes}&quot;"/>
<listOptionValue builtIn="false" value="&quot;${workspace_loc:/${ProjName}/services}&quot;"/>
<listOptionValue builtIn="false" value="&quot;${workspace_loc:/${ProjName}/SRC/Core}&quot;"/>
<listOptionValue builtIn="false" value="&quot;${workspace_loc:/${ProjName}/SRC/Startup}&quot;"/>
<listOptionValue builtIn="false" value="&quot;${workspace_loc:/${ProjName}/SRC/Debug}&quot;"/>
<listOptionValue builtIn="false" value="&quot;${workspace_loc:/${ProjName}/SRC/Peripheral/inc}&quot;"/>
<listOptionValue builtIn="false" value="&quot;${workspace_loc:/${ProjName}/APP/include}&quot;"/>
<listOptionValue builtIn="false" value="&quot;${workspace_loc:/${ProjName}/Profile/include}&quot;"/>
<listOptionValue builtIn="false" value="&quot;${workspace_loc:/${ProjName}/HAL/include}&quot;"/>
<listOptionValue builtIn="false" value="&quot;${workspace_loc:/${ProjName}/app/include}&quot;"/>
<listOptionValue builtIn="false" value="&quot;${workspace_loc:/${ProjName}/LIB}&quot;"/>
</option>
<option id="ilg.gnumcueclipse.managedbuild.cross.riscv.option.c.compiler.std.2020844713" name="Language standard" superClass="ilg.gnumcueclipse.managedbuild.cross.riscv.option.c.compiler.std" useByScannerDiscovery="true" value="ilg.gnumcueclipse.managedbuild.cross.riscv.option.c.compiler.std.gnu99" valueType="enumerated"/>
<option IS_BUILTIN_EMPTY="false" IS_VALUE_EMPTY="false" id="ilg.gnumcueclipse.managedbuild.cross.riscv.option.c.compiler.defs.177116515" name="Defined symbols (-D)" superClass="ilg.gnumcueclipse.managedbuild.cross.riscv.option.c.compiler.defs" useByScannerDiscovery="true" valueType="definedSymbols">
<listOptionValue builtIn="false" value="HAL_KEY=1"/>
<listOptionValue builtIn="false" value="CH32V20x_D8W"/>
<listOptionValue builtIn="false" value="CONFIG_RISCV"/>
</option>
<option IS_BUILTIN_EMPTY="false" IS_VALUE_EMPTY="true" id="ilg.gnumcueclipse.managedbuild.cross.riscv.option.c.compiler.include.files.288896968" name="Include files (-include)" superClass="ilg.gnumcueclipse.managedbuild.cross.riscv.option.c.compiler.include.files" useByScannerDiscovery="true" valueType="includeFiles"/>
<option id="ilg.gnumcueclipse.managedbuild.cross.riscv.option.c.compiler.preprocessonly.1594987158" name="Preprocess only (-E)" superClass="ilg.gnumcueclipse.managedbuild.cross.riscv.option.c.compiler.preprocessonly" useByScannerDiscovery="false" value="false" valueType="boolean"/>
<option id="ilg.gnumcueclipse.managedbuild.cross.riscv.option.c.compiler.nostdinc.698774408" name="Do not search system directories (-nostdinc)" superClass="ilg.gnumcueclipse.managedbuild.cross.riscv.option.c.compiler.nostdinc" useByScannerDiscovery="true" value="false" valueType="boolean"/>
<option IS_BUILTIN_EMPTY="false" IS_VALUE_EMPTY="false" id="ilg.gnumcueclipse.managedbuild.cross.riscv.option.c.compiler.include.systempaths.1030624098" name="Include system paths (-isystem)" superClass="ilg.gnumcueclipse.managedbuild.cross.riscv.option.c.compiler.include.systempaths" useByScannerDiscovery="true" valueType="includePath">
<listOptionValue builtIn="false" value="&quot;${workspace_loc:/${ProjName}/includes}&quot;"/>
</option>
<inputType id="ilg.gnumcueclipse.managedbuild.cross.riscv.tool.c.compiler.input.2036806839" superClass="ilg.gnumcueclipse.managedbuild.cross.riscv.tool.c.compiler.input"/>
</tool>
<tool id="ilg.gnumcueclipse.managedbuild.cross.riscv.tool.cpp.compiler.1610882921" name="GNU RISC-V Cross C++ Compiler" superClass="ilg.gnumcueclipse.managedbuild.cross.riscv.tool.cpp.compiler"/>
<tool id="ilg.gnumcueclipse.managedbuild.cross.riscv.tool.c.linker.1620074387" name="GNU RISC-V Cross C Linker" superClass="ilg.gnumcueclipse.managedbuild.cross.riscv.tool.c.linker">
<option id="ilg.gnumcueclipse.managedbuild.cross.riscv.option.c.linker.gcsections.194760422" name="Remove unused sections (-Xlinker --gc-sections)" superClass="ilg.gnumcueclipse.managedbuild.cross.riscv.option.c.linker.gcsections" useByScannerDiscovery="false" value="true" valueType="boolean"/>
<option IS_BUILTIN_EMPTY="false" IS_VALUE_EMPTY="false" id="ilg.gnumcueclipse.managedbuild.cross.riscv.option.c.linker.paths.2057340378" name="Library search path (-L)" superClass="ilg.gnumcueclipse.managedbuild.cross.riscv.option.c.linker.paths" useByScannerDiscovery="false" valueType="libPaths">
<listOptionValue builtIn="false" value="&quot;../&quot;"/>
<listOptionValue builtIn="false" value="&quot;${workspace_loc:/${ProjName}/LIB}&quot;"/>
</option>
<option IS_BUILTIN_EMPTY="false" IS_VALUE_EMPTY="false" id="ilg.gnumcueclipse.managedbuild.cross.riscv.option.c.linker.scriptfile.1390103472" name="Script files (-T)" superClass="ilg.gnumcueclipse.managedbuild.cross.riscv.option.c.linker.scriptfile" useByScannerDiscovery="false" valueType="stringList">
<listOptionValue builtIn="false" value="&quot;${workspace_loc:/${ProjName}/HAL/Link.ld}&quot;"/>
</option>
<option id="ilg.gnumcueclipse.managedbuild.cross.riscv.option.c.linker.nostart.913830613" name="Do not use standard start files (-nostartfiles)" superClass="ilg.gnumcueclipse.managedbuild.cross.riscv.option.c.linker.nostart" useByScannerDiscovery="false" value="true" valueType="boolean"/>
<option id="ilg.gnumcueclipse.managedbuild.cross.riscv.option.c.linker.usenewlibnano.239404511" name="Use newlib-nano (--specs=nano.specs)" superClass="ilg.gnumcueclipse.managedbuild.cross.riscv.option.c.linker.usenewlibnano" useByScannerDiscovery="false" value="true" valueType="boolean"/>
<option id="ilg.gnumcueclipse.managedbuild.cross.riscv.option.c.linker.usenewlibnosys.351964161" name="Do not use syscalls (--specs=nosys.specs)" superClass="ilg.gnumcueclipse.managedbuild.cross.riscv.option.c.linker.usenewlibnosys" useByScannerDiscovery="false" value="true" valueType="boolean"/>
<option IS_BUILTIN_EMPTY="false" IS_VALUE_EMPTY="true" id="ilg.gnumcueclipse.managedbuild.cross.riscv.option.c.linker.otherobjs.16994550" name="Other objects" superClass="ilg.gnumcueclipse.managedbuild.cross.riscv.option.c.linker.otherobjs" useByScannerDiscovery="false" valueType="userObjs"/>
<option id="ilg.gnumcueclipse.managedbuild.cross.riscv.option.c.linker.strip.1780837525" name="Omit all symbol information (-s)" superClass="ilg.gnumcueclipse.managedbuild.cross.riscv.option.c.linker.strip" useByScannerDiscovery="false" value="false" valueType="boolean"/>
<option id="ilg.gnumcueclipse.managedbuild.cross.riscv.option.c.linker.cref.1847421489" name="Cross reference (-Xlinker --cref)" superClass="ilg.gnumcueclipse.managedbuild.cross.riscv.option.c.linker.cref" useByScannerDiscovery="false" value="false" valueType="boolean"/>
<option id="ilg.gnumcueclipse.managedbuild.cross.riscv.option.c.linker.printmap.548776282" name="Print link map (-Xlinker --print-map)" superClass="ilg.gnumcueclipse.managedbuild.cross.riscv.option.c.linker.printmap" useByScannerDiscovery="false" value="false" valueType="boolean"/>
<option IS_BUILTIN_EMPTY="false" IS_VALUE_EMPTY="false" id="ilg.gnumcueclipse.managedbuild.cross.riscv.option.c.linker.libs.1350862782" name="Libraries (-l)" superClass="ilg.gnumcueclipse.managedbuild.cross.riscv.option.c.linker.libs" useByScannerDiscovery="false" valueType="libs">
<listOptionValue builtIn="false" value="wchble"/>
</option>
<option IS_BUILTIN_EMPTY="false" IS_VALUE_EMPTY="false" id="ilg.gnumcueclipse.managedbuild.cross.riscv.option.c.linker.flags.1577723329" name="Linker flags (-Xlinker [option])" superClass="ilg.gnumcueclipse.managedbuild.cross.riscv.option.c.linker.flags" useByScannerDiscovery="false" valueType="stringList">
<listOptionValue builtIn="false" value="--print-memory-usage"/>
</option>
<inputType id="ilg.gnumcueclipse.managedbuild.cross.riscv.tool.c.linker.input.1859223768" superClass="ilg.gnumcueclipse.managedbuild.cross.riscv.tool.c.linker.input">
<additionalInput kind="additionalinputdependency" paths="$(USER_OBJS)"/>
<additionalInput kind="additionalinput" paths="$(LIBS)"/>
</inputType>
</tool>
<tool id="ilg.gnumcueclipse.managedbuild.cross.riscv.tool.cpp.linker.1947503520" name="GNU RISC-V Cross C++ Linker" superClass="ilg.gnumcueclipse.managedbuild.cross.riscv.tool.cpp.linker">
<option id="ilg.gnumcueclipse.managedbuild.cross.riscv.option.cpp.linker.gcsections.1689063433" name="Remove unused sections (-Xlinker --gc-sections)" superClass="ilg.gnumcueclipse.managedbuild.cross.riscv.option.cpp.linker.gcsections" value="true" valueType="boolean"/>
<option IS_BUILTIN_EMPTY="false" IS_VALUE_EMPTY="false" id="ilg.gnumcueclipse.managedbuild.cross.riscv.option.cpp.linker.paths.1029177148" name="Library search path (-L)" superClass="ilg.gnumcueclipse.managedbuild.cross.riscv.option.cpp.linker.paths" valueType="libPaths">
<listOptionValue builtIn="false" value="&quot;../LD&quot;"/>
</option>
<option IS_BUILTIN_EMPTY="false" IS_VALUE_EMPTY="false" id="ilg.gnumcueclipse.managedbuild.cross.riscv.option.cpp.linker.scriptfile.1751226764" name="Script files (-T)" superClass="ilg.gnumcueclipse.managedbuild.cross.riscv.option.cpp.linker.scriptfile" valueType="stringList">
<listOptionValue builtIn="false" value="Link.ld"/>
</option>
<option id="ilg.gnumcueclipse.managedbuild.cross.riscv.option.cpp.linker.nostart.642896175" name="Do not use standard start files (-nostartfiles)" superClass="ilg.gnumcueclipse.managedbuild.cross.riscv.option.cpp.linker.nostart" value="true" valueType="boolean"/>
<option id="ilg.gnumcueclipse.managedbuild.cross.riscv.option.cpp.linker.usenewlibnano.1540675679" name="Use newlib-nano (--specs=nano.specs)" superClass="ilg.gnumcueclipse.managedbuild.cross.riscv.option.cpp.linker.usenewlibnano" value="true" valueType="boolean"/>
</tool>
<tool id="ilg.gnumcueclipse.managedbuild.cross.riscv.tool.archiver.1292785366" name="GNU RISC-V Cross Archiver" superClass="ilg.gnumcueclipse.managedbuild.cross.riscv.tool.archiver"/>
<tool id="ilg.gnumcueclipse.managedbuild.cross.riscv.tool.createflash.1801165667" name="GNU RISC-V Cross Create Flash Image" superClass="ilg.gnumcueclipse.managedbuild.cross.riscv.tool.createflash"/>
<tool id="ilg.gnumcueclipse.managedbuild.cross.riscv.tool.createlisting.1356766765" name="GNU RISC-V Cross Create Listing" superClass="ilg.gnumcueclipse.managedbuild.cross.riscv.tool.createlisting">
<option id="ilg.gnumcueclipse.managedbuild.cross.riscv.option.createlisting.source.2052761852" name="Display source (--source|-S)" superClass="ilg.gnumcueclipse.managedbuild.cross.riscv.option.createlisting.source" useByScannerDiscovery="false" value="true" valueType="boolean"/>
<option id="ilg.gnumcueclipse.managedbuild.cross.riscv.option.createlisting.allheaders.439659821" name="Display all headers (--all-headers|-x)" superClass="ilg.gnumcueclipse.managedbuild.cross.riscv.option.createlisting.allheaders" useByScannerDiscovery="false" value="true" valueType="boolean"/>
<option id="ilg.gnumcueclipse.managedbuild.cross.riscv.option.createlisting.demangle.67111865" name="Demangle names (--demangle|-C)" superClass="ilg.gnumcueclipse.managedbuild.cross.riscv.option.createlisting.demangle" useByScannerDiscovery="false" value="true" valueType="boolean"/>
<option id="ilg.gnumcueclipse.managedbuild.cross.riscv.option.createlisting.linenumbers.1549373929" name="Display line numbers (--line-numbers|-l)" superClass="ilg.gnumcueclipse.managedbuild.cross.riscv.option.createlisting.linenumbers" useByScannerDiscovery="false" value="true" valueType="boolean"/>
<option id="ilg.gnumcueclipse.managedbuild.cross.riscv.option.createlisting.wide.1298918921" name="Wide lines (--wide|-w)" superClass="ilg.gnumcueclipse.managedbuild.cross.riscv.option.createlisting.wide" useByScannerDiscovery="false" value="true" valueType="boolean"/>
<option id="ilg.gnumcueclipse.managedbuild.cross.riscv.option.createlisting.other.108640181" name="Other flags" superClass="ilg.gnumcueclipse.managedbuild.cross.riscv.option.createlisting.other" useByScannerDiscovery="false" value="" valueType="string"/>
<option id="ilg.gnumcueclipse.managedbuild.cross.riscv.option.createlisting.debugging.1148933762" name="Display debugging info (--debugging|-g)" superClass="ilg.gnumcueclipse.managedbuild.cross.riscv.option.createlisting.debugging" useByScannerDiscovery="false" value="false" valueType="boolean"/>
<option id="ilg.gnumcueclipse.managedbuild.cross.riscv.option.createlisting.symbols.1971725808" name="Display symbols (--syms|-t)" superClass="ilg.gnumcueclipse.managedbuild.cross.riscv.option.createlisting.symbols" useByScannerDiscovery="false" value="false" valueType="boolean"/>
</tool>
<tool id="ilg.gnumcueclipse.managedbuild.cross.riscv.tool.printsize.712424314" name="GNU RISC-V Cross Print Size" superClass="ilg.gnumcueclipse.managedbuild.cross.riscv.tool.printsize">
<option id="ilg.gnumcueclipse.managedbuild.cross.riscv.option.printsize.format.1404031980" name="Size format" superClass="ilg.gnumcueclipse.managedbuild.cross.riscv.option.printsize.format" useByScannerDiscovery="false"/>
</tool>
</toolChain>
</folderInfo>
<sourceEntries>
<entry excluding="LIB|app|services|HAL|SRC|src/ancsApp.h|src/ancsApp.c|Profile" flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="sourcePath" name=""/>
<entry flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="sourcePath" name="HAL"/>
<entry flags="VALUE_WORKSPACE_PATH" kind="sourcePath" name="LIB"/>
<entry excluding="Startup/startup_ch32v20x_D8.S|Startup/startup_ch32v20x_D6.S|Ld/" flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="sourcePath" name="SRC"/>
<entry flags="VALUE_WORKSPACE_PATH" kind="sourcePath" name="app"/>
<entry flags="VALUE_WORKSPACE_PATH" kind="sourcePath" name="services"/>
</sourceEntries>
</configuration>
</storageModule>
<storageModule moduleId="org.eclipse.cdt.core.externalSettings"/>
<storageModule moduleId="ilg.gnumcueclipse.managedbuild.packs"/>
</cconfiguration>
</storageModule>
<storageModule moduleId="cdtBuildSystem" version="4.0.0">
<project id="999.ilg.gnumcueclipse.managedbuild.cross.riscv.target.elf.275846018" projectType="ilg.gnumcueclipse.managedbuild.cross.riscv.target.elf"/>
</storageModule>
<storageModule moduleId="scannerConfiguration">
<autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId=""/>
<scannerConfigBuildInfo instanceId="ilg.gnumcueclipse.managedbuild.cross.riscv.config.elf.debug.767917625;ilg.gnumcueclipse.managedbuild.cross.riscv.config.elf.debug.767917625.;ilg.gnumcueclipse.managedbuild.cross.riscv.tool.c.compiler.1375371130;ilg.gnumcueclipse.managedbuild.cross.riscv.tool.c.compiler.input.1473381709">
<autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId=""/>
</scannerConfigBuildInfo>
<scannerConfigBuildInfo instanceId="ilg.gnumcueclipse.managedbuild.cross.riscv.config.elf.release.1008047074;ilg.gnumcueclipse.managedbuild.cross.riscv.config.elf.release.1008047074.;ilg.gnumcueclipse.managedbuild.cross.riscv.tool.c.compiler.1731377187;ilg.gnumcueclipse.managedbuild.cross.riscv.tool.c.compiler.input.2036806839">
<autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId=""/>
</scannerConfigBuildInfo>
</storageModule>
<storageModule moduleId="org.eclipse.cdt.core.LanguageSettingsProviders"/>
<storageModule moduleId="org.eclipse.cdt.make.core.buildtargets"/>
</cproject>
@@ -0,0 +1,54 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<projectDescription>
<name>peripheral_ancs_client</name>
<comment/>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.cdt.managedbuilder.core.genmakebuilder</name>
<triggers>clean,full,incremental,</triggers>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder</name>
<triggers>full,incremental,</triggers>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.cdt.core.cnature</nature>
<nature>org.eclipse.cdt.managedbuilder.core.managedBuildNature</nature>
<nature>org.eclipse.cdt.managedbuilder.core.ScannerConfigNature</nature>
</natures>
<linkedResources>
<link>
<name>HAL</name>
<type>2</type>
<locationURI>PARENT-1-PROJECT_LOC/HAL</locationURI>
</link>
<link>
<name>LIB</name>
<type>2</type>
<locationURI>PARENT-1-PROJECT_LOC/LIB</locationURI>
</link>
<link>
<name>SRC</name>
<type>2</type>
<locationURI>PARENT-2-PROJECT_LOC/SRC</locationURI>
</link>
</linkedResources>
<filteredResources>
<filter>
<id>1602468250279</id>
<name/>
<type>22</type>
<matcher>
<id>org.eclipse.ui.ide.multiFilter</id>
<arguments>1.0-name-matches-false-false-*.wvproj</arguments>
</matcher>
</filter>
</filteredResources>
</projectDescription>
@@ -0,0 +1,4 @@
eclipse.preferences.version=1
encoding//StdPeriphDriver/CH58x_sys.c=UTF-8
encoding//app/main.c=UTF-8
encoding//includes/sys/util.h=UTF-8
@@ -0,0 +1,909 @@
/*
* Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd.
*
* Attention: This software (modified or not) and binary are used for
* microcontroller manufactured by Nanjing Qinheng Microelectronics.
*/
/*********************************************************************
* INCLUDES
*/
#include "CONFIG.h"
#include "devinfoservice.h"
#include "ancs_client.h"
#include "ancs_client_internal.h"
#include "ancs.h"
/*********************************************************************
* MACROS
*/
// Length of bd addr as a string
#define B_ADDR_STR_LEN 15
/*********************************************************************
* CONSTANTS
*/
// What is the advertising interval when device is discoverable (units of 625us, 80=50ms)
#define DEFAULT_ADVERTISING_INTERVAL 80
// Minimum connection interval (units of 1.25ms, 6=7.5ms)
#define DEFAULT_DESIRED_MIN_CONN_INTERVAL 6
// Maximum connection interval (units of 1.25ms, 100=125ms)
#define DEFAULT_DESIRED_MAX_CONN_INTERVAL 100
// Slave latency to use parameter update
#define DEFAULT_DESIRED_SLAVE_LATENCY 0
// Minimum connection interval (units of 1.25ms)
#define DEFAULT_UPDATE_MIN_CONN_INTERVAL 24
// Maximum connection interval (units of 1.25ms)
#define DEFAULT_UPDATE_MAX_CONN_INTERVAL 40
// Slave latency to use parameter update
#define DEFAULT_UPDATE_SLAVE_LATENCY 0
// Supervision timeout value (units of 10ms)
#define DEFAULT_UPDATE_CONN_TIMEOUT 42
// Default parameter update delay in 0.625ms
#define DEFAULT_PARAM_UPDATE_DELAY 3200
// Default phy update delay in 0.625ms
#define DEFAULT_PHY_UPDATE_DELAY 2400
// ANCS Service: 7905F431-B5CE-4E99-A40F-4B1E122D00D0
#define ANCS_SVC_UUID 0xD0, 0x00, 0x2D, 0x12, 0x1E, 0x4B, 0x0F, 0xA4, 0x99, 0x4E, 0xCE, 0xB5, 0x31, 0xF4, 0x05, 0x79
// Notification Source: UUID 9FBF120D-6301-42D9-8C58-25E699A21DBD (notifiable)
#define ANCS_NOTIF_SRC_CHAR_UUID 0x1DBD // Last 2 bytes of the 128bit-16bytes UUID
// Control point: UUID 69D1D8F3-45E1-49A8-9821-9BBDFDAAD9D9 (writeable with response)
#define ANCS_CTRL_PT_CHAR_UUID 0xD9D9
// Data Source: UUID 22EAC6E9-24D6-4BB5-BE44-B36ACE7C7BFB (notifiable)
#define ANCS_DATA_SRC_CHAR_UUID 0x7BFB
#define CHAR_DESC_HDL_UUID128_LEN 21 // 5 + 16bytes = 21
/*********************************************************************
* TYPEDEFS
*/
/*********************************************************************
* GLOBAL VARIABLES
*/
uint16_t Ancs_handleCache[HDL_CACHE_LEN];
// Task ID for internal task/event processing
uint8_t ancs_taskid;
/*********************************************************************
* EXTERNAL VARIABLES
*/
/*********************************************************************
* EXTERNAL FUNCTIONS
*/
/*********************************************************************
* LOCAL VARIABLES
*/
struct bt_ancs_client ancs_c;
// GAP - SCAN RSP data (max size = 31 bytes)
static uint8_t scanRspData[] = {
// complete name
0x05, // length of this data
GAP_ADTYPE_LOCAL_NAME_COMPLETE,
'A',
'N',
'C',
'S',
// connection interval range
0x05, // length of this data
GAP_ADTYPE_SLAVE_CONN_INTERVAL_RANGE,
LO_UINT16(DEFAULT_DESIRED_MIN_CONN_INTERVAL), // 100ms
HI_UINT16(DEFAULT_DESIRED_MIN_CONN_INTERVAL),
LO_UINT16(DEFAULT_DESIRED_MAX_CONN_INTERVAL), // 1s
HI_UINT16(DEFAULT_DESIRED_MAX_CONN_INTERVAL),
// Tx power level
0x02, // length of this data
GAP_ADTYPE_POWER_LEVEL,
0 // 0dBm
};
// GAP - Advertisement data (max size = 31 bytes, though this is
// best kept short to conserve power while advertising)
static uint8_t advertData[] = {
// Flags; this sets the device to use limited discoverable
// mode (advertises for 30 seconds at a time) instead of general
// discoverable mode (advertises indefinitely)
0x02, // length of this data
GAP_ADTYPE_FLAGS,
GAP_ADTYPE_FLAGS_LIMITED | GAP_ADTYPE_FLAGS_BREDR_NOT_SUPPORTED,
// service UUID, to notify central devices what services are included
// in this peripheral
0x11, // length of this data
GAP_ADTYPE_SERVICES_LIST_128BIT, // some of the UUID's, but not all
ANCS_SVC_UUID,
0x05,
GAP_ADTYPE_LOCAL_NAME_COMPLETE,
'A', 'N', 'C', 'S',
};
// Connection handle of current connection
static peripheralConnItem_t ancs_conn_list = {
.connHandle = GAP_CONNHANDLE_INIT
};
static uint8_t discoveryState = DISC_ANCS_START;
// GAP GATT Attributes
static uint8_t attDeviceName[GAP_DEVICE_NAME_LEN] = "ANCS";
/*********************************************************************
* LOCAL FUNCTIONS
*/
uint16_t ANCS_ProcessEvent(uint8_t task_id, uint16_t events);
static void AncsProcessTMOSMsg(tmos_event_hdr_t *pMsg);
static void AncsApp_discoverService(gattMsgEvent_t *pMsg);
static void AncsStateNotificationCB(gapRole_States_t newState, gapRoleEvent_t *pEvent);
static void AncsParamUpdateCB(uint16_t connHandle, uint16_t connInterval,
uint16_t connSlaveLatency, uint16_t connTimeout);
static void AncsPasscodeCB(uint8_t *deviceAddr, uint16_t connectionHandle,
uint8_t uiInputs, uint8_t uiOutputs);
static void AncsPairStateCB(uint16_t connHandle, uint8_t state, uint8_t status);
/*********************************************************************
* PROFILE CALLBACKS
*/
// GAP Role Callbacks
static gapRolesCBs_t AncsPeripheralCBs = {
AncsStateNotificationCB, // Profile State Change Callbacks
NULL, // When a valid RSSI is read from controller (not used by application)
AncsParamUpdateCB
};
// Bond Manager Callbacks
static gapBondCBs_t AncsBondCB = {
AncsPasscodeCB,
AncsPairStateCB
};
static struct bt_ancs_subscribe_cb *bt_ancs_subscribe;
void ancs_subscribe_cb_register(struct bt_ancs_subscribe_cb *cb)
{
bt_ancs_subscribe = cb;
}
/*********************************************************************
* @fn peripheral_ancs_client_init
*
* @brief Initialization function for the Central App Task.
* This is called during initialization and should contain
* any application specific initialization (ie. hardware
* initialization/setup, table initialization, power up
* notification).
*
* @param task_id - the ID assigned by TMOS. This ID should be
* used to send messages and set timers.
*
* @return none
*/
void peripheral_ancs_client_init(void)
{
ancs_taskid = TMOS_ProcessEventRegister(ANCS_ProcessEvent);
// Setup the GAP Peripheral Role Profile
{
uint8_t initial_advertising_enable = TRUE;
uint16_t desired_min_interval = DEFAULT_DESIRED_MIN_CONN_INTERVAL;
uint16_t desired_max_interval = DEFAULT_DESIRED_MAX_CONN_INTERVAL;
// Set the GAP Role Parameters
GAPRole_SetParameter(GAPROLE_ADVERT_ENABLED, sizeof(uint8_t), &initial_advertising_enable);
GAPRole_SetParameter(GAPROLE_SCAN_RSP_DATA, sizeof(scanRspData), scanRspData);
GAPRole_SetParameter(GAPROLE_ADVERT_DATA, sizeof(advertData), advertData);
GAPRole_SetParameter(GAPROLE_MIN_CONN_INTERVAL, sizeof(uint16_t), &desired_min_interval);
GAPRole_SetParameter(GAPROLE_MAX_CONN_INTERVAL, sizeof(uint16_t), &desired_max_interval);
}
// Set the GAP Characteristics
GGS_SetParameter(GGS_DEVICE_NAME_ATT, GAP_DEVICE_NAME_LEN, attDeviceName);
{
uint16_t advInt = DEFAULT_ADVERTISING_INTERVAL;
// Set advertising interval
GAP_SetParamValue(TGAP_DISC_ADV_INT_MIN, advInt);
GAP_SetParamValue(TGAP_DISC_ADV_INT_MAX, advInt);
// Enable scan req notify
GAP_SetParamValue(TGAP_ADV_SCAN_REQ_NOTIFY, ENABLE);
}
// Setup the GAP Bond Manager
{
uint32_t passkey = 0; // passkey "000000"
uint8_t pairMode = GAPBOND_PAIRING_MODE_WAIT_FOR_REQ;
uint8_t mitm = TRUE;
uint8_t bonding = TRUE;
uint8_t ioCap = GAPBOND_IO_CAP_DISPLAY_ONLY;
GAPBondMgr_SetParameter(GAPBOND_PERI_DEFAULT_PASSCODE, sizeof(uint32_t), &passkey);
GAPBondMgr_SetParameter(GAPBOND_PERI_PAIRING_MODE, sizeof(uint8_t), &pairMode);
GAPBondMgr_SetParameter(GAPBOND_PERI_MITM_PROTECTION, sizeof(uint8_t), &mitm);
GAPBondMgr_SetParameter(GAPBOND_PERI_IO_CAPABILITIES, sizeof(uint8_t), &ioCap);
GAPBondMgr_SetParameter(GAPBOND_PERI_BONDING_ENABLED, sizeof(uint8_t), &bonding);
}
// Initialize GATT Client
GATT_InitClient();
// Register to receive incoming ATT Indications/Notifications
GATT_RegisterForInd(ancs_taskid);
// Initialize GATT attributes
GGS_AddService(GATT_ALL_SERVICES); // GAP
GATTServApp_AddService(GATT_ALL_SERVICES); // GATT attributes
DevInfo_AddService();
// Setup a delayed profile startup
tmos_set_event(ancs_taskid, START_DEVICE_EVT);
}
/*********************************************************************
* @fn Central_ProcessEvent
*
* @brief Central Application Task event processor. This function
* is called to process all events for the task. Events
* include timers, messages and any other user defined events.
*
* @param task_id - The TMOS assigned task ID.
* @param events - events to process. This is a bit map and can
* contain more than one event.
*
* @return events not processed
*/
uint16_t ANCS_ProcessEvent(uint8_t task_id, uint16_t events)
{
if(events & SYS_EVENT_MSG)
{
uint8_t *pMsg;
if((pMsg = tmos_msg_receive(ancs_taskid)) != NULL)
{
AncsProcessTMOSMsg((tmos_event_hdr_t *)pMsg);
// Release the TMOS message
tmos_msg_deallocate(pMsg);
}
// return unprocessed events
return (events ^ SYS_EVENT_MSG);
}
if(events & START_DEVICE_EVT)
{
// Start the Device
GAPRole_PeripheralStartDevice(ancs_taskid, &AncsBondCB, &AncsPeripheralCBs);
return (events ^ START_DEVICE_EVT);
}
if(events & START_SUBSCRIPE_NOTIDATA_EVT)
{
int err ;
if((err = bt_ancs_subscribe_data_source(&ancs_c,
bt_ancs_subscribe->data_source)) != SUCCESS){
if(err != -EALREADY)
tmos_start_task(ancs_taskid, START_SUBSCRIPE_NOTIDATA_EVT, MS1_TO_SYSTEM_TIME(100));
}
return (events ^ START_SUBSCRIPE_NOTIDATA_EVT);
}
if(events & START_SUBSCRIPE_NOTISRC_EVT)
{
int err ;
if((err = bt_ancs_subscribe_notification_source(&ancs_c,
bt_ancs_subscribe->notification_source)) != SUCCESS){
if(err != -EALREADY)
tmos_start_task(ancs_taskid, START_SUBSCRIPE_NOTISRC_EVT, MS1_TO_SYSTEM_TIME(100));
}
return (events ^ START_SUBSCRIPE_NOTISRC_EVT);
}
if(events & START_DISCOVER_EVT)
{
AncsApp_discoverService(NULL);
return (events ^ START_DISCOVER_EVT);
}
if(events & START_PARAM_UPDATE_EVT)
{
// start connect parameter update
GAPRole_PeripheralConnParamUpdateReq(ancs_conn_list.connHandle,
DEFAULT_UPDATE_MIN_CONN_INTERVAL,
DEFAULT_UPDATE_MAX_CONN_INTERVAL,
DEFAULT_UPDATE_SLAVE_LATENCY,
DEFAULT_UPDATE_CONN_TIMEOUT,
ancs_taskid);
return (events ^ START_PARAM_UPDATE_EVT);
}
if(events & START_PHY_UPDATE_EVT)
{
// start phy update
PRINT("PHY Update %x...\n", GAPRole_UpdatePHY(ancs_conn_list.connHandle, 0, GAP_PHY_BIT_LE_2M,
GAP_PHY_BIT_LE_2M, 0));
return (events ^ START_PHY_UPDATE_EVT);
}
// Discard unknown events
return 0;
}
/*********************************************************************
* @fn centralProcessGATTMsg
*
* @brief Process GATT messages
*
* @return none
*/
static void AncsProcessGATTMsg(gattMsgEvent_t *pMsg)
{
if(!atomic_test_bit(&ancs_c.state, ANCS_BT_CONNECTED)){
// In case a GATT message came after a connection has dropped,
// ignore the message
GATT_bm_free(&pMsg->msg, pMsg->method);
return;
}
if(!atomic_test_bit(&ancs_c.state, ANCS_BT_DISCOVERY_COMPLETE)){
AncsApp_discoverService(pMsg);
} else if (pMsg->method == ATT_HANDLE_VALUE_NOTI ||
pMsg->method == ATT_HANDLE_VALUE_IND) {
// Now in ancs_client.c
Ancs_handleNotification(pMsg);
} else if ((pMsg->method == ATT_READ_RSP) ||
((pMsg->method == ATT_ERROR_RSP) &&
(pMsg->msg.errorRsp.reqOpcode == ATT_READ_REQ))){
} else if((pMsg->method == ATT_WRITE_RSP) ||
((pMsg->method == ATT_ERROR_RSP) &&
(pMsg->msg.errorRsp.reqOpcode == ATT_WRITE_REQ))){
bt_ancs_write_cb write_cb;
write_cb = ancs_c.cp_write_cb;
atomic_clear_bit(&ancs_c.state, ANCS_CP_WRITE_PENDING);
if (write_cb) {
write_cb(&ancs_c, pMsg->msg.errorRsp.errCode);
}
} else if(pMsg->method == ATT_ERROR_RSP){
PRINT("response %#x error: %#x\n", pMsg->msg.errorRsp.reqOpcode,
pMsg->msg.errorRsp.errCode);
}
GATT_bm_free(&pMsg->msg, pMsg->method);
}
/*********************************************************************
* @fn central_ProcessTMOSMsg
*
* @brief Process an incoming task message.
*
* @param pMsg - message to process
*
* @return none
*/
static void AncsProcessTMOSMsg(tmos_event_hdr_t *pMsg)
{
switch(pMsg->event)
{
case GATT_MSG_EVENT:
AncsProcessGATTMsg((gattMsgEvent_t *)pMsg);
break;
}
}
/*********************************************************************
* @fn Peripheral_LinkEstablished
*
* @brief Process link established.
*
* @param pEvent - event to process
*
* @return none
*/
static void Peripheral_LinkEstablished(gapRoleEvent_t *pEvent)
{
gapEstLinkReqEvent_t *event = (gapEstLinkReqEvent_t *)pEvent;
// See if already connected
if(ancs_conn_list.connHandle != GAP_CONNHANDLE_INIT)
{
GAPRole_TerminateLink(pEvent->linkCmpl.connectionHandle);
PRINT("Connection max...\n");
}
else
{
ancs_conn_list.connHandle = event->connectionHandle;
ancs_conn_list.connInterval = event->connInterval;
ancs_conn_list.connSlaveLatency = event->connLatency;
ancs_conn_list.connTimeout = event->connTimeout;
ancs_c.conn = ancs_conn_list.connHandle;
atomic_set_bit(&ancs_c.state, ANCS_BT_CONNECTED);
// Set timer for param update event
tmos_start_task(ancs_taskid, START_PARAM_UPDATE_EVT, DEFAULT_PARAM_UPDATE_DELAY);
discoveryState = DISC_ANCS_START;
PRINT("Conn: %d - params: %d %d %d\n", event->connectionHandle,
event->connInterval, event->connLatency, event->connTimeout);
}
}
/*********************************************************************
* @fn Peripheral_LinkTerminated
*
* @brief Process link terminated.
*
* @param pEvent - event to process
*
* @return none
*/
static void Peripheral_LinkTerminated(gapRoleEvent_t *pEvent)
{
gapTerminateLinkEvent_t *event = (gapTerminateLinkEvent_t *)pEvent;
if(event->connectionHandle == ancs_conn_list.connHandle)
{
ancs_conn_list.connHandle = GAP_CONNHANDLE_INIT;
ancs_conn_list.connInterval = 0;
ancs_conn_list.connSlaveLatency = 0;
ancs_conn_list.connTimeout = 0;
// Restart advertising
{
uint8_t advertising_enable = TRUE;
GAPRole_SetParameter(GAPROLE_ADVERT_ENABLED, sizeof(uint8_t), &advertising_enable);
}
ancs_reinit(&ancs_c);
}
else
{
PRINT("ERR..\n");
}
}
static void AncsParamUpdateCB(uint16_t connHandle, uint16_t connInterval,
uint16_t connSlaveLatency, uint16_t connTimeout)
{
if(connHandle == ancs_conn_list.connHandle)
{
ancs_conn_list.connInterval = connInterval;
ancs_conn_list.connSlaveLatency = connSlaveLatency;
ancs_conn_list.connTimeout = connTimeout;
PRINT("update: %d - params: %d %d %d\n", connHandle,
connInterval, connSlaveLatency, connTimeout);
}
else
{
PRINT("ERR..\n");
}
}
static void AncsStateNotificationCB(gapRole_States_t newState, gapRoleEvent_t *pEvent)
{
switch(newState & GAPROLE_STATE_ADV_MASK)
{
case GAPROLE_STARTED:
PRINT("Initialized..\n");
break;
case GAPROLE_ADVERTISING:
if(pEvent->gap.opcode == GAP_LINK_TERMINATED_EVENT)
{
Peripheral_LinkTerminated(pEvent);
PRINT("Disconnected.. Reason:%x\n", pEvent->linkTerminate.reason);
PRINT("Advertising..\n");
}
else if(pEvent->gap.opcode == GAP_MAKE_DISCOVERABLE_DONE_EVENT)
{
PRINT("Advertising..\n");
}
break;
case GAPROLE_CONNECTED:
if(pEvent->gap.opcode == GAP_LINK_ESTABLISHED_EVENT)
{
Peripheral_LinkEstablished(pEvent);
PRINT("Connected..\n");
}
break;
case GAPROLE_CONNECTED_ADV:
if(pEvent->gap.opcode == GAP_MAKE_DISCOVERABLE_DONE_EVENT)
{
PRINT("Connected Advertising..\n");
}
break;
case GAPROLE_WAITING:
if(pEvent->gap.opcode == GAP_END_DISCOVERABLE_DONE_EVENT)
{
PRINT("Waiting for advertising..\n");
}
else if(pEvent->gap.opcode == GAP_LINK_TERMINATED_EVENT)
{
Peripheral_LinkTerminated(pEvent);
PRINT("Disconnected.. Reason:%x\n", pEvent->linkTerminate.reason);
}
else if(pEvent->gap.opcode == GAP_LINK_ESTABLISHED_EVENT)
{
if(pEvent->gap.hdr.status != SUCCESS)
{
PRINT("Waiting for advertising..\n");
}
else
{
PRINT("Error..\n");
}
}
else
{
PRINT("Error..%x\n", pEvent->gap.opcode);
}
break;
case GAPROLE_ERROR:
PRINT("Error..\n");
break;
default:
break;
}
}
/*********************************************************************
* @fn pairStateCB
*
* @brief Pairing state callback.
*
* @return none
*/
static void AncsPairStateCB(uint16_t connHandle, uint8_t state, uint8_t status)
{
if(state == GAPBOND_PAIRING_STATE_STARTED)
{
PRINT("Pairing started:%d\n", status);
}
else if(state == GAPBOND_PAIRING_STATE_COMPLETE)
{
if(status == SUCCESS)
{
PRINT("Pairing success\n");
}
else
{
PRINT("Pairing fail\n");
}
}
else if(state == GAPBOND_PAIRING_STATE_BONDED)
{
if(status == SUCCESS)
{
PRINT("Bonding success\n");
tmos_set_event(ancs_taskid, START_DISCOVER_EVT);
}
}
else if(state == GAPBOND_PAIRING_STATE_BOND_SAVED)
{
if(status == SUCCESS)
{
PRINT("Bond save success\n");
tmos_set_event(ancs_taskid, START_DISCOVER_EVT);
}
else
{
PRINT("Bond save failed: %d\n", status);
}
}
}
/*********************************************************************
* @fn centralPasscodeCB
*
* @brief Passcode callback.
*
* @return none
*/
static void AncsPasscodeCB(uint8_t *deviceAddr, uint16_t connectionHandle,
uint8_t uiInputs, uint8_t uiOutputs)
{
uint32_t passcode;
// Create random passcode
passcode = tmos_rand();
passcode %= 1000000;
// Display passcode to user
if(uiOutputs != 0)
{
PRINT("Passcode:%06d\n", (int)passcode);
}
// Send passcode response
GAPBondMgr_PasscodeRsp(connectionHandle, SUCCESS, passcode);
}
/*********************************************************************
* @fn centralGATTDiscoveryEvent
*
* @brief Process GATT discovery event
*
* @return none
*/
static void AncsApp_discoverService(gattMsgEvent_t *pMsg)
{
static uint16_t Ancs_svcStartHdl;
static uint16_t Ancs_svcEndHdl;
static uint8_t Ancs_endHdlIdx;
static uint8_t isNotifCCCD = FALSE;
switch (discoveryState)
{
case DISC_ANCS_START:
{
uint8_t uuid[ATT_UUID_SIZE] = {ANCS_SVC_UUID};
// Initialize service discovery variables
Ancs_svcStartHdl = Ancs_svcEndHdl = 0;
Ancs_endHdlIdx = 0;
// Discover ANCS service by UUID
GATT_DiscPrimaryServiceByUUID(ancs_c.conn, uuid,
ATT_UUID_SIZE, ancs_taskid);
discoveryState = DISC_ANCS_SVC;
}
break;
case DISC_ANCS_SVC:
// Service found, store handles
if (pMsg->method == ATT_FIND_BY_TYPE_VALUE_RSP &&
pMsg->msg.findByTypeValueRsp.numInfo > 0)
{
Ancs_svcStartHdl =
ATT_ATTR_HANDLE(pMsg->msg.findByTypeValueRsp.pHandlesInfo, 0);
Ancs_svcEndHdl =
ATT_GRP_END_HANDLE(pMsg->msg.findByTypeValueRsp.pHandlesInfo, 0);
}
// If procedure complete
if ((pMsg->method == ATT_FIND_BY_TYPE_VALUE_RSP &&
pMsg->hdr.status == bleProcedureComplete) ||
(pMsg->method == ATT_ERROR_RSP))
{
// If service found
if (Ancs_svcStartHdl != 0)
{
// Discover all characteristics
GATT_DiscAllChars(pMsg->connHandle, Ancs_svcStartHdl,
Ancs_svcEndHdl, ancs_taskid);
discoveryState = DISC_ANCS_CHAR;
}
else
{
// Service not found
discoveryState = DISC_FAILED;
PRINT("ANCS SVC not present\n");
}
}
break;
case DISC_ANCS_CHAR:
{
// Characteristics found, chache them
uint8_t *pHandleValuePairList;
uint16_t handle;
uint16_t uuid;
if (pMsg->method == ATT_READ_BY_TYPE_RSP &&
pMsg->msg.readByTypeRsp.numPairs > 0 &&
pMsg->msg.readByTypeRsp.len == CHAR_DESC_HDL_UUID128_LEN)
{
pHandleValuePairList = pMsg->msg.readByTypeRsp.pDataList;
uint8_t i;
// For each handle value pair in the list of chars in ANCS service
for (i = pMsg->msg.readByTypeRsp.numPairs; i > 0; i--)
{
// Parse characteristic declaration
handle = BUILD_UINT16(pHandleValuePairList[3],
pHandleValuePairList[4]);
uuid = BUILD_UINT16(pHandleValuePairList[5],
pHandleValuePairList[6]);
// If looking for end handle
if (Ancs_endHdlIdx != 0)
{
// End handle is characteristic declaration handle - 1
Ancs_handleCache[Ancs_endHdlIdx] =
BUILD_UINT16(pHandleValuePairList[0],
pHandleValuePairList[1]) - 1;
Ancs_endHdlIdx = 0;
}
// If UUID is of interest, cache handle
switch (uuid)
{
case ANCS_NOTIF_SRC_CHAR_UUID:
ancs_c.handle_ns = handle;
Ancs_handleCache[HDL_ANCS_NTF_NOTIF_START] = handle;
Ancs_endHdlIdx = HDL_ANCS_NTF_NOTIF_END;
break;
case ANCS_CTRL_PT_CHAR_UUID:
ancs_c.handle_cp = handle;
Ancs_handleCache[HDL_ANCS_CTRL_PT_START] = handle;
Ancs_endHdlIdx = HDL_ANCS_CTRL_PT_END;
break;
case ANCS_DATA_SRC_CHAR_UUID:
ancs_c.handle_ds = handle;
Ancs_handleCache[HDL_ANCS_DATA_SRC_START] = handle;
Ancs_endHdlIdx = HDL_ANCS_DATA_SRC_END;
break;
default:
break;
}
pHandleValuePairList += CHAR_DESC_HDL_UUID128_LEN;
}
}
// If procedure complete
if ((pMsg->method == ATT_READ_BY_TYPE_RSP &&
pMsg->hdr.status == bleProcedureComplete) ||
(pMsg->method == ATT_ERROR_RSP))
{
// Special case of end handle at end of service
if (Ancs_endHdlIdx != 0)
{
Ancs_handleCache[Ancs_endHdlIdx] = Ancs_svcEndHdl;
Ancs_endHdlIdx = 0;
}
// If notification source char is missing, there is something wrong
if (Ancs_handleCache[HDL_ANCS_NTF_NOTIF_START] == 0)
{
PRINT("Notif src not found\n");
discoveryState = DISC_FAILED;
}
else if (Ancs_handleCache[HDL_ANCS_NTF_NOTIF_START] <
Ancs_handleCache[HDL_ANCS_NTF_NOTIF_END])
{
// Discover ANCS Notification Source CCCD
GATT_DiscAllCharDescs(pMsg->connHandle,
Ancs_handleCache[HDL_ANCS_NTF_NOTIF_START] + 1,
Ancs_handleCache[HDL_ANCS_NTF_NOTIF_END],
ancs_taskid);
isNotifCCCD = TRUE;
discoveryState = DISC_ANCS_CCCD;
}
else
{
PRINT("CCCD missing\n");
// Missing required characteristic descriptor
Ancs_handleCache[HDL_ANCS_NTF_NOTIF_START] = 0;
discoveryState = DISC_FAILED;
}
}
// Discover all characteristics of ANCS service until end handle
else
{
GATT_DiscAllChars(pMsg->connHandle, handle+1,
Ancs_svcEndHdl, ancs_taskid);
}
}
break;
case DISC_ANCS_CCCD:
{
// Characteristic descriptors found
if (pMsg->method == ATT_FIND_INFO_RSP &&
pMsg->msg.findInfoRsp.numInfo > 0 &&
pMsg->msg.findInfoRsp.format == 0x01)
{
uint8_t i;
// For each handle/uuid pair
for (i = 0; i < pMsg->msg.findInfoRsp.numInfo; i++)
{
// Look for CCCD
if (ATT_BT_PAIR_UUID(pMsg->msg.findInfoRsp.pInfo, i) ==
GATT_CLIENT_CHAR_CFG_UUID)
{
// CCCD found
// if it is Notification Source CCCD
if (isNotifCCCD == TRUE)
{
ancs_c.handle_ns_ccc =
ATT_BT_PAIR_HANDLE(pMsg->msg.findInfoRsp.pInfo, i);
Ancs_handleCache[HDL_ANCS_NTF_CCCD] =
ATT_BT_PAIR_HANDLE(pMsg->msg.findInfoRsp.pInfo, i);
}
// else it is Data Source CCCD
else
{
ancs_c.handle_ds_ccc =
ATT_BT_PAIR_HANDLE(pMsg->msg.findInfoRsp.pInfo, i);
Ancs_handleCache[HDL_ANCS_DATA_SRC_CCCD] =
ATT_BT_PAIR_HANDLE(pMsg->msg.findInfoRsp.pInfo, i);
}
break;
}
}
}
// If procedure complete
if ((pMsg->method == ATT_FIND_INFO_RSP &&
pMsg->hdr.status == bleProcedureComplete) ||
(pMsg->method == ATT_ERROR_RSP))
{
// Discover ANCS Data Source characteristic descriptors
if (isNotifCCCD == TRUE &&
Ancs_handleCache[HDL_ANCS_DATA_SRC_CCCD] == 0)
{
GATT_DiscAllCharDescs(pMsg->connHandle,
Ancs_handleCache[HDL_ANCS_DATA_SRC_START] + 1,
Ancs_handleCache[HDL_ANCS_DATA_SRC_END],
ancs_taskid);
isNotifCCCD = FALSE;
}
else
{
discoveryState = DISC_IDLE;
atomic_set_bit(&ancs_c.state, ANCS_BT_DISCOVERY_COMPLETE);
PRINT("discover complete\n");
tmos_set_event(ancs_taskid, START_SUBSCRIPE_NOTIDATA_EVT);
tmos_set_event(ancs_taskid, START_SUBSCRIPE_NOTISRC_EVT);
}
}
}
break;
default:
break;
}
}
/************************ endfile @ central **************************/
@@ -0,0 +1,74 @@
/********************************** (C) COPYRIGHT *******************************
* File Name : ch32v20x_it.c
* Author : WCH
* Version : V1.0.0
* Date : 2022/06/16
* Description : Main Interrupt Service Routines.
*********************************************************************************
* Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd.
* Attention: This software (modified or not) and binary are used for
* microcontroller manufactured by Nanjing Qinheng Microelectronics.
*******************************************************************************/
/*********************************************************************
* INCLUDES
*/
#include "ch32v20x_it.h"
#include "CONFIG.h"
/*********************************************************************
* LOCAL FUNCTIONS
*/
void NMI_Handler(void) __attribute__((interrupt("WCH-Interrupt-fast")));
void HardFault_Handler(void) __attribute__((interrupt("WCH-Interrupt-fast")));
void BB_IRQHandler(void) __attribute__((interrupt("WCH-Interrupt-fast")));
void LLE_IRQHandler(void) __attribute__((interrupt("WCH-Interrupt-fast")));
/*********************************************************************
* @fn NMI_Handler
*
* @brief This function handles NMI exception.
*
* @return None
*/
void NMI_Handler(void)
{
}
/*********************************************************************
* @fn HardFault_Handler
*
* @brief This function handles Hard Fault exception.
*
* @return None
*/
void HardFault_Handler(void)
{
while(1)
{
}
}
/*********************************************************************
* @fn BB_IRQHandler
*
* @brief BB Interrupt for BLE.
*
* @return None
*/
void BB_IRQHandler(void)
{
BB_IRQLibHandler();
}
/*********************************************************************
* @fn LLE_IRQHandler
*
* @brief LLE Interrupt for BLE.
*
* @return None
*/
void LLE_IRQHandler(void)
{
LLE_IRQLibHandler();
}
@@ -0,0 +1,135 @@
/*
* Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd.
*
* Attention: This software (modified or not) and binary are used for
* microcontroller manufactured by Nanjing Qinheng Microelectronics.
*/
#ifndef ANCS_H
#define ANCS_H
#ifdef __cplusplus
extern "C"
{
#endif
/*********************************************************************
* INCLUDES
*/
#include "config.h"
#include "ancs_client.h"
#include <stdint.h>
/*********************************************************************
* CONSTANTS
*/
// ANCS discovery states
enum
{
DISC_IDLE = 0x00, // Idle state
DISC_ANCS_START = 0x10, // ANCS service
DISC_ANCS_SVC, // Discover service
DISC_ANCS_CHAR, // Discover all characteristics
DISC_ANCS_CCCD, // Discover ANCS CCCD
DISC_FAILED = 0xFF // Discovery failed
};
// ANCS handle cache indexes
enum
{
HDL_ANCS_NTF_NOTIF_START, // ANCS notification characteristic start handle
HDL_ANCS_NTF_NOTIF_END, // ANCS notification characteristic end handle
HDL_ANCS_NTF_CCCD, // ANCS notification CCCD
HDL_ANCS_CTRL_PT_START,
HDL_ANCS_CTRL_PT_END,
HDL_ANCS_DATA_SRC_START, // ANCS data source characteristic start handle
HDL_ANCS_DATA_SRC_END, // ANCS data source characteristic end handle
HDL_ANCS_DATA_SRC_CCCD, // ANCS data source CCCD
HDL_CACHE_LEN
};
/*********************************************************************
* TYPEDEFS
*/
struct bt_ancs_subscribe_cb {
void(*notification_source)(struct bt_ancs_client *ancs_c,
int err, const struct bt_ancs_evt_notif *notif);
void(*data_source)(struct bt_ancs_client *ancs_c,
const struct bt_ancs_attr_response *response);
void(*write_response)(struct bt_ancs_client *ancs_c,
uint8_t err);
};
typedef struct
{
uint16_t connHandle; // Connection handle of current connection
uint16_t connInterval;
uint16_t connSlaveLatency;
uint16_t connTimeout;
} peripheralConnItem_t;
/*********************************************************************
* MACROS
*/
#define START_DEVICE_EVT 0x0001
#define START_SUBSCRIPE_NOTISRC_EVT 0x0002
#define START_SUBSCRIPE_NOTIDATA_EVT 0x0004
#define START_DISCOVER_EVT 0x0008
#define START_PARAM_UPDATE_EVT 0x0010
#define START_PHY_UPDATE_EVT 0x0020
// Handle and 16-bit Bluetooth UUID pair indexes
#define ATT_BT_PAIR_HANDLE_IDX( i ) ( (i) * (2 + ATT_BT_UUID_SIZE) )
#define ATT_BT_PAIR_UUID_IDX( i ) ( ATT_BT_PAIR_HANDLE_IDX( (i) ) + 2 )
#define ATT_BT_PAIR_HANDLE( info, i ) ( BUILD_UINT16( (info)[ATT_BT_PAIR_HANDLE_IDX((i))], \
(info)[ATT_BT_PAIR_HANDLE_IDX((i))+1] ) )
#define ATT_BT_PAIR_UUID( info, i ) ( BUILD_UINT16( (info)[ATT_BT_PAIR_UUID_IDX((i))], \
(info)[ATT_BT_PAIR_UUID_IDX((i))+1] ) )
/*********************************************************************
* GLOBAL
*/
enum
{
CCCD_CONFIG_NOTIF = 0x00,
CCCD_CONFIG_DATA,
CCCD_CONFIG_DONE
};
extern struct bt_ancs_client ancs_c;
extern uint8_t ancs_taskid;
// Handle cache
extern uint16_t Ancs_handleCache[HDL_CACHE_LEN];
/*********************************************************************
* FUNCTIONS
*/
/*
* ANCS service discovery functions.
*/
void peripheral_ancs_client_init(void);
void ancs_subscribe_cb_register(struct bt_ancs_subscribe_cb *cb);
/*********************************************************************
*********************************************************************/
#ifdef __cplusplus
}
#endif
#endif /* ANCS_H */
@@ -0,0 +1,37 @@
/********************************** (C) COPYRIGHT *******************************
* File Name : ch32v20x_conf.h
* Author : WCH
* Version : V1.0.0
* Date : 2022/06/16
* Description : Library configuration file.
*********************************************************************************
* Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd.
* Attention: This software (modified or not) and binary are used for
* microcontroller manufactured by Nanjing Qinheng Microelectronics.
*******************************************************************************/
#ifndef __CH32V20x_CONF_H
#define __CH32V20x_CONF_H
#include "ch32v20x_adc.h"
#include "ch32v20x_bkp.h"
#include "ch32v20x_can.h"
#include "ch32v20x_crc.h"
#include "ch32v20x_dbgmcu.h"
#include "ch32v20x_dma.h"
#include "ch32v20x_exti.h"
#include "ch32v20x_flash.h"
#include "ch32v20x_gpio.h"
#include "ch32v20x_i2c.h"
#include "ch32v20x_iwdg.h"
#include "ch32v20x_pwr.h"
#include "ch32v20x_rcc.h"
#include "ch32v20x_rtc.h"
#include "ch32v20x_spi.h"
#include "ch32v20x_tim.h"
#include "ch32v20x_usart.h"
#include "ch32v20x_wwdg.h"
#include "ch32v20x_it.h"
#include "ch32v20x_misc.h"
#endif /* __CH32V20x_CONF_H */
@@ -0,0 +1,18 @@
/********************************** (C) COPYRIGHT *******************************
* File Name : ch32v20x_it.h
* Author : WCH
* Version : V1.0.0
* Date : 2022/06/16
* Description : This file contains the headers of the interrupt handlers.
*********************************************************************************
* Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd.
* Attention: This software (modified or not) and binary are used for
* microcontroller manufactured by Nanjing Qinheng Microelectronics.
*******************************************************************************/
#ifndef __CH32V20x_IT_H
#define __CH32V20x_IT_H
#include "debug.h"
#endif /* __CH32V20x_IT_H */
@@ -0,0 +1,31 @@
/********************************** (C) COPYRIGHT *******************************
* File Name : system_ch32v20x.h
* Author : WCH
* Version : V1.0.0
* Date : 2022/06/16
* Description : CH32V20x Device Peripheral Access Layer System Header File.
*********************************************************************************
* Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd.
* Attention: This software (modified or not) and binary are used for
* microcontroller manufactured by Nanjing Qinheng Microelectronics.
*******************************************************************************/
#ifndef __SYSTEM_ch32v20x_H
#define __SYSTEM_ch32v20x_H
#ifdef __cplusplus
extern "C" {
#endif
extern uint32_t SystemCoreClock; /* System Clock Frequency (Core Clock) */
/* System_Exported_Functions */
extern void SystemInit(void);
extern void SystemCoreClockUpdate(void);
#ifdef __cplusplus
}
#endif
#endif /*__CH32V20x_SYSTEM_H */
@@ -0,0 +1,32 @@
#ifndef TIME_UNITS_H
#define TIME_UNITS_H
#include "config.h"
#ifdef CLK_OSC32K
#if (CLK_OSC32K==1)
#define FREQ_RTC 32000
#else
#define FREQ_RTC 32768
#endif
#endif /* CLK_OSC32K */
#define CLK_PER_US (1.0 / ((1.0 / FREQ_RTC) * 1000 * 1000)) // 0.032 clk/us
#define CLK_PER_MS (CLK_PER_US * 1000) // 32clk/ms
#define US_PER_CLK (1.0 / CLK_PER_US) // 31.25us/clk
#define MS_PER_CLK (US_PER_CLK / 1000.0) // 0.03125ms/clk
#define SYS_TO_US(clk) ((uint32_t)((clk) * US_PER_CLK))
#define SYS_TO_MS(clk) ((uint32_t)((clk) * MS_PER_CLK))
#define US_TO_SYS(us) ((uint32_t)((us) * CLK_PER_US))
#define MS_TO_SYS(ms) ((uint32_t)((ms) * CLK_PER_MS ))
static inline uint32_t get_current_time(void)
{
return SYS_TO_MS(RTC_GetCounter());
}
#endif /* TIME_UNITS_H */
@@ -0,0 +1,413 @@
/********************************** (C) COPYRIGHT *******************************
* File Name : main.c
* Author : WCH
* Version : V1.1
* Date : 2020/08/06
* Description :
*********************************************************************************
* Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd.
* Attention: This software (modified or not) and binary are used for
* microcontroller manufactured by Nanjing Qinheng Microelectronics.
*******************************************************************************/
/******************************************************************************/
#include <time_units.h>
#include "CONFIG.h"
#include "HAL.h"
#include "ancs.h"
#include "ancs_client.h"
#include "time_units.h"
static void bt_ancs_notification_source_handler(struct bt_ancs_client *ancs_c,
int err, const struct bt_ancs_evt_notif *notif);
static void bt_ancs_data_source_handler(struct bt_ancs_client *ancs_c,
const struct bt_ancs_attr_response *response);
static void bt_ancs_write_response_handler(struct bt_ancs_client *ancs_c,
uint8_t err);
__attribute__((aligned(4))) uint32_t MEM_BUF[BLE_MEMHEAP_SIZE / 4];
#if(defined(BLE_MAC)) && (BLE_MAC == TRUE)
const uint8_t MacAddr[6] = {0x84, 0xC2, 0xE4, 0x03, 0x02, 0x02};
#endif
#define ATTR_DATA_SIZE BT_ANCS_ATTR_DATA_MAX
#define KEY_REQ_NOTI_ATTR (1<<0)
#define KEY_REQ_APP_ATTR (1<<1)
#define KEY_POS_ACTION (1<<0)
#define KEY_NEG_ACTION (1<<0)
static struct bt_ancs_subscribe_cb subscribe = {
.data_source = bt_ancs_data_source_handler,
.notification_source = bt_ancs_notification_source_handler,
.write_response = bt_ancs_write_response_handler,
};
/* Local copy to keep track of the newest arriving notifications. */
static struct bt_ancs_evt_notif notification_latest;
/* Local copy of the newest notification attribute. */
static struct bt_ancs_attr notif_attr_latest;
/* Local copy of the newest app attribute. */
static struct bt_ancs_attr notif_attr_app_id_latest;
/* Buffers to store attribute data. */
static uint8_t attr_appid[ATTR_DATA_SIZE];
static uint8_t attr_title[ATTR_DATA_SIZE];
static uint8_t attr_subtitle[ATTR_DATA_SIZE];
static uint8_t attr_message[ATTR_DATA_SIZE];
static uint8_t attr_message_size[ATTR_DATA_SIZE];
static uint8_t attr_date[ATTR_DATA_SIZE];
static uint8_t attr_posaction[ATTR_DATA_SIZE];
static uint8_t attr_negaction[ATTR_DATA_SIZE];
static uint8_t attr_disp_name[ATTR_DATA_SIZE];
/* String literals for the iOS notification categories.
* Used then printing to UART.
*/
static const char *lit_catid[BT_ANCS_CATEGORY_ID_COUNT] = {
"Other",
"Incoming Call",
"Missed Call",
"Voice Mail",
"Social",
"Schedule",
"Email",
"News",
"Health And Fitness",
"Business And Finance",
"Location",
"Entertainment"
};
/* String literals for the iOS notification event types.
* Used then printing to UART.
*/
static const char *lit_eventid[BT_ANCS_EVT_ID_COUNT] = { "Added",
"Modified",
"Removed" };
/* String literals for the iOS notification attribute types.
* Used when printing to UART.
*/
static const char *lit_attrid[BT_ANCS_NOTIF_ATTR_COUNT] = {
"App Identifier",
"Title",
"Subtitle",
"Message",
"Message Size",
"Date",
"Positive Action Label",
"Negative Action Label"
};
static const char *lit_appid[BT_ANCS_APP_ATTR_COUNT] = { "Display Name" };
static void notif_print(struct bt_ancs_evt_notif *notif)
{
PRINT("\nNotification\n");
PRINT("Event: %s\n", lit_eventid[notif->evt_id]);
PRINT("Category ID: %s\n", lit_catid[notif->category_id]);
PRINT("Category Cnt:%u\n", (unsigned int)notif->category_count);
PRINT("UID: %u\n", (unsigned int)notif->notif_uid);
PRINT("Flags:\n");
if (notif->evt_flags.silent) {
PRINT(" Silent\n");
}
if (notif->evt_flags.important) {
PRINT(" Important\n");
}
if (notif->evt_flags.pre_existing) {
PRINT(" Pre-existing\n");
}
if (notif->evt_flags.positive_action) {
PRINT(" Positive Action\n");
}
if (notif->evt_flags.negative_action) {
PRINT(" Negative Action\n");
}
}
/**@brief Function for printing iOS notification attribute data.
*
* @param[in] attr Pointer to an iOS notification attribute.
*/
static void notif_attr_print(const struct bt_ancs_attr *attr)
{
if (attr->attr_len != 0) {
PRINT("%s: %s\n", lit_attrid[attr->attr_id],
(char *)attr->attr_data);
} else if (attr->attr_len == 0) {
PRINT("%s: (N/A)\n", lit_attrid[attr->attr_id]);
}
}
/**@brief Function for printing iOS notification attribute data.
*
* @param[in] attr Pointer to an iOS App attribute.
*/
static void app_attr_print(const struct bt_ancs_attr *attr)
{
if (attr->attr_len != 0) {
PRINT("%s: %s\n", lit_appid[attr->attr_id],
(char *)attr->attr_data);
} else if (attr->attr_len == 0) {
PRINT("%s: (N/A)\n", lit_appid[attr->attr_id]);
}
}
static int ancs_init(void)
{
int err;
err = bt_ancs_client_init(&ancs_c);
if (err) {
return err;
}
err = bt_ancs_register_attr(&ancs_c,
BT_ANCS_NOTIF_ATTR_ID_APP_IDENTIFIER,
attr_appid, ATTR_DATA_SIZE);
if (err) {
return err;
}
err = bt_ancs_register_app_attr(&ancs_c,
BT_ANCS_APP_ATTR_ID_DISPLAY_NAME,
attr_disp_name, sizeof(attr_disp_name));
if (err) {
return err;
}
err = bt_ancs_register_attr(&ancs_c,
BT_ANCS_NOTIF_ATTR_ID_TITLE,
attr_title, ATTR_DATA_SIZE);
if (err) {
return err;
}
err = bt_ancs_register_attr(&ancs_c,
BT_ANCS_NOTIF_ATTR_ID_MESSAGE,
attr_message, ATTR_DATA_SIZE);
if (err) {
return err;
}
err = bt_ancs_register_attr(&ancs_c,
BT_ANCS_NOTIF_ATTR_ID_SUBTITLE,
attr_subtitle, ATTR_DATA_SIZE);
if (err) {
return err;
}
err = bt_ancs_register_attr(&ancs_c,
BT_ANCS_NOTIF_ATTR_ID_MESSAGE_SIZE,
attr_message_size, ATTR_DATA_SIZE);
if (err) {
return err;
}
err = bt_ancs_register_attr(&ancs_c,
BT_ANCS_NOTIF_ATTR_ID_DATE,
attr_date, ATTR_DATA_SIZE);
if (err) {
return err;
}
err = bt_ancs_register_attr(&ancs_c,
BT_ANCS_NOTIF_ATTR_ID_POSITIVE_ACTION_LABEL,
attr_posaction, ATTR_DATA_SIZE);
if (err) {
return err;
}
err = bt_ancs_register_attr(&ancs_c,
BT_ANCS_NOTIF_ATTR_ID_NEGATIVE_ACTION_LABEL,
attr_negaction, ATTR_DATA_SIZE);
return err;
}
static void bt_ancs_notification_source_handler(struct bt_ancs_client *ancs_c,
int err, const struct bt_ancs_evt_notif *notif)
{
if (!err) {
notification_latest = *notif;
notif_print(&notification_latest);
}
}
static void bt_ancs_data_source_handler(struct bt_ancs_client *ancs_c,
const struct bt_ancs_attr_response *response)
{
switch (response->command_id) {
case BT_ANCS_COMMAND_ID_GET_NOTIF_ATTRIBUTES:
notif_attr_latest = response->attr;
notif_attr_print(&notif_attr_latest);
if (response->attr.attr_id ==
BT_ANCS_NOTIF_ATTR_ID_APP_IDENTIFIER) {
notif_attr_app_id_latest = response->attr;
}
break;
case BT_ANCS_COMMAND_ID_GET_APP_ATTRIBUTES:
app_attr_print(&response->attr);
break;
default:
/* No implementation needed. */
break;
}
}
static void bt_ancs_write_response_handler(struct bt_ancs_client *ancs_c,
uint8_t err)
{
switch (err) {
case BT_ATT_ERR_ANCS_NP_UNKNOWN_COMMAND:
PRINT("Error: Command ID was not recognized by the Notification Provider.\n");
break;
case BT_ATT_ERR_ANCS_NP_INVALID_COMMAND:
PRINT("Error: Command failed to be parsed on the Notification Provider.\n");
break;
case BT_ATT_ERR_ANCS_NP_INVALID_PARAMETER:
PRINT("Error: Parameter does not refer to an existing object on the Notification Provider.\n");
break;
case BT_ATT_ERR_ANCS_NP_ACTION_FAILED:
PRINT("Error: Perform Notification Action Failed on the Notification Provider.\n");
break;
default:
break;
}
}
struct key_state {
uint32_t timestamp_key1;
uint32_t timestamp_key2;
};
/**
* key1(PB22):
* single: request attributes for a notification.
* double: request attributes for a given app.
*
* key2(PB4):
* single: perform a positive action.
* double: perform a negative action.
*/
void button_changed(uint8_t keys)
{
int err;
static struct key_state k_s = {0};
if(keys & HAL_KEY_SW_1){
uint32_t current_time = get_current_time();
if(current_time - k_s.timestamp_key1 < 600){
if (notif_attr_app_id_latest.attr_id ==
BT_ANCS_NOTIF_ATTR_ID_APP_IDENTIFIER &&
notif_attr_app_id_latest.attr_len != 0) {
PRINT("Request for %s:\n",
notif_attr_app_id_latest.attr_data);
err = bt_ancs_request_app_attr(
&ancs_c, notif_attr_app_id_latest.attr_data,
notif_attr_app_id_latest.attr_len,
bt_ancs_write_response_handler);
if (err) {
PRINT("Failed requesting attributes for a given app (err: %d)\n",
err);
}
}
} else {
err = bt_ancs_request_attrs(&ancs_c, &notification_latest,
bt_ancs_write_response_handler);
if(err){
PRINT("Failed requesting attributes for a notification (err: %d)\n",
err);
}
}
k_s.timestamp_key1 = current_time;
}
if(keys & HAL_KEY_SW_2){
uint32_t current_time = get_current_time();
if(current_time - k_s.timestamp_key2 < 600){
PRINT("Performing Negative Action.\n");
err = bt_ancs_notification_action(
&ancs_c, notification_latest.notif_uid,
BT_ANCS_ACTION_ID_NEGATIVE,
bt_ancs_write_response_handler);
if (err) {
PRINT("Failed performing action (err: %d)\n",
err);
}
} else {
PRINT("Performing Positive Action.\n");
err = bt_ancs_notification_action(
&ancs_c, notification_latest.notif_uid,
BT_ANCS_ACTION_ID_POSITIVE,
bt_ancs_write_response_handler);
if (err) {
PRINT("Failed performing action (err: %d)\n",
err);
}
}
k_s.timestamp_key2 = current_time;
}
}
/*********************************************************************
* @fn Main_Circulation
*
* @brief Main loop
*
* @return none
*/
__attribute__((section(".highcode")))
__attribute__((noinline))
void Main_Circulation(void)
{
while(1)
{
TMOS_SystemProcess();
}
}
/*********************************************************************
* @fn main
*
* @brief Main function
*
* @return none
*/
int main(void)
{
Delay_Init();
#ifdef DEBUG
USART_Printf_Init( 115200 );
#endif
PRINT("start.%s\n",__TIME__);
PRINT("%s\n", VER_LIB);
WCHBLE_Init();
HAL_Init();
HalKeyConfig(button_changed);
ancs_init();
ancs_subscribe_cb_register(&subscribe);
GAPRole_PeripheralInit();
peripheral_ancs_client_init();
Main_Circulation();
}
/******************************** endfile @ main ******************************/
@@ -0,0 +1,989 @@
/********************************** (C) COPYRIGHT *******************************
* File Name : system_ch32v20x.c
* Author : WCH
* Version : V1.0.0
* Date : 2021/06/06
* Description : CH32V20x Device Peripheral Access Layer System Source File.
* For HSE = 32Mhz (CH32V208x/CH32V203RBT6)
* For HSE = 8Mhz (other CH32V203x)
*********************************************************************************
* Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd.
* Attention: This software (modified or not) and binary are used for
* microcontroller manufactured by Nanjing Qinheng Microelectronics.
*******************************************************************************/
#include "ch32v20x.h"
/*
* Uncomment the line corresponding to the desired System clock (SYSCLK) frequency (after
* reset the HSI is used as SYSCLK source).
* If none of the define below is enabled, the HSI is used as System clock source.
*/
//#define SYSCLK_FREQ_HSE HSE_VALUE
//#define SYSCLK_FREQ_48MHz_HSE 48000000
//#define SYSCLK_FREQ_56MHz_HSE 56000000
//#define SYSCLK_FREQ_72MHz_HSE 72000000
#define SYSCLK_FREQ_96MHz_HSE 96000000
//#define SYSCLK_FREQ_120MHz_HSE 120000000
//#define SYSCLK_FREQ_144MHz_HSE 144000000
//#define SYSCLK_FREQ_HSI HSI_VALUE
//#define SYSCLK_FREQ_48MHz_HSI 48000000
//#define SYSCLK_FREQ_56MHz_HSI 56000000
//#define SYSCLK_FREQ_72MHz_HSI 72000000
//#define SYSCLK_FREQ_96MHz_HSI 96000000
//#define SYSCLK_FREQ_120MHz_HSI 120000000
//#define SYSCLK_FREQ_144MHz_HSI 144000000
/* Clock Definitions */
#ifdef SYSCLK_FREQ_HSE
uint32_t SystemCoreClock = SYSCLK_FREQ_HSE; /* System Clock Frequency (Core Clock) */
#elif defined SYSCLK_FREQ_48MHz_HSE
uint32_t SystemCoreClock = SYSCLK_FREQ_48MHz_HSE; /* System Clock Frequency (Core Clock) */
#elif defined SYSCLK_FREQ_56MHz_HSE
uint32_t SystemCoreClock = SYSCLK_FREQ_56MHz_HSE; /* System Clock Frequency (Core Clock) */
#elif defined SYSCLK_FREQ_72MHz_HSE
uint32_t SystemCoreClock = SYSCLK_FREQ_72MHz_HSE; /* System Clock Frequency (Core Clock) */
#elif defined SYSCLK_FREQ_96MHz_HSE
uint32_t SystemCoreClock = SYSCLK_FREQ_96MHz_HSE; /* System Clock Frequency (Core Clock) */
#elif defined SYSCLK_FREQ_120MHz_HSE
uint32_t SystemCoreClock = SYSCLK_FREQ_120MHz_HSE; /* System Clock Frequency (Core Clock) */
#elif defined SYSCLK_FREQ_144MHz_HSE
uint32_t SystemCoreClock = SYSCLK_FREQ_144MHz_HSE; /* System Clock Frequency (Core Clock) */
#elif defined SYSCLK_FREQ_48MHz_HSI
uint32_t SystemCoreClock = SYSCLK_FREQ_48MHz_HSI; /* System Clock Frequency (Core Clock) */
#elif defined SYSCLK_FREQ_56MHz_HSI
uint32_t SystemCoreClock = SYSCLK_FREQ_56MHz_HSI; /* System Clock Frequency (Core Clock) */
#elif defined SYSCLK_FREQ_72MHz_HSI
uint32_t SystemCoreClock = SYSCLK_FREQ_72MHz_HSI; /* System Clock Frequency (Core Clock) */
#elif defined SYSCLK_FREQ_96MHz_HSI
uint32_t SystemCoreClock = SYSCLK_FREQ_96MHz_HSI; /* System Clock Frequency (Core Clock) */
#elif defined SYSCLK_FREQ_120MHz_HSI
uint32_t SystemCoreClock = SYSCLK_FREQ_120MHz_HSI; /* System Clock Frequency (Core Clock) */
#elif defined SYSCLK_FREQ_144MHz_HSI
uint32_t SystemCoreClock = SYSCLK_FREQ_144MHz_HSI; /* System Clock Frequency (Core Clock) */
#else
uint32_t SystemCoreClock = HSI_VALUE; /* System Clock Frequency (Core Clock) */
#endif
__I uint8_t AHBPrescTable[16] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 6, 7, 8, 9};
/* system_private_function_proto_types */
static void SetSysClock(void);
#ifdef SYSCLK_FREQ_HSE
static void SetSysClockToHSE( void );
#elif defined SYSCLK_FREQ_48MHz_HSE
static void SetSysClockTo48_HSE( void );
#elif defined SYSCLK_FREQ_56MHz_HSE
static void SetSysClockTo56_HSE( void );
#elif defined SYSCLK_FREQ_72MHz_HSE
static void SetSysClockTo72_HSE( void );
#elif defined SYSCLK_FREQ_96MHz_HSE
static void SetSysClockTo96_HSE( void );
#elif defined SYSCLK_FREQ_120MHz_HSE
static void SetSysClockTo120_HSE( void );
#elif defined SYSCLK_FREQ_144MHz_HSE
static void SetSysClockTo144_HSE( void );
#elif defined SYSCLK_FREQ_48MHz_HSI
static void SetSysClockTo48_HSI( void );
#elif defined SYSCLK_FREQ_56MHz_HSI
static void SetSysClockTo56_HSI( void );
#elif defined SYSCLK_FREQ_72MHz_HSI
static void SetSysClockTo72_HSI( void );
#elif defined SYSCLK_FREQ_96MHz_HSI
static void SetSysClockTo96_HSI( void );
#elif defined SYSCLK_FREQ_120MHz_HSI
static void SetSysClockTo120_HSI( void );
#elif defined SYSCLK_FREQ_144MHz_HSI
static void SetSysClockTo144_HSI( void );
#endif
/*********************************************************************
* @fn SystemInit
*
* @brief Setup the microcontroller system Initialize the Embedded Flash Interface,
* the PLL and update the SystemCoreClock variable.
*
* @return none
*/
void SystemInit (void)
{
RCC->CTLR |= (uint32_t)0x00000001;
RCC->CFGR0 &= (uint32_t)0xF8FF0000;
RCC->CTLR &= (uint32_t)0xFEF6FFFF;
RCC->CTLR &= (uint32_t)0xFFFBFFFF;
RCC->CFGR0 &= (uint32_t)0xFF80FFFF;
RCC->INTR = 0x009F0000;
SetSysClock();
}
/*********************************************************************
* @fn SystemCoreClockUpdate
*
* @brief Update SystemCoreClock variable according to Clock Register Values.
*
* @return none
*/
void SystemCoreClockUpdate (void)
{
uint32_t tmp = 0, pllmull = 0, pllsource = 0, Pll_6_5 = 0;
tmp = RCC->CFGR0 & RCC_SWS;
switch (tmp)
{
case 0x00:
SystemCoreClock = HSI_VALUE;
break;
case 0x04:
SystemCoreClock = HSE_VALUE;
break;
case 0x08:
pllmull = RCC->CFGR0 & RCC_PLLMULL;
pllsource = RCC->CFGR0 & RCC_PLLSRC;
pllmull = ( pllmull >> 18) + 2;
if(pllmull == 17) pllmull = 18;
if (pllsource == 0x00)
{
if(EXTEN->EXTEN_CTR & EXTEN_PLL_HSI_PRE){
SystemCoreClock = HSI_VALUE * pllmull;
}
else{
SystemCoreClock = (HSI_VALUE >> 1) * pllmull;
}
}
else
{
#if defined (CH32V20x_D8W)
if((RCC->CFGR0 & (3<<22)) == (3<<22))
{
SystemCoreClock = ((HSE_VALUE>>1)) * pllmull;
}
else
#endif
if ((RCC->CFGR0 & RCC_PLLXTPRE) != (uint32_t)RESET)
{
#if defined (CH32V20x_D8) || defined (CH32V20x_D8W)
SystemCoreClock = ((HSE_VALUE>>2) >> 1) * pllmull;
#else
SystemCoreClock = (HSE_VALUE >> 1) * pllmull;
#endif
}
else
{
#if defined (CH32V20x_D8) || defined (CH32V20x_D8W)
SystemCoreClock = (HSE_VALUE>>2) * pllmull;
#else
SystemCoreClock = HSE_VALUE * pllmull;
#endif
}
}
if(Pll_6_5 == 1) SystemCoreClock = (SystemCoreClock / 2);
break;
default:
SystemCoreClock = HSI_VALUE;
break;
}
tmp = AHBPrescTable[((RCC->CFGR0 & RCC_HPRE) >> 4)];
SystemCoreClock >>= tmp;
}
/*********************************************************************
* @fn SetSysClock
*
* @brief Configures the System clock frequency, HCLK, PCLK2 and PCLK1 prescalers.
*
* @return none
*/
static void SetSysClock(void)
{
#ifdef SYSCLK_FREQ_HSE
SetSysClockToHSE();
#elif defined SYSCLK_FREQ_48MHz_HSE
SetSysClockTo48_HSE();
#elif defined SYSCLK_FREQ_56MHz_HSE
SetSysClockTo56_HSE();
#elif defined SYSCLK_FREQ_72MHz_HSE
SetSysClockTo72_HSE();
#elif defined SYSCLK_FREQ_96MHz_HSE
SetSysClockTo96_HSE();
#elif defined SYSCLK_FREQ_120MHz_HSE
SetSysClockTo120_HSE();
#elif defined SYSCLK_FREQ_144MHz_HSE
SetSysClockTo144_HSE();
#elif defined SYSCLK_FREQ_48MHz_HSI
SetSysClockTo48_HSI();
#elif defined SYSCLK_FREQ_56MHz_HSI
SetSysClockTo56_HSI();
#elif defined SYSCLK_FREQ_72MHz_HSI
SetSysClockTo72_HSI();
#elif defined SYSCLK_FREQ_96MHz_HSI
SetSysClockTo96_HSI();
#elif defined SYSCLK_FREQ_120MHz_HSI
SetSysClockTo120_HSI();
#elif defined SYSCLK_FREQ_144MHz_HSI
SetSysClockTo144_HSI();
#endif
/* If none of the define above is enabled, the HSI is used as System clock
* source (default after reset)
*/
}
#ifdef SYSCLK_FREQ_HSE
/*********************************************************************
* @fn SetSysClockToHSE
*
* @brief Sets HSE as System clock source and configure HCLK, PCLK2 and PCLK1 prescalers.
*
* @return none
*/
static void SetSysClockToHSE(void)
{
__IO uint32_t StartUpCounter = 0, HSEStatus = 0;
RCC->CTLR |= ((uint32_t)RCC_HSEON);
/* Wait till HSE is ready and if Time out is reached exit */
do
{
HSEStatus = RCC->CTLR & RCC_HSERDY;
StartUpCounter++;
} while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT));
if ((RCC->CTLR & RCC_HSERDY) != RESET)
{
HSEStatus = (uint32_t)0x01;
}
else
{
HSEStatus = (uint32_t)0x00;
}
if (HSEStatus == (uint32_t)0x01)
{
/* HCLK = SYSCLK */
RCC->CFGR0 |= (uint32_t)RCC_HPRE_DIV1;
/* PCLK2 = HCLK */
RCC->CFGR0 |= (uint32_t)RCC_PPRE2_DIV1;
/* PCLK1 = HCLK */
RCC->CFGR0 |= (uint32_t)RCC_PPRE1_DIV1;
/* Select HSE as system clock source
* CH32V20x_D6 (HSE=8MHZ)
* CH32V20x_D8 (HSE=32MHZ)
* CH32V20x_D8W (HSE=32MHZ)
*/
RCC->CFGR0 &= (uint32_t)((uint32_t)~(RCC_SW));
RCC->CFGR0 |= (uint32_t)RCC_SW_HSE;
/* Wait till HSE is used as system clock source */
while ((RCC->CFGR0 & (uint32_t)RCC_SWS) != (uint32_t)0x04)
{
}
}
else
{
/* If HSE fails to start-up, the application will have wrong clock
* configuration. User can add here some code to deal with this error
*/
}
}
#elif defined SYSCLK_FREQ_48MHz_HSE
/*********************************************************************
* @fn SetSysClockTo48_HSE
*
* @brief Sets System clock frequency to 48MHz and configure HCLK, PCLK2 and PCLK1 prescalers.
*
* @return none
*/
static void SetSysClockTo48_HSE(void)
{
__IO uint32_t StartUpCounter = 0, HSEStatus = 0;
RCC->CTLR |= ((uint32_t)RCC_HSEON);
/* Wait till HSE is ready and if Time out is reached exit */
do
{
HSEStatus = RCC->CTLR & RCC_HSERDY;
StartUpCounter++;
} while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT));
if ((RCC->CTLR & RCC_HSERDY) != RESET)
{
HSEStatus = (uint32_t)0x01;
}
else
{
HSEStatus = (uint32_t)0x00;
}
if (HSEStatus == (uint32_t)0x01)
{
/* HCLK = SYSCLK */
RCC->CFGR0 |= (uint32_t)RCC_HPRE_DIV1;
/* PCLK2 = HCLK */
RCC->CFGR0 |= (uint32_t)RCC_PPRE2_DIV1;
/* PCLK1 = HCLK */
RCC->CFGR0 |= (uint32_t)RCC_PPRE1_DIV2;
/* CH32V20x_D6-PLL configuration: PLLCLK = HSE * 6 = 48 MHz (HSE=8MHZ)
* CH32V20x_D8-PLL configuration: PLLCLK = HSE/4 * 6 = 48 MHz (HSE=32MHZ)
* CH32V20x_D8W-PLL configuration: PLLCLK = HSE/4 * 6 = 48 MHz (HSE=32MHZ)
*/
RCC->CFGR0 &= (uint32_t)((uint32_t)~(RCC_PLLSRC | RCC_PLLXTPRE | RCC_PLLMULL));
RCC->CFGR0 |= (uint32_t)(RCC_PLLSRC_HSE | RCC_PLLXTPRE_HSE | RCC_PLLMULL6);
/* Enable PLL */
RCC->CTLR |= RCC_PLLON;
/* Wait till PLL is ready */
while((RCC->CTLR & RCC_PLLRDY) == 0)
{
}
/* Select PLL as system clock source */
RCC->CFGR0 &= (uint32_t)((uint32_t)~(RCC_SW));
RCC->CFGR0 |= (uint32_t)RCC_SW_PLL;
/* Wait till PLL is used as system clock source */
while ((RCC->CFGR0 & (uint32_t)RCC_SWS) != (uint32_t)0x08)
{
}
}
else
{
/*
* If HSE fails to start-up, the application will have wrong clock
* configuration. User can add here some code to deal with this error
*/
}
}
#elif defined SYSCLK_FREQ_56MHz_HSE
/*********************************************************************
* @fn SetSysClockTo56_HSE
*
* @brief Sets System clock frequency to 56MHz and configure HCLK, PCLK2 and PCLK1 prescalers.
*
* @return none
*/
static void SetSysClockTo56_HSE(void)
{
__IO uint32_t StartUpCounter = 0, HSEStatus = 0;
RCC->CTLR |= ((uint32_t)RCC_HSEON);
/* Wait till HSE is ready and if Time out is reached exit */
do
{
HSEStatus = RCC->CTLR & RCC_HSERDY;
StartUpCounter++;
} while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT));
if ((RCC->CTLR & RCC_HSERDY) != RESET)
{
HSEStatus = (uint32_t)0x01;
}
else
{
HSEStatus = (uint32_t)0x00;
}
if (HSEStatus == (uint32_t)0x01)
{
/* HCLK = SYSCLK */
RCC->CFGR0 |= (uint32_t)RCC_HPRE_DIV1;
/* PCLK2 = HCLK */
RCC->CFGR0 |= (uint32_t)RCC_PPRE2_DIV1;
/* PCLK1 = HCLK */
RCC->CFGR0 |= (uint32_t)RCC_PPRE1_DIV2;
/* CH32V20x_D6-PLL configuration: PLLCLK = HSE * 7 = 56 MHz (HSE=8MHZ)
* CH32V20x_D8-PLL configuration: PLLCLK = HSE/4 * 7 = 56 MHz (HSE=32MHZ)
* CH32V20x_D8W-PLL configuration: PLLCLK = HSE/4 * 7 = 56 MHz (HSE=32MHZ)
*/
RCC->CFGR0 &= (uint32_t)((uint32_t)~(RCC_PLLSRC | RCC_PLLXTPRE | RCC_PLLMULL));
RCC->CFGR0 |= (uint32_t)(RCC_PLLSRC_HSE | RCC_PLLXTPRE_HSE | RCC_PLLMULL7);
/* Enable PLL */
RCC->CTLR |= RCC_PLLON;
/* Wait till PLL is ready */
while((RCC->CTLR & RCC_PLLRDY) == 0)
{
}
/* Select PLL as system clock source */
RCC->CFGR0 &= (uint32_t)((uint32_t)~(RCC_SW));
RCC->CFGR0 |= (uint32_t)RCC_SW_PLL;
/* Wait till PLL is used as system clock source */
while ((RCC->CFGR0 & (uint32_t)RCC_SWS) != (uint32_t)0x08)
{
}
}
else
{
/*
* If HSE fails to start-up, the application will have wrong clock
* configuration. User can add here some code to deal with this error
*/
}
}
#elif defined SYSCLK_FREQ_72MHz_HSE
/*********************************************************************
* @fn SetSysClockTo72_HSE
*
* @brief Sets System clock frequency to 72MHz and configure HCLK, PCLK2 and PCLK1 prescalers.
*
* @return none
*/
static void SetSysClockTo72_HSE(void)
{
__IO uint32_t StartUpCounter = 0, HSEStatus = 0;
RCC->CTLR |= ((uint32_t)RCC_HSEON);
/* Wait till HSE is ready and if Time out is reached exit */
do
{
HSEStatus = RCC->CTLR & RCC_HSERDY;
StartUpCounter++;
} while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT));
if ((RCC->CTLR & RCC_HSERDY) != RESET)
{
HSEStatus = (uint32_t)0x01;
}
else
{
HSEStatus = (uint32_t)0x00;
}
if (HSEStatus == (uint32_t)0x01)
{
/* HCLK = SYSCLK */
RCC->CFGR0 |= (uint32_t)RCC_HPRE_DIV1;
/* PCLK2 = HCLK */
RCC->CFGR0 |= (uint32_t)RCC_PPRE2_DIV1;
/* PCLK1 = HCLK */
RCC->CFGR0 |= (uint32_t)RCC_PPRE1_DIV2;
/* CH32V20x_D6-PLL configuration: PLLCLK = HSE * 9 = 72 MHz (HSE=8MHZ)
* CH32V20x_D8-PLL configuration: PLLCLK = HSE/4 * 9 = 72 MHz (HSE=32MHZ)
* CH32V20x_D8W-PLL configuration: PLLCLK = HSE/4 * 9 = 72 MHz (HSE=32MHZ)
*/
RCC->CFGR0 &= (uint32_t)((uint32_t)~(RCC_PLLSRC | RCC_PLLXTPRE |
RCC_PLLMULL));
RCC->CFGR0 |= (uint32_t)(RCC_PLLSRC_HSE | RCC_PLLXTPRE_HSE | RCC_PLLMULL9);
/* Enable PLL */
RCC->CTLR |= RCC_PLLON;
/* Wait till PLL is ready */
while((RCC->CTLR & RCC_PLLRDY) == 0)
{
}
/* Select PLL as system clock source */
RCC->CFGR0 &= (uint32_t)((uint32_t)~(RCC_SW));
RCC->CFGR0 |= (uint32_t)RCC_SW_PLL;
/* Wait till PLL is used as system clock source */
while ((RCC->CFGR0 & (uint32_t)RCC_SWS) != (uint32_t)0x08)
{
}
}
else
{
/*
* If HSE fails to start-up, the application will have wrong clock
* configuration. User can add here some code to deal with this error
*/
}
}
#elif defined SYSCLK_FREQ_96MHz_HSE
/*********************************************************************
* @fn SetSysClockTo96_HSE
*
* @brief Sets System clock frequency to 96MHz and configure HCLK, PCLK2 and PCLK1 prescalers.
*
* @return none
*/
static void SetSysClockTo96_HSE(void)
{
__IO uint32_t StartUpCounter = 0, HSEStatus = 0;
RCC->CTLR |= ((uint32_t)RCC_HSEON);
/* Wait till HSE is ready and if Time out is reached exit */
do
{
HSEStatus = RCC->CTLR & RCC_HSERDY;
StartUpCounter++;
} while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT));
if ((RCC->CTLR & RCC_HSERDY) != RESET)
{
HSEStatus = (uint32_t)0x01;
}
else
{
HSEStatus = (uint32_t)0x00;
}
if (HSEStatus == (uint32_t)0x01)
{
/* HCLK = SYSCLK */
RCC->CFGR0 |= (uint32_t)RCC_HPRE_DIV1;
/* PCLK2 = HCLK */
RCC->CFGR0 |= (uint32_t)RCC_PPRE2_DIV1;
/* PCLK1 = HCLK */
RCC->CFGR0 |= (uint32_t)RCC_PPRE1_DIV2;
/* CH32V20x_D6-PLL configuration: PLLCLK = HSE * 12 = 96 MHz (HSE=8MHZ)
* CH32V20x_D8-PLL configuration: PLLCLK = HSE/4 * 12 = 96 MHz (HSE=32MHZ)
* CH32V20x_D8W-PLL configuration: PLLCLK = HSE/4 * 12 = 96 MHz (HSE=32MHZ)
*/
RCC->CFGR0 &= (uint32_t)((uint32_t)~(RCC_PLLSRC | RCC_PLLXTPRE |
RCC_PLLMULL));
RCC->CFGR0 |= (uint32_t)(RCC_PLLSRC_HSE | RCC_PLLXTPRE_HSE | RCC_PLLMULL12);
/* Enable PLL */
RCC->CTLR |= RCC_PLLON;
/* Wait till PLL is ready */
while((RCC->CTLR & RCC_PLLRDY) == 0)
{
}
/* Select PLL as system clock source */
RCC->CFGR0 &= (uint32_t)((uint32_t)~(RCC_SW));
RCC->CFGR0 |= (uint32_t)RCC_SW_PLL;
/* Wait till PLL is used as system clock source */
while ((RCC->CFGR0 & (uint32_t)RCC_SWS) != (uint32_t)0x08)
{
}
}
else
{
/*
* If HSE fails to start-up, the application will have wrong clock
* configuration. User can add here some code to deal with this error
*/
}
}
#elif defined SYSCLK_FREQ_120MHz_HSE
/*********************************************************************
* @fn SetSysClockTo120_HSE
*
* @brief Sets System clock frequency to 120MHz and configure HCLK, PCLK2 and PCLK1 prescalers.
*
* @return none
*/
static void SetSysClockTo120_HSE(void)
{
__IO uint32_t StartUpCounter = 0, HSEStatus = 0;
RCC->CTLR |= ((uint32_t)RCC_HSEON);
/* Wait till HSE is ready and if Time out is reached exit */
do
{
HSEStatus = RCC->CTLR & RCC_HSERDY;
StartUpCounter++;
} while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT));
if((RCC->CTLR & RCC_HSERDY) != RESET)
{
HSEStatus = (uint32_t)0x01;
}
else
{
HSEStatus = (uint32_t)0x00;
}
if(HSEStatus == (uint32_t)0x01)
{
#if defined (CH32V20x_D8W)
RCC->CFGR0 |= (uint32_t)(3<<22);
/* HCLK = SYSCLK/2 */
RCC->CFGR0 |= (uint32_t)RCC_HPRE_DIV2;
#else
/* HCLK = SYSCLK */
RCC->CFGR0 |= (uint32_t)RCC_HPRE_DIV1;
#endif
/* PCLK2 = HCLK */
RCC->CFGR0 |= (uint32_t)RCC_PPRE2_DIV1;
/* PCLK1 = HCLK */
RCC->CFGR0 |= (uint32_t)RCC_PPRE1_DIV2;
/* CH32V20x_D6-PLL configuration: PLLCLK = HSE * 15 = 120 MHz (HSE=8MHZ)
* CH32V20x_D8-PLL configuration: PLLCLK = HSE/4 * 15 = 120 MHz (HSE=32MHZ)
* CH32V20x_D8W-PLL configuration: PLLCLK = HSE/2 * 15 = 240 MHz (HSE=32MHZ)
*/
RCC->CFGR0 &= (uint32_t)((uint32_t) ~(RCC_PLLSRC | RCC_PLLXTPRE |
RCC_PLLMULL));
RCC->CFGR0 |= (uint32_t)(RCC_PLLSRC_HSE | RCC_PLLXTPRE_HSE | RCC_PLLMULL15);
/* Enable PLL */
RCC->CTLR |= RCC_PLLON;
/* Wait till PLL is ready */
while((RCC->CTLR & RCC_PLLRDY) == 0)
{
}
/* Select PLL as system clock source */
RCC->CFGR0 &= (uint32_t)((uint32_t) ~(RCC_SW));
RCC->CFGR0 |= (uint32_t)RCC_SW_PLL;
/* Wait till PLL is used as system clock source */
while((RCC->CFGR0 & (uint32_t)RCC_SWS) != (uint32_t)0x08)
{
}
}
else
{
/*
* If HSE fails to start-up, the application will have wrong clock
* configuration. User can add here some code to deal with this error
*/
}
}
#elif defined SYSCLK_FREQ_144MHz_HSE
/*********************************************************************
* @fn SetSysClockTo144_HSE
*
* @brief Sets System clock frequency to 144MHz and configure HCLK, PCLK2 and PCLK1 prescalers.
*
* @return none
*/
static void SetSysClockTo144_HSE(void)
{
__IO uint32_t StartUpCounter = 0, HSEStatus = 0;
RCC->CTLR |= ((uint32_t)RCC_HSEON);
/* Wait till HSE is ready and if Time out is reached exit */
do
{
HSEStatus = RCC->CTLR & RCC_HSERDY;
StartUpCounter++;
} while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT));
if ((RCC->CTLR & RCC_HSERDY) != RESET)
{
HSEStatus = (uint32_t)0x01;
}
else
{
HSEStatus = (uint32_t)0x00;
}
if (HSEStatus == (uint32_t)0x01)
{
/* HCLK = SYSCLK */
RCC->CFGR0 |= (uint32_t)RCC_HPRE_DIV1;
/* PCLK2 = HCLK */
RCC->CFGR0 |= (uint32_t)RCC_PPRE2_DIV1;
/* PCLK1 = HCLK */
RCC->CFGR0 |= (uint32_t)RCC_PPRE1_DIV2;
/* CH32V20x_D6-PLL configuration: PLLCLK = HSE * 18 = 144 MHz (HSE=8MHZ)
* CH32V20x_D8-PLL configuration: PLLCLK = HSE/4 * 18 = 144 MHz (HSE=32MHZ)
* CH32V20x_D8W-PLL configuration: PLLCLK = HSE/4 * 18 = 144 MHz (HSE=32MHZ)
*/
RCC->CFGR0 &= (uint32_t)((uint32_t)~(RCC_PLLSRC | RCC_PLLXTPRE |
RCC_PLLMULL));
RCC->CFGR0 |= (uint32_t)(RCC_PLLSRC_HSE | RCC_PLLXTPRE_HSE | RCC_PLLMULL18);
/* Enable PLL */
RCC->CTLR |= RCC_PLLON;
/* Wait till PLL is ready */
while((RCC->CTLR & RCC_PLLRDY) == 0)
{
}
/* Select PLL as system clock source */
RCC->CFGR0 &= (uint32_t)((uint32_t)~(RCC_SW));
RCC->CFGR0 |= (uint32_t)RCC_SW_PLL;
/* Wait till PLL is used as system clock source */
while ((RCC->CFGR0 & (uint32_t)RCC_SWS) != (uint32_t)0x08)
{
}
}
else
{
/*
* If HSE fails to start-up, the application will have wrong clock
* configuration. User can add here some code to deal with this error
*/
}
}
#elif defined SYSCLK_FREQ_48MHz_HSI
/*********************************************************************
* @fn SetSysClockTo48_HSI
*
* @brief Sets System clock frequency to 48MHz and configure HCLK, PCLK2 and PCLK1 prescalers.
*
* @return none
*/
static void SetSysClockTo48_HSI(void)
{
EXTEN->EXTEN_CTR |= EXTEN_PLL_HSI_PRE;
/* HCLK = SYSCLK */
RCC->CFGR0 |= (uint32_t)RCC_HPRE_DIV1;
/* PCLK2 = HCLK */
RCC->CFGR0 |= (uint32_t)RCC_PPRE2_DIV1;
/* PCLK1 = HCLK */
RCC->CFGR0 |= (uint32_t)RCC_PPRE1_DIV2;
/* PLL configuration: PLLCLK = HSI * 6 = 48 MHz */
RCC->CFGR0 &= (uint32_t)((uint32_t)~(RCC_PLLSRC | RCC_PLLXTPRE | RCC_PLLMULL));
RCC->CFGR0 |= (uint32_t)(RCC_PLLSRC_HSI_Div2 | RCC_PLLMULL6);
/* Enable PLL */
RCC->CTLR |= RCC_PLLON;
/* Wait till PLL is ready */
while((RCC->CTLR & RCC_PLLRDY) == 0)
{
}
/* Select PLL as system clock source */
RCC->CFGR0 &= (uint32_t)((uint32_t)~(RCC_SW));
RCC->CFGR0 |= (uint32_t)RCC_SW_PLL;
/* Wait till PLL is used as system clock source */
while ((RCC->CFGR0 & (uint32_t)RCC_SWS) != (uint32_t)0x08)
{
}
}
#elif defined SYSCLK_FREQ_56MHz_HSI
/*********************************************************************
* @fn SetSysClockTo56_HSI
*
* @brief Sets System clock frequency to 56MHz and configure HCLK, PCLK2 and PCLK1 prescalers.
*
* @return none
*/
static void SetSysClockTo56_HSI(void)
{
EXTEN->EXTEN_CTR |= EXTEN_PLL_HSI_PRE;
/* HCLK = SYSCLK */
RCC->CFGR0 |= (uint32_t)RCC_HPRE_DIV1;
/* PCLK2 = HCLK */
RCC->CFGR0 |= (uint32_t)RCC_PPRE2_DIV1;
/* PCLK1 = HCLK */
RCC->CFGR0 |= (uint32_t)RCC_PPRE1_DIV2;
/* PLL configuration: PLLCLK = HSI * 7 = 48 MHz */
RCC->CFGR0 &= (uint32_t)((uint32_t)~(RCC_PLLSRC | RCC_PLLXTPRE | RCC_PLLMULL));
RCC->CFGR0 |= (uint32_t)(RCC_PLLSRC_HSI_Div2 | RCC_PLLMULL7);
/* Enable PLL */
RCC->CTLR |= RCC_PLLON;
/* Wait till PLL is ready */
while((RCC->CTLR & RCC_PLLRDY) == 0)
{
}
/* Select PLL as system clock source */
RCC->CFGR0 &= (uint32_t)((uint32_t)~(RCC_SW));
RCC->CFGR0 |= (uint32_t)RCC_SW_PLL;
/* Wait till PLL is used as system clock source */
while ((RCC->CFGR0 & (uint32_t)RCC_SWS) != (uint32_t)0x08)
{
}
}
#elif defined SYSCLK_FREQ_72MHz_HSI
/*********************************************************************
* @fn SetSysClockTo72_HSI
*
* @brief Sets System clock frequency to 72MHz and configure HCLK, PCLK2 and PCLK1 prescalers.
*
* @return none
*/
static void SetSysClockTo72_HSI(void)
{
EXTEN->EXTEN_CTR |= EXTEN_PLL_HSI_PRE;
/* HCLK = SYSCLK */
RCC->CFGR0 |= (uint32_t)RCC_HPRE_DIV1;
/* PCLK2 = HCLK */
RCC->CFGR0 |= (uint32_t)RCC_PPRE2_DIV1;
/* PCLK1 = HCLK */
RCC->CFGR0 |= (uint32_t)RCC_PPRE1_DIV2;
/* PLL configuration: PLLCLK = HSI * 9 = 72 MHz */
RCC->CFGR0 &= (uint32_t)((uint32_t)~(RCC_PLLSRC | RCC_PLLXTPRE | RCC_PLLMULL));
RCC->CFGR0 |= (uint32_t)(RCC_PLLSRC_HSI_Div2 | RCC_PLLMULL9);
/* Enable PLL */
RCC->CTLR |= RCC_PLLON;
/* Wait till PLL is ready */
while((RCC->CTLR & RCC_PLLRDY) == 0)
{
}
/* Select PLL as system clock source */
RCC->CFGR0 &= (uint32_t)((uint32_t)~(RCC_SW));
RCC->CFGR0 |= (uint32_t)RCC_SW_PLL;
/* Wait till PLL is used as system clock source */
while ((RCC->CFGR0 & (uint32_t)RCC_SWS) != (uint32_t)0x08)
{
}
}
#elif defined SYSCLK_FREQ_96MHz_HSI
/*********************************************************************
* @fn SetSysClockTo96_HSI
*
* @brief Sets System clock frequency to 96MHz and configure HCLK, PCLK2 and PCLK1 prescalers.
*
* @return none
*/
static void SetSysClockTo96_HSI(void)
{
EXTEN->EXTEN_CTR |= EXTEN_PLL_HSI_PRE;
/* HCLK = SYSCLK */
RCC->CFGR0 |= (uint32_t)RCC_HPRE_DIV1;
/* PCLK2 = HCLK */
RCC->CFGR0 |= (uint32_t)RCC_PPRE2_DIV1;
/* PCLK1 = HCLK */
RCC->CFGR0 |= (uint32_t)RCC_PPRE1_DIV2;
/* PLL configuration: PLLCLK = HSI * 12 = 96 MHz */
RCC->CFGR0 &= (uint32_t)((uint32_t)~(RCC_PLLSRC | RCC_PLLXTPRE | RCC_PLLMULL));
RCC->CFGR0 |= (uint32_t)(RCC_PLLSRC_HSI_Div2 | RCC_PLLMULL12);
/* Enable PLL */
RCC->CTLR |= RCC_PLLON;
/* Wait till PLL is ready */
while((RCC->CTLR & RCC_PLLRDY) == 0)
{
}
/* Select PLL as system clock source */
RCC->CFGR0 &= (uint32_t)((uint32_t)~(RCC_SW));
RCC->CFGR0 |= (uint32_t)RCC_SW_PLL;
/* Wait till PLL is used as system clock source */
while ((RCC->CFGR0 & (uint32_t)RCC_SWS) != (uint32_t)0x08)
{
}
}
#elif defined SYSCLK_FREQ_120MHz_HSI
/*********************************************************************
* @fn SetSysClockTo120_HSI
*
* @brief Sets System clock frequency to 120MHz and configure HCLK, PCLK2 and PCLK1 prescalers.
*
* @return none
*/
static void SetSysClockTo120_HSI(void)
{
EXTEN->EXTEN_CTR |= EXTEN_PLL_HSI_PRE;
/* HCLK = SYSCLK */
RCC->CFGR0 |= (uint32_t)RCC_HPRE_DIV1;
/* PCLK2 = HCLK */
RCC->CFGR0 |= (uint32_t)RCC_PPRE2_DIV1;
/* PCLK1 = HCLK */
RCC->CFGR0 |= (uint32_t)RCC_PPRE1_DIV2;
/* PLL configuration: PLLCLK = HSI * 15 = 120 MHz */
RCC->CFGR0 &= (uint32_t)((uint32_t) ~(RCC_PLLSRC | RCC_PLLXTPRE |
RCC_PLLMULL));
RCC->CFGR0 |= (uint32_t)(RCC_PLLSRC_HSI_Div2 | RCC_PLLMULL15);
/* Enable PLL */
RCC->CTLR |= RCC_PLLON;
/* Wait till PLL is ready */
while((RCC->CTLR & RCC_PLLRDY) == 0)
{
}
/* Select PLL as system clock source */
RCC->CFGR0 &= (uint32_t)((uint32_t) ~(RCC_SW));
RCC->CFGR0 |= (uint32_t)RCC_SW_PLL;
/* Wait till PLL is used as system clock source */
while((RCC->CFGR0 & (uint32_t)RCC_SWS) != (uint32_t)0x08)
{
}
}
#elif defined SYSCLK_FREQ_144MHz_HSI
/*********************************************************************
* @fn SetSysClockTo144_HSI
*
* @brief Sets System clock frequency to 144MHz and configure HCLK, PCLK2 and PCLK1 prescalers.
*
* @return none
*/
static void SetSysClockTo144_HSI(void)
{
EXTEN->EXTEN_CTR |= EXTEN_PLL_HSI_PRE;
/* HCLK = SYSCLK */
RCC->CFGR0 |= (uint32_t)RCC_HPRE_DIV1;
/* PCLK2 = HCLK */
RCC->CFGR0 |= (uint32_t)RCC_PPRE2_DIV1;
/* PCLK1 = HCLK */
RCC->CFGR0 |= (uint32_t)RCC_PPRE1_DIV2;
/* PLL configuration: PLLCLK = HSI * 18 = 144 MHz */
RCC->CFGR0 &= (uint32_t)((uint32_t)~(RCC_PLLSRC | RCC_PLLXTPRE | RCC_PLLMULL));
RCC->CFGR0 |= (uint32_t)(RCC_PLLSRC_HSI_Div2 | RCC_PLLMULL18);
/* Enable PLL */
RCC->CTLR |= RCC_PLLON;
/* Wait till PLL is ready */
while((RCC->CTLR & RCC_PLLRDY) == 0)
{
}
/* Select PLL as system clock source */
RCC->CFGR0 &= (uint32_t)((uint32_t)~(RCC_SW));
RCC->CFGR0 |= (uint32_t)RCC_SW_PLL;
/* Wait till PLL is used as system clock source */
while ((RCC->CFGR0 & (uint32_t)RCC_SWS) != (uint32_t)0x08)
{
}
}
#endif
@@ -0,0 +1,243 @@
/*
* Copyright (c) 1997-2015, Wind River Systems, Inc.
* Copyright (c) 2021 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef ZEPHYR_INCLUDE_SYS_ATOMIC_H_
#define ZEPHYR_INCLUDE_SYS_ATOMIC_H_
#include <stdbool.h>
#include <toolchain.h>
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef int atomic_t;
typedef atomic_t atomic_val_t;
typedef void *atomic_ptr_t;
typedef atomic_ptr_t atomic_ptr_val_t;
/* Low-level primitives come in several styles: */
#if defined(CONFIG_ATOMIC_OPERATIONS_C)
/* Generic-but-slow implementation based on kernel locking and syscalls */
#include <sys/atomic_c.h>
#elif defined(CONFIG_ATOMIC_OPERATIONS_ARCH)
/* Some architectures need their own implementation */
# ifdef CONFIG_XTENSA
/* Not all Xtensa toolchains support GCC-style atomic intrinsics */
# include <arch/xtensa/atomic_xtensa.h>
# else
/* Other arch specific implementation */
# include <sys/atomic_arch.h>
# endif /* CONFIG_XTENSA */
#else
/* Default. See this file for the Doxygen reference: */
#include <sys/atomic_builtin.h>
#endif
/* Portable higher-level utilities: */
/**
* @defgroup atomic_apis Atomic Services APIs
* @ingroup kernel_apis
* @{
*/
/**
* @brief Initialize an atomic variable.
*
* This macro can be used to initialize an atomic variable. For example,
* @code atomic_t my_var = ATOMIC_INIT(75); @endcode
*
* @param i Value to assign to atomic variable.
*/
#define ATOMIC_INIT(i) (i)
/**
* @brief Initialize an atomic pointer variable.
*
* This macro can be used to initialize an atomic pointer variable. For
* example,
* @code atomic_ptr_t my_ptr = ATOMIC_PTR_INIT(&data); @endcode
*
* @param p Pointer value to assign to atomic pointer variable.
*/
#define ATOMIC_PTR_INIT(p) (p)
/**
* @cond INTERNAL_HIDDEN
*/
#define ATOMIC_BITS (sizeof(atomic_val_t) * 8)
#define ATOMIC_MASK(bit) (1U << ((uint32_t)(bit) & (ATOMIC_BITS - 1U)))
#define ATOMIC_ELEM(addr, bit) ((addr) + ((bit) / ATOMIC_BITS))
/**
* INTERNAL_HIDDEN @endcond
*/
/**
* @brief This macro computes the number of atomic variables necessary to
* represent a bitmap with @a num_bits.
*
* @param num_bits Number of bits.
*/
#define ATOMIC_BITMAP_SIZE(num_bits) (1 + ((num_bits) - 1) / ATOMIC_BITS)
/**
* @brief Define an array of atomic variables.
*
* This macro defines an array of atomic variables containing at least
* @a num_bits bits.
*
* @note
* If used from file scope, the bits of the array are initialized to zero;
* if used from within a function, the bits are left uninitialized.
*
* @cond INTERNAL_HIDDEN
* @note
* This macro should be replicated in the PREDEFINED field of the documentation
* Doxyfile.
* @endcond
*
* @param name Name of array of atomic variables.
* @param num_bits Number of bits needed.
*/
#define ATOMIC_DEFINE(name, num_bits) \
atomic_t name[ATOMIC_BITMAP_SIZE(num_bits)]
/**
* @brief Atomically test a bit.
*
* This routine tests whether bit number @a bit of @a target is set or not.
* The target may be a single atomic variable or an array of them.
*
* @param target Address of atomic variable or array.
* @param bit Bit number (starting from 0).
*
* @return true if the bit was set, false if it wasn't.
*/
static inline bool atomic_test_bit(const atomic_t *target, int bit)
{
atomic_val_t val = atomic_get(ATOMIC_ELEM(target, bit));
return (1 & (val >> (bit & (ATOMIC_BITS - 1)))) != 0;
}
/**
* @brief Atomically test and clear a bit.
*
* Atomically clear bit number @a bit of @a target and return its old value.
* The target may be a single atomic variable or an array of them.
*
* @param target Address of atomic variable or array.
* @param bit Bit number (starting from 0).
*
* @return true if the bit was set, false if it wasn't.
*/
static inline bool atomic_test_and_clear_bit(atomic_t *target, int bit)
{
atomic_val_t mask = ATOMIC_MASK(bit);
atomic_val_t old;
old = atomic_and(ATOMIC_ELEM(target, bit), ~mask);
return (old & mask) != 0;
}
/**
* @brief Atomically set a bit.
*
* Atomically set bit number @a bit of @a target and return its old value.
* The target may be a single atomic variable or an array of them.
*
* @param target Address of atomic variable or array.
* @param bit Bit number (starting from 0).
*
* @return true if the bit was set, false if it wasn't.
*/
static inline bool atomic_test_and_set_bit(atomic_t *target, int bit)
{
atomic_val_t mask = ATOMIC_MASK(bit);
atomic_val_t old;
old = atomic_or(ATOMIC_ELEM(target, bit), mask);
return (old & mask) != 0;
}
/**
* @brief Atomically clear a bit.
*
* Atomically clear bit number @a bit of @a target.
* The target may be a single atomic variable or an array of them.
*
* @param target Address of atomic variable or array.
* @param bit Bit number (starting from 0).
*
* @return N/A
*/
static inline void atomic_clear_bit(atomic_t *target, int bit)
{
atomic_val_t mask = ATOMIC_MASK(bit);
(void)atomic_and(ATOMIC_ELEM(target, bit), ~mask);
}
/**
* @brief Atomically set a bit.
*
* Atomically set bit number @a bit of @a target.
* The target may be a single atomic variable or an array of them.
*
* @param target Address of atomic variable or array.
* @param bit Bit number (starting from 0).
*
* @return N/A
*/
static inline void atomic_set_bit(atomic_t *target, int bit)
{
atomic_val_t mask = ATOMIC_MASK(bit);
(void)atomic_or(ATOMIC_ELEM(target, bit), mask);
}
/**
* @brief Atomically set a bit to a given value.
*
* Atomically set bit number @a bit of @a target to value @a val.
* The target may be a single atomic variable or an array of them.
*
* @param target Address of atomic variable or array.
* @param bit Bit number (starting from 0).
* @param val true for 1, false for 0.
*
* @return N/A
*/
static inline void atomic_set_bit_to(atomic_t *target, int bit, bool val)
{
atomic_val_t mask = ATOMIC_MASK(bit);
if (val) {
(void)atomic_or(ATOMIC_ELEM(target, bit), mask);
} else {
(void)atomic_and(ATOMIC_ELEM(target, bit), ~mask);
}
}
/**
* @}
*/
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* ZEPHYR_INCLUDE_SYS_ATOMIC_H_ */
@@ -0,0 +1,303 @@
/* atomic operations */
/*
* Copyright (c) 1997-2015, Wind River Systems, Inc.
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef ZEPHYR_INCLUDE_SYS_ATOMIC_BUILTIN_H_
#define ZEPHYR_INCLUDE_SYS_ATOMIC_BUILTIN_H_
#ifdef __cplusplus
extern "C" {
#endif
/* Included from <atomic.h> */
/**
* @addtogroup atomic_apis Atomic Services APIs
* @ingroup kernel_apis
* @{
*/
/**
* @brief Atomic compare-and-set.
*
* This routine performs an atomic compare-and-set on @a target. If the current
* value of @a target equals @a old_value, @a target is set to @a new_value.
* If the current value of @a target does not equal @a old_value, @a target
* is left unchanged.
*
* @param target Address of atomic variable.
* @param old_value Original value to compare against.
* @param new_value New value to store.
* @return true if @a new_value is written, false otherwise.
*/
static inline bool atomic_cas(atomic_t *target, atomic_val_t old_value,
atomic_val_t new_value)
{
return __atomic_compare_exchange_n(target, &old_value, new_value,
0, __ATOMIC_SEQ_CST,
__ATOMIC_SEQ_CST);
}
/**
* @brief Atomic compare-and-set with pointer values
*
* This routine performs an atomic compare-and-set on @a target. If the current
* value of @a target equals @a old_value, @a target is set to @a new_value.
* If the current value of @a target does not equal @a old_value, @a target
* is left unchanged.
*
* @param target Address of atomic variable.
* @param old_value Original value to compare against.
* @param new_value New value to store.
* @return true if @a new_value is written, false otherwise.
*/
static inline bool atomic_ptr_cas(atomic_ptr_t *target, atomic_ptr_val_t old_value,
atomic_ptr_val_t new_value)
{
return __atomic_compare_exchange_n(target, &old_value, new_value,
0, __ATOMIC_SEQ_CST,
__ATOMIC_SEQ_CST);
}
/**
*
* @brief Atomic addition.
*
* This routine performs an atomic addition on @a target.
*
* @param target Address of atomic variable.
* @param value Value to add.
*
* @return Previous value of @a target.
*/
static inline atomic_val_t atomic_add(atomic_t *target, atomic_val_t value)
{
return __atomic_fetch_add(target, value, __ATOMIC_SEQ_CST);
}
/**
*
* @brief Atomic subtraction.
*
* This routine performs an atomic subtraction on @a target.
*
* @param target Address of atomic variable.
* @param value Value to subtract.
*
* @return Previous value of @a target.
*/
static inline atomic_val_t atomic_sub(atomic_t *target, atomic_val_t value)
{
return __atomic_fetch_sub(target, value, __ATOMIC_SEQ_CST);
}
/**
*
* @brief Atomic increment.
*
* This routine performs an atomic increment by 1 on @a target.
*
* @param target Address of atomic variable.
*
* @return Previous value of @a target.
*/
static inline atomic_val_t atomic_inc(atomic_t *target)
{
return atomic_add(target, 1);
}
/**
*
* @brief Atomic decrement.
*
* This routine performs an atomic decrement by 1 on @a target.
*
* @param target Address of atomic variable.
*
* @return Previous value of @a target.
*/
static inline atomic_val_t atomic_dec(atomic_t *target)
{
return atomic_sub(target, 1);
}
/**
*
* @brief Atomic get.
*
* This routine performs an atomic read on @a target.
*
* @param target Address of atomic variable.
*
* @return Value of @a target.
*/
static inline atomic_val_t atomic_get(const atomic_t *target)
{
return __atomic_load_n(target, __ATOMIC_SEQ_CST);
}
/**
*
* @brief Atomic get a pointer value
*
* This routine performs an atomic read on @a target.
*
* @param target Address of pointer variable.
*
* @return Value of @a target.
*/
static inline atomic_ptr_val_t atomic_ptr_get(const atomic_ptr_t *target)
{
return __atomic_load_n(target, __ATOMIC_SEQ_CST);
}
/**
*
* @brief Atomic get-and-set.
*
* This routine atomically sets @a target to @a value and returns
* the previous value of @a target.
*
* @param target Address of atomic variable.
* @param value Value to write to @a target.
*
* @return Previous value of @a target.
*/
static inline atomic_val_t atomic_set(atomic_t *target, atomic_val_t value)
{
/* This builtin, as described by Intel, is not a traditional
* test-and-set operation, but rather an atomic exchange operation. It
* writes value into *ptr, and returns the previous contents of *ptr.
*/
return __atomic_exchange_n(target, value, __ATOMIC_SEQ_CST);
}
/**
*
* @brief Atomic get-and-set for pointer values
*
* This routine atomically sets @a target to @a value and returns
* the previous value of @a target.
*
* @param target Address of atomic variable.
* @param value Value to write to @a target.
*
* @return Previous value of @a target.
*/
static inline atomic_ptr_val_t atomic_ptr_set(atomic_ptr_t *target, atomic_ptr_val_t value)
{
return __atomic_exchange_n(target, value, __ATOMIC_SEQ_CST);
}
/**
*
* @brief Atomic clear.
*
* This routine atomically sets @a target to zero and returns its previous
* value. (Hence, it is equivalent to atomic_set(target, 0).)
*
* @param target Address of atomic variable.
*
* @return Previous value of @a target.
*/
static inline atomic_val_t atomic_clear(atomic_t *target)
{
return atomic_set(target, 0);
}
/**
*
* @brief Atomic clear of a pointer value
*
* This routine atomically sets @a target to zero and returns its previous
* value. (Hence, it is equivalent to atomic_set(target, 0).)
*
* @param target Address of atomic variable.
*
* @return Previous value of @a target.
*/
static inline atomic_ptr_val_t atomic_ptr_clear(atomic_ptr_t *target)
{
return atomic_ptr_set(target, NULL);
}
/**
*
* @brief Atomic bitwise inclusive OR.
*
* This routine atomically sets @a target to the bitwise inclusive OR of
* @a target and @a value.
*
* @param target Address of atomic variable.
* @param value Value to OR.
*
* @return Previous value of @a target.
*/
static inline atomic_val_t atomic_or(atomic_t *target, atomic_val_t value)
{
return __atomic_fetch_or(target, value, __ATOMIC_SEQ_CST);
}
/**
*
* @brief Atomic bitwise exclusive OR (XOR).
*
* This routine atomically sets @a target to the bitwise exclusive OR (XOR) of
* @a target and @a value.
*
* @param target Address of atomic variable.
* @param value Value to XOR
*
* @return Previous value of @a target.
*/
static inline atomic_val_t atomic_xor(atomic_t *target, atomic_val_t value)
{
return __atomic_fetch_xor(target, value, __ATOMIC_SEQ_CST);
}
/**
*
* @brief Atomic bitwise AND.
*
* This routine atomically sets @a target to the bitwise AND of @a target
* and @a value.
*
* @param target Address of atomic variable.
* @param value Value to AND.
*
* @return Previous value of @a target.
*/
static inline atomic_val_t atomic_and(atomic_t *target, atomic_val_t value)
{
return __atomic_fetch_and(target, value, __ATOMIC_SEQ_CST);
}
/**
*
* @brief Atomic bitwise NAND.
*
* This routine atomically sets @a target to the bitwise NAND of @a target
* and @a value. (This operation is equivalent to target = ~(target & value).)
*
* @param target Address of atomic variable.
* @param value Value to NAND.
*
* @return Previous value of @a target.
*/
static inline atomic_val_t atomic_nand(atomic_t *target, atomic_val_t value)
{
return __atomic_fetch_nand(target, value, __ATOMIC_SEQ_CST);
}
/** @} */
#ifdef __cplusplus
}
#endif
#endif /* ZEPHYR_INCLUDE_SYS_ATOMIC_BUILTIN_H_ */
@@ -0,0 +1,568 @@
/** @file
* @brief Byte order helpers.
*/
/*
* Copyright (c) 2015-2016, Intel Corporation.
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef ZEPHYR_INCLUDE_SYS_BYTEORDER_H_
#define ZEPHYR_INCLUDE_SYS_BYTEORDER_H_
#include <zephyr/types.h>
#include <stddef.h>
// #include <sys/__assert.h>
#include <toolchain.h>
/* Internal helpers only used by the sys_* APIs further below */
#define __bswap_16(x) ((uint16_t) ((((x) >> 8) & 0xff) | (((x) & 0xff) << 8)))
#define __bswap_24(x) ((uint32_t) ((((x) >> 16) & 0xff) | \
(((x)) & 0xff00) | \
(((x) & 0xff) << 16)))
#define __bswap_32(x) ((uint32_t) ((((x) >> 24) & 0xff) | \
(((x) >> 8) & 0xff00) | \
(((x) & 0xff00) << 8) | \
(((x) & 0xff) << 24)))
#define __bswap_48(x) ((uint64_t) ((((x) >> 40) & 0xff) | \
(((x) >> 24) & 0xff00) | \
(((x) >> 8) & 0xff0000) | \
(((x) & 0xff0000) << 8) | \
(((x) & 0xff00) << 24) | \
(((x) & 0xff) << 40)))
#define __bswap_64(x) ((uint64_t) ((((x) >> 56) & 0xff) | \
(((x) >> 40) & 0xff00) | \
(((x) >> 24) & 0xff0000) | \
(((x) >> 8) & 0xff000000) | \
(((x) & 0xff000000) << 8) | \
(((x) & 0xff0000) << 24) | \
(((x) & 0xff00) << 40) | \
(((x) & 0xff) << 56)))
/** @def sys_le16_to_cpu
* @brief Convert 16-bit integer from little-endian to host endianness.
*
* @param val 16-bit integer in little-endian format.
*
* @return 16-bit integer in host endianness.
*/
/** @def sys_cpu_to_le16
* @brief Convert 16-bit integer from host endianness to little-endian.
*
* @param val 16-bit integer in host endianness.
*
* @return 16-bit integer in little-endian format.
*/
/** @def sys_le24_to_cpu
* @brief Convert 24-bit integer from little-endian to host endianness.
*
* @param val 24-bit integer in little-endian format.
*
* @return 24-bit integer in host endianness.
*/
/** @def sys_cpu_to_le24
* @brief Convert 24-bit integer from host endianness to little-endian.
*
* @param val 24-bit integer in host endianness.
*
* @return 24-bit integer in little-endian format.
*/
/** @def sys_le32_to_cpu
* @brief Convert 32-bit integer from little-endian to host endianness.
*
* @param val 32-bit integer in little-endian format.
*
* @return 32-bit integer in host endianness.
*/
/** @def sys_cpu_to_le32
* @brief Convert 32-bit integer from host endianness to little-endian.
*
* @param val 32-bit integer in host endianness.
*
* @return 32-bit integer in little-endian format.
*/
/** @def sys_le48_to_cpu
* @brief Convert 48-bit integer from little-endian to host endianness.
*
* @param val 48-bit integer in little-endian format.
*
* @return 48-bit integer in host endianness.
*/
/** @def sys_cpu_to_le48
* @brief Convert 48-bit integer from host endianness to little-endian.
*
* @param val 48-bit integer in host endianness.
*
* @return 48-bit integer in little-endian format.
*/
/** @def sys_be16_to_cpu
* @brief Convert 16-bit integer from big-endian to host endianness.
*
* @param val 16-bit integer in big-endian format.
*
* @return 16-bit integer in host endianness.
*/
/** @def sys_cpu_to_be16
* @brief Convert 16-bit integer from host endianness to big-endian.
*
* @param val 16-bit integer in host endianness.
*
* @return 16-bit integer in big-endian format.
*/
/** @def sys_be24_to_cpu
* @brief Convert 24-bit integer from big-endian to host endianness.
*
* @param val 24-bit integer in big-endian format.
*
* @return 24-bit integer in host endianness.
*/
/** @def sys_cpu_to_be24
* @brief Convert 24-bit integer from host endianness to big-endian.
*
* @param val 24-bit integer in host endianness.
*
* @return 24-bit integer in big-endian format.
*/
/** @def sys_be32_to_cpu
* @brief Convert 32-bit integer from big-endian to host endianness.
*
* @param val 32-bit integer in big-endian format.
*
* @return 32-bit integer in host endianness.
*/
/** @def sys_cpu_to_be32
* @brief Convert 32-bit integer from host endianness to big-endian.
*
* @param val 32-bit integer in host endianness.
*
* @return 32-bit integer in big-endian format.
*/
/** @def sys_be48_to_cpu
* @brief Convert 48-bit integer from big-endian to host endianness.
*
* @param val 48-bit integer in big-endian format.
*
* @return 48-bit integer in host endianness.
*/
/** @def sys_cpu_to_be48
* @brief Convert 48-bit integer from host endianness to big-endian.
*
* @param val 48-bit integer in host endianness.
*
* @return 48-bit integer in big-endian format.
*/
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
#define sys_le16_to_cpu(val) (val)
#define sys_cpu_to_le16(val) (val)
#define sys_le24_to_cpu(val) (val)
#define sys_cpu_to_le24(val) (val)
#define sys_le32_to_cpu(val) (val)
#define sys_cpu_to_le32(val) (val)
#define sys_le48_to_cpu(val) (val)
#define sys_cpu_to_le48(val) (val)
#define sys_le64_to_cpu(val) (val)
#define sys_cpu_to_le64(val) (val)
#define sys_be16_to_cpu(val) __bswap_16(val)
#define sys_cpu_to_be16(val) __bswap_16(val)
#define sys_be24_to_cpu(val) __bswap_24(val)
#define sys_cpu_to_be24(val) __bswap_24(val)
#define sys_be32_to_cpu(val) __bswap_32(val)
#define sys_cpu_to_be32(val) __bswap_32(val)
#define sys_be48_to_cpu(val) __bswap_48(val)
#define sys_cpu_to_be48(val) __bswap_48(val)
#define sys_be64_to_cpu(val) __bswap_64(val)
#define sys_cpu_to_be64(val) __bswap_64(val)
#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
#define sys_le16_to_cpu(val) __bswap_16(val)
#define sys_cpu_to_le16(val) __bswap_16(val)
#define sys_le24_to_cpu(val) __bswap_24(val)
#define sys_cpu_to_le24(val) __bswap_24(val)
#define sys_le32_to_cpu(val) __bswap_32(val)
#define sys_cpu_to_le32(val) __bswap_32(val)
#define sys_le48_to_cpu(val) __bswap_48(val)
#define sys_cpu_to_le48(val) __bswap_48(val)
#define sys_le64_to_cpu(val) __bswap_64(val)
#define sys_cpu_to_le64(val) __bswap_64(val)
#define sys_be16_to_cpu(val) (val)
#define sys_cpu_to_be16(val) (val)
#define sys_be24_to_cpu(val) (val)
#define sys_cpu_to_be24(val) (val)
#define sys_be32_to_cpu(val) (val)
#define sys_cpu_to_be32(val) (val)
#define sys_be48_to_cpu(val) (val)
#define sys_cpu_to_be48(val) (val)
#define sys_be64_to_cpu(val) (val)
#define sys_cpu_to_be64(val) (val)
#else
#error "Unknown byte order"
#endif
/**
* @brief Put a 16-bit integer as big-endian to arbitrary location.
*
* Put a 16-bit integer, originally in host endianness, to a
* potentially unaligned memory location in big-endian format.
*
* @param val 16-bit integer in host endianness.
* @param dst Destination memory address to store the result.
*/
static inline void sys_put_be16(uint16_t val, uint8_t dst[2])
{
dst[0] = val >> 8;
dst[1] = val;
}
/**
* @brief Put a 24-bit integer as big-endian to arbitrary location.
*
* Put a 24-bit integer, originally in host endianness, to a
* potentially unaligned memory location in big-endian format.
*
* @param val 24-bit integer in host endianness.
* @param dst Destination memory address to store the result.
*/
static inline void sys_put_be24(uint32_t val, uint8_t dst[3])
{
dst[0] = val >> 16;
sys_put_be16(val, &dst[1]);
}
/**
* @brief Put a 32-bit integer as big-endian to arbitrary location.
*
* Put a 32-bit integer, originally in host endianness, to a
* potentially unaligned memory location in big-endian format.
*
* @param val 32-bit integer in host endianness.
* @param dst Destination memory address to store the result.
*/
static inline void sys_put_be32(uint32_t val, uint8_t dst[4])
{
sys_put_be16(val >> 16, dst);
sys_put_be16(val, &dst[2]);
}
/**
* @brief Put a 48-bit integer as big-endian to arbitrary location.
*
* Put a 48-bit integer, originally in host endianness, to a
* potentially unaligned memory location in big-endian format.
*
* @param val 48-bit integer in host endianness.
* @param dst Destination memory address to store the result.
*/
static inline void sys_put_be48(uint64_t val, uint8_t dst[6])
{
sys_put_be16(val >> 32, dst);
sys_put_be32(val, &dst[2]);
}
/**
* @brief Put a 64-bit integer as big-endian to arbitrary location.
*
* Put a 64-bit integer, originally in host endianness, to a
* potentially unaligned memory location in big-endian format.
*
* @param val 64-bit integer in host endianness.
* @param dst Destination memory address to store the result.
*/
static inline void sys_put_be64(uint64_t val, uint8_t dst[8])
{
sys_put_be32(val >> 32, dst);
sys_put_be32(val, &dst[4]);
}
/**
* @brief Put a 16-bit integer as little-endian to arbitrary location.
*
* Put a 16-bit integer, originally in host endianness, to a
* potentially unaligned memory location in little-endian format.
*
* @param val 16-bit integer in host endianness.
* @param dst Destination memory address to store the result.
*/
static inline void sys_put_le16(uint16_t val, uint8_t dst[2])
{
dst[0] = val;
dst[1] = val >> 8;
}
/**
* @brief Put a 24-bit integer as little-endian to arbitrary location.
*
* Put a 24-bit integer, originally in host endianness, to a
* potentially unaligned memory location in littel-endian format.
*
* @param val 24-bit integer in host endianness.
* @param dst Destination memory address to store the result.
*/
static inline void sys_put_le24(uint32_t val, uint8_t dst[3])
{
sys_put_le16(val, dst);
dst[2] = val >> 16;
}
/**
* @brief Put a 32-bit integer as little-endian to arbitrary location.
*
* Put a 32-bit integer, originally in host endianness, to a
* potentially unaligned memory location in little-endian format.
*
* @param val 32-bit integer in host endianness.
* @param dst Destination memory address to store the result.
*/
static inline void sys_put_le32(uint32_t val, uint8_t dst[4])
{
sys_put_le16(val, dst);
sys_put_le16(val >> 16, &dst[2]);
}
/**
* @brief Put a 48-bit integer as little-endian to arbitrary location.
*
* Put a 48-bit integer, originally in host endianness, to a
* potentially unaligned memory location in little-endian format.
*
* @param val 48-bit integer in host endianness.
* @param dst Destination memory address to store the result.
*/
static inline void sys_put_le48(uint64_t val, uint8_t dst[6])
{
sys_put_le32(val, dst);
sys_put_le16(val >> 32, &dst[4]);
}
/**
* @brief Put a 64-bit integer as little-endian to arbitrary location.
*
* Put a 64-bit integer, originally in host endianness, to a
* potentially unaligned memory location in little-endian format.
*
* @param val 64-bit integer in host endianness.
* @param dst Destination memory address to store the result.
*/
static inline void sys_put_le64(uint64_t val, uint8_t dst[8])
{
sys_put_le32(val, dst);
sys_put_le32(val >> 32, &dst[4]);
}
/**
* @brief Get a 16-bit integer stored in big-endian format.
*
* Get a 16-bit integer, stored in big-endian format in a potentially
* unaligned memory location, and convert it to the host endianness.
*
* @param src Location of the big-endian 16-bit integer to get.
*
* @return 16-bit integer in host endianness.
*/
static inline uint16_t sys_get_be16(const uint8_t src[2])
{
return ((uint16_t)src[0] << 8) | src[1];
}
/**
* @brief Get a 24-bit integer stored in big-endian format.
*
* Get a 24-bit integer, stored in big-endian format in a potentially
* unaligned memory location, and convert it to the host endianness.
*
* @param src Location of the big-endian 24-bit integer to get.
*
* @return 24-bit integer in host endianness.
*/
static inline uint32_t sys_get_be24(const uint8_t src[3])
{
return ((uint32_t)src[0] << 16) | sys_get_be16(&src[1]);
}
/**
* @brief Get a 32-bit integer stored in big-endian format.
*
* Get a 32-bit integer, stored in big-endian format in a potentially
* unaligned memory location, and convert it to the host endianness.
*
* @param src Location of the big-endian 32-bit integer to get.
*
* @return 32-bit integer in host endianness.
*/
static inline uint32_t sys_get_be32(const uint8_t src[4])
{
return ((uint32_t)sys_get_be16(&src[0]) << 16) | sys_get_be16(&src[2]);
}
/**
* @brief Get a 48-bit integer stored in big-endian format.
*
* Get a 48-bit integer, stored in big-endian format in a potentially
* unaligned memory location, and convert it to the host endianness.
*
* @param src Location of the big-endian 48-bit integer to get.
*
* @return 48-bit integer in host endianness.
*/
static inline uint64_t sys_get_be48(const uint8_t src[6])
{
return ((uint64_t)sys_get_be32(&src[0]) << 16) | sys_get_be16(&src[4]);
}
/**
* @brief Get a 64-bit integer stored in big-endian format.
*
* Get a 64-bit integer, stored in big-endian format in a potentially
* unaligned memory location, and convert it to the host endianness.
*
* @param src Location of the big-endian 64-bit integer to get.
*
* @return 64-bit integer in host endianness.
*/
static inline uint64_t sys_get_be64(const uint8_t src[8])
{
return ((uint64_t)sys_get_be32(&src[0]) << 32) | sys_get_be32(&src[4]);
}
/**
* @brief Get a 16-bit integer stored in little-endian format.
*
* Get a 16-bit integer, stored in little-endian format in a potentially
* unaligned memory location, and convert it to the host endianness.
*
* @param src Location of the little-endian 16-bit integer to get.
*
* @return 16-bit integer in host endianness.
*/
static inline uint16_t sys_get_le16(const uint8_t src[2])
{
return ((uint16_t)src[1] << 8) | src[0];
}
/**
* @brief Get a 24-bit integer stored in big-endian format.
*
* Get a 24-bit integer, stored in big-endian format in a potentially
* unaligned memory location, and convert it to the host endianness.
*
* @param src Location of the big-endian 24-bit integer to get.
*
* @return 24-bit integer in host endianness.
*/
static inline uint32_t sys_get_le24(const uint8_t src[3])
{
return ((uint32_t)src[2] << 16) | sys_get_le16(&src[0]);
}
/**
* @brief Get a 32-bit integer stored in little-endian format.
*
* Get a 32-bit integer, stored in little-endian format in a potentially
* unaligned memory location, and convert it to the host endianness.
*
* @param src Location of the little-endian 32-bit integer to get.
*
* @return 32-bit integer in host endianness.
*/
static inline uint32_t sys_get_le32(const uint8_t src[4])
{
return ((uint32_t)sys_get_le16(&src[2]) << 16) | sys_get_le16(&src[0]);
}
/**
* @brief Get a 48-bit integer stored in little-endian format.
*
* Get a 48-bit integer, stored in little-endian format in a potentially
* unaligned memory location, and convert it to the host endianness.
*
* @param src Location of the little-endian 48-bit integer to get.
*
* @return 48-bit integer in host endianness.
*/
static inline uint64_t sys_get_le48(const uint8_t src[6])
{
return ((uint64_t)sys_get_le32(&src[2]) << 16) | sys_get_le16(&src[0]);
}
/**
* @brief Get a 64-bit integer stored in little-endian format.
*
* Get a 64-bit integer, stored in little-endian format in a potentially
* unaligned memory location, and convert it to the host endianness.
*
* @param src Location of the little-endian 64-bit integer to get.
*
* @return 64-bit integer in host endianness.
*/
static inline uint64_t sys_get_le64(const uint8_t src[8])
{
return ((uint64_t)sys_get_le32(&src[4]) << 32) | sys_get_le32(&src[0]);
}
/**
* @brief Swap one buffer content into another
*
* Copy the content of src buffer into dst buffer in reversed order,
* i.e.: src[n] will be put in dst[end-n]
* Where n is an index and 'end' the last index in both arrays.
* The 2 memory pointers must be pointing to different areas, and have
* a minimum size of given length.
*
* @param dst A valid pointer on a memory area where to copy the data in
* @param src A valid pointer on a memory area where to copy the data from
* @param length Size of both dst and src memory areas
*/
static inline void sys_memcpy_swap(void *dst, const void *src, size_t length)
{
uint8_t *pdst = (uint8_t *)dst;
const uint8_t *psrc = (const uint8_t *)src;
// __ASSERT(((psrc < pdst && (psrc + length) <= pdst) ||
// (psrc > pdst && (pdst + length) <= psrc)),
// "Source and destination buffers must not overlap");
psrc += length - 1;
for (; length > 0; length--) {
*pdst++ = *psrc--;
}
}
/**
* @brief Swap buffer content
*
* In-place memory swap, where final content will be reversed.
* I.e.: buf[n] will be put in buf[end-n]
* Where n is an index and 'end' the last index of buf.
*
* @param buf A valid pointer on a memory area to swap
* @param length Size of buf memory area
*/
static inline void sys_mem_swap(void *buf, size_t length)
{
size_t i;
for (i = 0; i < (length/2); i++) {
uint8_t tmp = ((uint8_t *)buf)[i];
((uint8_t *)buf)[i] = ((uint8_t *)buf)[length - 1 - i];
((uint8_t *)buf)[length - 1 - i] = tmp;
}
}
#endif /* ZEPHYR_INCLUDE_SYS_BYTEORDER_H_ */
@@ -0,0 +1,433 @@
/*
* Copyright (c) 2011-2014, Wind River Systems, Inc.
*
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @file
* @brief Misc utilities
*
* Misc utilities usable by the kernel and application code.
*/
#ifndef ZEPHYR_INCLUDE_SYS_UTIL_H_
#define ZEPHYR_INCLUDE_SYS_UTIL_H_
#include <sys/util_macro.h>
/* needs to be outside _ASMLANGUAGE so 'true' and 'false' can turn
* into '1' and '0' for asm or linker scripts
*/
#include <stdbool.h>
#ifndef _ASMLANGUAGE
#include <zephyr/types.h>
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* @defgroup sys-util Utility Functions
* @{
*/
/** @brief Cast @p x, a pointer, to an unsigned integer. */
#define POINTER_TO_UINT(x) ((uintptr_t) (x))
/** @brief Cast @p x, an unsigned integer, to a <tt>void*</tt>. */
#define UINT_TO_POINTER(x) ((void *) (uintptr_t) (x))
/** @brief Cast @p x, a pointer, to a signed integer. */
#define POINTER_TO_INT(x) ((intptr_t) (x))
/** @brief Cast @p x, a signed integer, to a <tt>void*</tt>. */
#define INT_TO_POINTER(x) ((void *) (intptr_t) (x))
#if !(defined(__CHAR_BIT__) && defined(__SIZEOF_LONG__))
# error Missing required predefined macros for BITS_PER_LONG calculation
#endif
/** Number of bits in a long int. */
#define BITS_PER_LONG (__CHAR_BIT__ * __SIZEOF_LONG__)
/**
* @brief Create a contiguous bitmask starting at bit position @p l
* and ending at position @p h.
*/
#define GENMASK(h, l) \
(((~0UL) - (1UL << (l)) + 1) & (~0UL >> (BITS_PER_LONG - 1 - (h))))
/** @brief Extract the Least Significant Bit from @p value. */
#define LSB_GET(value) ((value) & -(value))
/**
* @brief Extract a bitfield element from @p value corresponding to
* the field mask @p mask.
*/
#define FIELD_GET(mask, value) (((value) & (mask)) / LSB_GET(mask))
/**
* @brief Prepare a bitfield element using @p value with @p mask representing
* its field position and width. The result should be combined
* with other fields using a logical OR.
*/
#define FIELD_PREP(mask, value) (((value) * LSB_GET(mask)) & (mask))
/** @brief 0 if @p cond is true-ish; causes a compile error otherwise. */
#define ZERO_OR_COMPILE_ERROR(cond) ((int) sizeof(char[1 - 2 * !(cond)]) - 1)
#if defined(__cplusplus)
/* The built-in function used below for type checking in C is not
* supported by GNU C++.
*/
#define ARRAY_SIZE(array) (sizeof(array) / sizeof((array)[0]))
#else /* __cplusplus */
/**
* @brief Zero if @p array has an array type, a compile error otherwise
*
* This macro is available only from C, not C++.
*/
#define IS_ARRAY(array) \
ZERO_OR_COMPILE_ERROR( \
!__builtin_types_compatible_p(__typeof__(array), \
__typeof__(&(array)[0])))
/**
* @brief Number of elements in the given @p array
*
* In C++, due to language limitations, this will accept as @p array
* any type that implements <tt>operator[]</tt>. The results may not be
* particulary meaningful in this case.
*
* In C, passing a pointer as @p array causes a compile error.
*/
#define ARRAY_SIZE(array) \
((long) (IS_ARRAY(array) + (sizeof(array) / sizeof((array)[0]))))
#endif /* __cplusplus */
/**
* @brief Check if a pointer @p ptr lies within @p array.
*
* In C but not C++, this causes a compile error if @p array is not an array
* (e.g. if @p ptr and @p array are mixed up).
*
* @param ptr a pointer
* @param array an array
* @return 1 if @p ptr is part of @p array, 0 otherwise
*/
#define PART_OF_ARRAY(array, ptr) \
((ptr) && ((ptr) >= &array[0] && (ptr) < &array[ARRAY_SIZE(array)]))
/**
* @brief Get a pointer to a structure containing the element
*
* Example:
*
* struct foo {
* int bar;
* };
*
* struct foo my_foo;
* int *ptr = &my_foo.bar;
*
* struct foo *container = CONTAINER_OF(ptr, struct foo, bar);
*
* Above, @p container points at @p my_foo.
*
* @param ptr pointer to a structure element
* @param type name of the type that @p ptr is an element of
* @param field the name of the field within the struct @p ptr points to
* @return a pointer to the structure that contains @p ptr
*/
#define CONTAINER_OF(ptr, type, field) \
((type *)(((char *)(ptr)) - offsetof(type, field)))
/**
* @brief Value of @p x rounded up to the next multiple of @p align,
* which must be a power of 2.
*/
#define ROUND_UP(x, align) \
(((unsigned long)(x) + ((unsigned long)(align) - 1)) & \
~((unsigned long)(align) - 1))
/**
* @brief Value of @p x rounded down to the previous multiple of @p
* align, which must be a power of 2.
*/
#define ROUND_DOWN(x, align) \
((unsigned long)(x) & ~((unsigned long)(align) - 1))
/** @brief Value of @p x rounded up to the next word boundary. */
#define WB_UP(x) ROUND_UP(x, sizeof(void *))
/** @brief Value of @p x rounded down to the previous word boundary. */
#define WB_DN(x) ROUND_DOWN(x, sizeof(void *))
/**
* @brief Ceiling function applied to @p numerator / @p divider as a fraction.
*/
#define ceiling_fraction(numerator, divider) \
(((numerator) + ((divider) - 1)) / (divider))
/**
* @def MAX
* @brief The larger value between @p a and @p b.
* @note Arguments are evaluated twice.
*/
#ifndef MAX
/* Use Z_MAX for a GCC-only, single evaluation version */
#define MAX(a, b) (((a) > (b)) ? (a) : (b))
#endif
/**
* @def MIN
* @brief The smaller value between @p a and @p b.
* @note Arguments are evaluated twice.
*/
#ifndef MIN
/* Use Z_MIN for a GCC-only, single evaluation version */
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
#endif
/**
* @def CLAMP
* @brief Clamp a value to a given range.
* @note Arguments are evaluated multiple times.
*/
#ifndef CLAMP
/* Use Z_CLAMP for a GCC-only, single evaluation version */
#define CLAMP(val, low, high) (((val) <= (low)) ? (low) : MIN(val, high))
#endif
/**
* @brief Is @p x a power of two?
* @param x value to check
* @return true if @p x is a power of two, false otherwise
*/
static inline bool is_power_of_two(unsigned int x)
{
return (x != 0U) && ((x & (x - 1U)) == 0U);
}
/**
* @brief Arithmetic shift right
* @param value value to shift
* @param shift number of bits to shift
* @return @p value shifted right by @p shift; opened bit positions are
* filled with the sign bit
*/
static inline int64_t arithmetic_shift_right(int64_t value, uint8_t shift)
{
int64_t sign_ext;
if (shift == 0U) {
return value;
}
/* extract sign bit */
sign_ext = (value >> 63) & 1;
/* make all bits of sign_ext be the same as the value's sign bit */
sign_ext = -sign_ext;
/* shift value and fill opened bit positions with sign bit */
return (value >> shift) | (sign_ext << (64 - shift));
}
/**
* @brief byte by byte memcpy.
*
* Copy `size` bytes of `src` into `dest`. This is guaranteed to be done byte by byte.
*
* @param dst Pointer to the destination memory.
* @param src Pointer to the source of the data.
* @param size The number of bytes to copy.
*/
static inline void bytecpy(void *dst, const void *src, size_t size)
{
size_t i;
for (i = 0; i < size; ++i) {
((volatile uint8_t *)dst)[i] = ((volatile const uint8_t *)src)[i];
}
}
/**
* @brief byte by byte swap.
*
* Swap @a size bytes between memory regions @a a and @a b. This is
* guaranteed to be done byte by byte.
*
* @param a Pointer to the the first memory region.
* @param b Pointer to the the second memory region.
* @param size The number of bytes to swap.
*/
static inline void byteswp(void *a, void *b, size_t size)
{
uint8_t t;
uint8_t *aa = (uint8_t *)a;
uint8_t *bb = (uint8_t *)b;
for (; size > 0; --size) {
t = *aa;
*aa++ = *bb;
*bb++ = t;
}
}
/**
* @brief Convert a single character into a hexadecimal nibble.
*
* @param c The character to convert
* @param x The address of storage for the converted number.
*
* @return Zero on success or (negative) error code otherwise.
*/
int char2hex(char c, uint8_t *x);
/**
* @brief Convert a single hexadecimal nibble into a character.
*
* @param c The number to convert
* @param x The address of storage for the converted character.
*
* @return Zero on success or (negative) error code otherwise.
*/
int hex2char(uint8_t x, char *c);
/**
* @brief Convert a binary array into string representation.
*
* @param buf The binary array to convert
* @param buflen The length of the binary array to convert
* @param hex Address of where to store the string representation.
* @param hexlen Size of the storage area for string representation.
*
* @return The length of the converted string, or 0 if an error occurred.
*/
size_t bin2hex(const uint8_t *buf, size_t buflen, char *hex, size_t hexlen);
/**
* @brief Convert a hexadecimal string into a binary array.
*
* @param hex The hexadecimal string to convert
* @param hexlen The length of the hexadecimal string to convert.
* @param buf Address of where to store the binary data
* @param buflen Size of the storage area for binary data
*
* @return The length of the binary array, or 0 if an error occurred.
*/
size_t hex2bin(const char *hex, size_t hexlen, uint8_t *buf, size_t buflen);
/**
* @brief Convert a binary coded decimal (BCD 8421) value to binary.
*
* @param bcd BCD 8421 value to convert.
*
* @return Binary representation of input value.
*/
static inline uint8_t bcd2bin(uint8_t bcd)
{
return ((10 * (bcd >> 4)) + (bcd & 0x0F));
}
/**
* @brief Convert a binary value to binary coded decimal (BCD 8421).
*
* @param bin Binary value to convert.
*
* @return BCD 8421 representation of input value.
*/
static inline uint8_t bin2bcd(uint8_t bin)
{
return (((bin / 10) << 4) | (bin % 10));
}
/**
* @brief Convert a uint8_t into a decimal string representation.
*
* Convert a uint8_t value into its ASCII decimal string representation.
* The string is terminated if there is enough space in buf.
*
* @param buf Address of where to store the string representation.
* @param buflen Size of the storage area for string representation.
* @param value The value to convert to decimal string
*
* @return The length of the converted string (excluding terminator if
* any), or 0 if an error occurred.
*/
uint8_t u8_to_dec(char *buf, uint8_t buflen, uint8_t value);
/**
* @brief Properly truncate a NULL-terminated UTF-8 string
*
* Take a NULL-terminated UTF-8 string and ensure that if the string has been
* truncated (by setting the NULL terminator) earlier by other means, that
* the string ends with a properly formatted UTF-8 character (1-4 bytes).
*
* @htmlonly
* Example:
* char test_str[] = "€€€";
* char trunc_utf8[8];
*
* printf("Original : %s\n", test_str); // €€€
* strncpy(trunc_utf8, test_str, sizeof(trunc_utf8));
* trunc_utf8[sizeof(trunc_utf8) - 1] = '\0';
* printf("Bad : %s\n", trunc_utf8); // €€�
* utf8_trunc(trunc_utf8);
* printf("Truncated: %s\n", trunc_utf8); // €€
* @endhtmlonly
*
* @param utf8_str NULL-terminated string
*
* @return Pointer to the @p utf8_str
*/
char *utf8_trunc(char *utf8_str);
/**
* @brief Copies a UTF-8 encoded string from @p src to @p dst
*
* The resulting @p dst will always be NULL terminated, and the @p dst string
* will always be properly UTF-8 truncated.
*
* @param dst The destination of the UTF-8 string.
* @param src The source string
* @param n The size of the @p dst buffer. Shall not be 0.
*
* return Pointer to the @p dst
*/
char *utf8_lcpy(char *dst, const char *src, size_t n);
#ifdef __cplusplus
}
#endif
#endif /* !_ASMLANGUAGE */
/** @brief Number of bytes in @p x kibibytes */
#ifdef _LINKER
/* This is used in linker scripts so need to avoid type casting there */
#define KB(x) ((x) << 10)
#else
#define KB(x) (((size_t)x) << 10)
#endif
/** @brief Number of bytes in @p x mebibytes */
#define MB(x) (KB(x) << 10)
/** @brief Number of bytes in @p x gibibytes */
#define GB(x) (MB(x) << 10)
/** @brief Number of Hz in @p x kHz */
#define KHZ(x) ((x) * 1000)
/** @brief Number of Hz in @p x MHz */
#define MHZ(x) (KHZ(x) * 1000)
/**
* @}
*/
#endif /* ZEPHYR_INCLUDE_SYS_UTIL_H_ */
@@ -0,0 +1,146 @@
/*
* Copyright (c) 2011-2014, Wind River Systems, Inc.
* Copyright (c) 2020, Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @file
* @brief Misc utilities
*
* Repetitive or obscure helper macros needed by sys/util.h.
*/
#ifndef ZEPHYR_INCLUDE_SYS_UTIL_INTERNAL_H_
#define ZEPHYR_INCLUDE_SYS_UTIL_INTERNAL_H_
#include "util_loops.h"
/* IS_ENABLED() helpers */
/* This is called from IS_ENABLED(), and sticks on a "_XXXX" prefix,
* it will now be "_XXXX1" if config_macro is "1", or just "_XXXX" if it's
* undefined.
* ENABLED: Z_IS_ENABLED2(_XXXX1)
* DISABLED Z_IS_ENABLED2(_XXXX)
*/
#define Z_IS_ENABLED1(config_macro) Z_IS_ENABLED2(_XXXX##config_macro)
/* Here's the core trick, we map "_XXXX1" to "_YYYY," (i.e. a string
* with a trailing comma), so it has the effect of making this a
* two-argument tuple to the preprocessor only in the case where the
* value is defined to "1"
* ENABLED: _YYYY, <--- note comma!
* DISABLED: _XXXX
*/
#define _XXXX1 _YYYY,
/* Then we append an extra argument to fool the gcc preprocessor into
* accepting it as a varargs macro.
* arg1 arg2 arg3
* ENABLED: Z_IS_ENABLED3(_YYYY, 1, 0)
* DISABLED Z_IS_ENABLED3(_XXXX 1, 0)
*/
#define Z_IS_ENABLED2(one_or_two_args) Z_IS_ENABLED3(one_or_two_args 1, 0)
/* And our second argument is thus now cooked to be 1 in the case
* where the value is defined to 1, and 0 if not:
*/
#define Z_IS_ENABLED3(ignore_this, val, ...) val
/* Used internally by COND_CODE_1 and COND_CODE_0. */
#define Z_COND_CODE_1(_flag, _if_1_code, _else_code) \
__COND_CODE(_XXXX##_flag, _if_1_code, _else_code)
#define Z_COND_CODE_0(_flag, _if_0_code, _else_code) \
__COND_CODE(_ZZZZ##_flag, _if_0_code, _else_code)
#define _ZZZZ0 _YYYY,
#define __COND_CODE(one_or_two_args, _if_code, _else_code) \
__GET_ARG2_DEBRACKET(one_or_two_args _if_code, _else_code)
/* Gets second argument and removes brackets around that argument. It
* is expected that the parameter is provided in brackets/parentheses.
*/
#define __GET_ARG2_DEBRACKET(ignore_this, val, ...) __DEBRACKET val
/* Used to remove brackets from around a single argument. */
#define __DEBRACKET(...) __VA_ARGS__
/* Used by IS_EMPTY() */
/* reference: https://gustedt.wordpress.com/2010/06/08/detect-empty-macro-arguments/ */
#define Z_HAS_COMMA(...) \
NUM_VA_ARGS_LESS_1_IMPL(__VA_ARGS__, 1, 1, 1, 1, 1, 1, 1, 1, \
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, \
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, \
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0)
#define Z_TRIGGER_PARENTHESIS_(...) ,
#define Z_IS_EMPTY_(...) \
Z_IS_EMPTY__( \
Z_HAS_COMMA(__VA_ARGS__), \
Z_HAS_COMMA(Z_TRIGGER_PARENTHESIS_ __VA_ARGS__), \
Z_HAS_COMMA(__VA_ARGS__ (/*empty*/)), \
Z_HAS_COMMA(Z_TRIGGER_PARENTHESIS_ __VA_ARGS__ (/*empty*/)))
#define Z_CAT5(_0, _1, _2, _3, _4) _0 ## _1 ## _2 ## _3 ## _4
#define Z_IS_EMPTY__(_0, _1, _2, _3) \
Z_HAS_COMMA(Z_CAT5(Z_IS_EMPTY_CASE_, _0, _1, _2, _3))
#define Z_IS_EMPTY_CASE_0001 ,
/* Used by LIST_DROP_EMPTY() */
/* Adding ',' after each element would add empty element at the end of
* list, which is hard to remove, so instead precede each element with ',',
* this way first element is empty, and this one is easy to drop.
*/
#define Z_LIST_ADD_ELEM(e) EMPTY, e
#define Z_LIST_DROP_FIRST(...) GET_ARGS_LESS_N(1, __VA_ARGS__)
#define Z_LIST_NO_EMPTIES(e) \
COND_CODE_1(IS_EMPTY(e), (), (Z_LIST_ADD_ELEM(e)))
#define UTIL_CAT(a, ...) UTIL_PRIMITIVE_CAT(a, __VA_ARGS__)
#define UTIL_PRIMITIVE_CAT(a, ...) a##__VA_ARGS__
#define UTIL_CHECK_N(x, n, ...) n
#define UTIL_CHECK(...) UTIL_CHECK_N(__VA_ARGS__, 0,)
#define UTIL_NOT(x) UTIL_CHECK(UTIL_PRIMITIVE_CAT(UTIL_NOT_, x))
#define UTIL_NOT_0 ~, 1,
#define UTIL_COMPL(b) UTIL_PRIMITIVE_CAT(UTIL_COMPL_, b)
#define UTIL_COMPL_0 1
#define UTIL_COMPL_1 0
#define UTIL_BOOL(x) UTIL_COMPL(UTIL_NOT(x))
#define UTIL_EVAL(...) __VA_ARGS__
#define UTIL_EXPAND(...) __VA_ARGS__
#define UTIL_REPEAT(...) UTIL_LISTIFY(__VA_ARGS__)
/* Implementation details for NUM_VA_ARGS_LESS_1 */
#define NUM_VA_ARGS_LESS_1_IMPL( \
_ignored, \
_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, \
_11, _12, _13, _14, _15, _16, _17, _18, _19, _20, \
_21, _22, _23, _24, _25, _26, _27, _28, _29, _30, \
_31, _32, _33, _34, _35, _36, _37, _38, _39, _40, \
_41, _42, _43, _44, _45, _46, _47, _48, _49, _50, \
_51, _52, _53, _54, _55, _56, _57, _58, _59, _60, \
_61, _62, N, ...) N
/* Used by MACRO_MAP_CAT */
#define MACRO_MAP_CAT_(...) \
/* To make sure it works also for 2 arguments in total */ \
MACRO_MAP_CAT_N(NUM_VA_ARGS_LESS_1(__VA_ARGS__), __VA_ARGS__)
#define MACRO_MAP_CAT_N_(N, ...) UTIL_CAT(MACRO_MC_, N)(__VA_ARGS__,)
#define MACRO_MC_0(...)
#define MACRO_MC_1(m, a, ...) m(a)
#define MACRO_MC_2(m, a, ...) UTIL_CAT(m(a), MACRO_MC_1(m, __VA_ARGS__,))
#define MACRO_MC_3(m, a, ...) UTIL_CAT(m(a), MACRO_MC_2(m, __VA_ARGS__,))
#define MACRO_MC_4(m, a, ...) UTIL_CAT(m(a), MACRO_MC_3(m, __VA_ARGS__,))
#define MACRO_MC_5(m, a, ...) UTIL_CAT(m(a), MACRO_MC_4(m, __VA_ARGS__,))
#define MACRO_MC_6(m, a, ...) UTIL_CAT(m(a), MACRO_MC_5(m, __VA_ARGS__,))
#define MACRO_MC_7(m, a, ...) UTIL_CAT(m(a), MACRO_MC_6(m, __VA_ARGS__,))
#define MACRO_MC_8(m, a, ...) UTIL_CAT(m(a), MACRO_MC_7(m, __VA_ARGS__,))
#define MACRO_MC_9(m, a, ...) UTIL_CAT(m(a), MACRO_MC_8(m, __VA_ARGS__,))
#define MACRO_MC_10(m, a, ...) UTIL_CAT(m(a), MACRO_MC_9(m, __VA_ARGS__,))
#define MACRO_MC_11(m, a, ...) UTIL_CAT(m(a), MACRO_MC_10(m, __VA_ARGS__,))
#define MACRO_MC_12(m, a, ...) UTIL_CAT(m(a), MACRO_MC_11(m, __VA_ARGS__,))
#define MACRO_MC_13(m, a, ...) UTIL_CAT(m(a), MACRO_MC_12(m, __VA_ARGS__,))
#define MACRO_MC_14(m, a, ...) UTIL_CAT(m(a), MACRO_MC_13(m, __VA_ARGS__,))
#define MACRO_MC_15(m, a, ...) UTIL_CAT(m(a), MACRO_MC_14(m, __VA_ARGS__,))
#endif /* ZEPHYR_INCLUDE_SYS_UTIL_INTERNAL_H_ */
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,601 @@
/*
* Copyright (c) 2011-2014, Wind River Systems, Inc.
*
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @file
* @brief Macro utilities
*
* Macro utilities are the public interface for C/C++ code and device tree
* related implementation. In general, C/C++ will include <sys/util.h>
* instead this file directly. For device tree implementation, this file
* should be include instead <sys/util_internal.h>
*/
#ifndef ZEPHYR_INCLUDE_SYS_UTIL_MACROS_H_
#define ZEPHYR_INCLUDE_SYS_UTIL_MACROS_H_
#ifdef __cplusplus
extern "C" {
#endif
/**
* @addtogroup sys-util
* @{
*/
/*
* Most of the eldritch implementation details for all the macrobatics
* below (APIs like IS_ENABLED(), COND_CODE_1(), etc.) are hidden away
* in this file.
*/
#include <sys/util_internal.h>
#ifndef BIT
#if defined(_ASMLANGUAGE)
#define BIT(n) (1 << (n))
#else
/**
* @brief Unsigned integer with bit position @p n set (signed in
* assembly language).
*/
#define BIT(n) (1UL << (n))
#endif
#endif
/** @brief 64-bit unsigned integer with bit position @p _n set. */
#define BIT64(_n) (1ULL << (_n))
/**
* @brief Set or clear a bit depending on a boolean value
*
* The argument @p var is a variable whose value is written to as a
* side effect.
*
* @param var Variable to be altered
* @param bit Bit number
* @param set if 0, clears @p bit in @p var; any other value sets @p bit
*/
#define WRITE_BIT(var, bit, set) \
((var) = (set) ? ((var) | BIT(bit)) : ((var) & ~BIT(bit)))
/**
* @brief Bit mask with bits 0 through <tt>n-1</tt> (inclusive) set,
* or 0 if @p n is 0.
*/
#define BIT_MASK(n) (BIT(n) - 1UL)
/**
* @brief 64-bit bit mask with bits 0 through <tt>n-1</tt> (inclusive) set,
* or 0 if @p n is 0.
*/
#define BIT64_MASK(n) (BIT64(n) - 1ULL)
/**
* @brief Check for macro definition in compiler-visible expressions
*
* This trick was pioneered in Linux as the config_enabled() macro. It
* has the effect of taking a macro value that may be defined to "1"
* or may not be defined at all and turning it into a literal
* expression that can be handled by the C compiler instead of just
* the preprocessor. It is often used with a @p CONFIG_FOO macro which
* may be defined to 1 via Kconfig, or left undefined.
*
* That is, it works similarly to <tt>\#if defined(CONFIG_FOO)</tt>
* except that its expansion is a C expression. Thus, much <tt>\#ifdef</tt>
* usage can be replaced with equivalents like:
*
* if (IS_ENABLED(CONFIG_FOO)) {
* do_something_with_foo
* }
*
* This is cleaner since the compiler can generate errors and warnings
* for @p do_something_with_foo even when @p CONFIG_FOO is undefined.
*
* @param config_macro Macro to check
* @return 1 if @p config_macro is defined to 1, 0 otherwise (including
* if @p config_macro is not defined)
*/
#define IS_ENABLED(config_macro) Z_IS_ENABLED1(config_macro)
/* INTERNAL: the first pass above is just to expand any existing
* macros, we need the macro value to be e.g. a literal "1" at
* expansion time in the next macro, not "(1)", etc... Standard
* recursive expansion does not work.
*/
/**
* @brief Insert code depending on whether @p _flag expands to 1 or not.
*
* This relies on similar tricks as IS_ENABLED(), but as the result of
* @p _flag expansion, results in either @p _if_1_code or @p
* _else_code is expanded.
*
* To prevent the preprocessor from treating commas as argument
* separators, the @p _if_1_code and @p _else_code expressions must be
* inside brackets/parentheses: <tt>()</tt>. These are stripped away
* during macro expansion.
*
* Example:
*
* COND_CODE_1(CONFIG_FLAG, (uint32_t x;), (there_is_no_flag();))
*
* If @p CONFIG_FLAG is defined to 1, this expands to:
*
* uint32_t x;
*
* It expands to <tt>there_is_no_flag();</tt> otherwise.
*
* This could be used as an alternative to:
*
* #if defined(CONFIG_FLAG) && (CONFIG_FLAG == 1)
* #define MAYBE_DECLARE(x) uint32_t x
* #else
* #define MAYBE_DECLARE(x) there_is_no_flag()
* #endif
*
* MAYBE_DECLARE(x);
*
* However, the advantage of COND_CODE_1() is that code is resolved in
* place where it is used, while the @p \#if method defines @p
* MAYBE_DECLARE on two lines and requires it to be invoked again on a
* separate line. This makes COND_CODE_1() more concise and also
* sometimes more useful when used within another macro's expansion.
*
* @note @p _flag can be the result of preprocessor expansion, e.g.
* an expression involving <tt>NUM_VA_ARGS_LESS_1(...)</tt>.
* However, @p _if_1_code is only expanded if @p _flag expands
* to the integer literal 1. Integer expressions that evaluate
* to 1, e.g. after doing some arithmetic, will not work.
*
* @param _flag evaluated flag
* @param _if_1_code result if @p _flag expands to 1; must be in parentheses
* @param _else_code result otherwise; must be in parentheses
*/
#define COND_CODE_1(_flag, _if_1_code, _else_code) \
Z_COND_CODE_1(_flag, _if_1_code, _else_code)
/**
* @brief Like COND_CODE_1() except tests if @p _flag is 0.
*
* This is like COND_CODE_1(), except that it tests whether @p _flag
* expands to the integer literal 0. It expands to @p _if_0_code if
* so, and @p _else_code otherwise; both of these must be enclosed in
* parentheses.
*
* @param _flag evaluated flag
* @param _if_0_code result if @p _flag expands to 0; must be in parentheses
* @param _else_code result otherwise; must be in parentheses
* @see COND_CODE_1()
*/
#define COND_CODE_0(_flag, _if_0_code, _else_code) \
Z_COND_CODE_0(_flag, _if_0_code, _else_code)
/**
* @brief Insert code if @p _flag is defined and equals 1.
*
* Like COND_CODE_1(), this expands to @p _code if @p _flag is defined to 1;
* it expands to nothing otherwise.
*
* Example:
*
* IF_ENABLED(CONFIG_FLAG, (uint32_t foo;))
*
* If @p CONFIG_FLAG is defined to 1, this expands to:
*
* uint32_t foo;
*
* and to nothing otherwise.
*
* It can be considered as a more compact alternative to:
*
* #if defined(CONFIG_FLAG) && (CONFIG_FLAG == 1)
* uint32_t foo;
* #endif
*
* @param _flag evaluated flag
* @param _code result if @p _flag expands to 1; must be in parentheses
*/
#define IF_ENABLED(_flag, _code) \
COND_CODE_1(_flag, _code, ())
/**
* @brief Check if a macro has a replacement expression
*
* If @p a is a macro defined to a nonempty value, this will return
* true, otherwise it will return false. It only works with defined
* macros, so an additional @p \#ifdef test may be needed in some cases.
*
* This macro may be used with COND_CODE_1() and COND_CODE_0() while
* processing <tt>__VA_ARGS__</tt> to avoid processing empty arguments.
*
* Example:
*
* #define EMPTY
* #define NON_EMPTY 1
* #undef UNDEFINED
* IS_EMPTY(EMPTY)
* IS_EMPTY(NON_EMPTY)
* IS_EMPTY(UNDEFINED)
* #if defined(EMPTY) && IS_EMPTY(EMPTY) == true
* some_conditional_code
* #endif
*
* In above examples, the invocations of IS_EMPTY(...) return @p true,
* @p false, and @p true; @p some_conditional_code is included.
*
* @param ... macro to check for emptiness (may be <tt>__VA_ARGS__</tt>)
*/
#define IS_EMPTY(...) Z_IS_EMPTY_(__VA_ARGS__)
/**
* @brief Remove empty arguments from list.
*
* During macro expansion, <tt>__VA_ARGS__</tt> and other preprocessor
* generated lists may contain empty elements, e.g.:
*
* #define LIST ,a,b,,d,
*
* Using EMPTY to show each empty element, LIST contains:
*
* EMPTY, a, b, EMPTY, d
*
* When processing such lists, e.g. using FOR_EACH(), all empty elements
* will be processed, and may require filtering out.
* To make that process easier, it is enough to invoke LIST_DROP_EMPTY
* which will remove all empty elements.
*
* Example:
*
* LIST_DROP_EMPTY(LIST)
*
* expands to:
*
* a, b, d
*
* @param ... list to be processed
*/
#define LIST_DROP_EMPTY(...) \
Z_LIST_DROP_FIRST(FOR_EACH(Z_LIST_NO_EMPTIES, (), __VA_ARGS__))
/**
* @brief Macro with an empty expansion
*
* This trivial definition is provided for readability when a macro
* should expand to an empty result, which e.g. is sometimes needed to
* silence checkpatch.
*
* Example:
*
* #define LIST_ITEM(n) , item##n
*
* The above would cause checkpatch to complain, but:
*
* #define LIST_ITEM(n) EMPTY, item##n
*
* would not.
*/
#define EMPTY
/**
* @brief Macro that expands to its argument
*
* This is useful in macros like @c FOR_EACH() when there is no
* transformation required on the list elements.
*
* @param V any value
*/
#define IDENTITY(V) V
/**
* @brief Get nth argument from argument list.
*
* @param N Argument index to fetch. Counter from 1.
* @param ... Variable list of argments from which one argument is returned.
*
* @return Nth argument.
*/
#define GET_ARG_N(N, ...) Z_GET_ARG_##N(__VA_ARGS__)
/**
* @brief Strips n first arguments from the argument list.
*
* @param N Number of arguments to discard.
* @param ... Variable list of argments.
*
* @return argument list without N first arguments.
*/
#define GET_ARGS_LESS_N(N, ...) Z_GET_ARGS_LESS_##N(__VA_ARGS__)
/**
* @brief Like <tt>a || b</tt>, but does evaluation and
* short-circuiting at C preprocessor time.
*
* This is not the same as the binary @p || operator; in particular,
* @p a should expand to an integer literal 0 or 1. However, @p b
* can be any value.
*
* This can be useful when @p b is an expression that would cause a
* build error when @p a is 1.
*/
#define UTIL_OR(a, b) COND_CODE_1(UTIL_BOOL(a), (a), (b))
/**
* @brief Like <tt>a && b</tt>, but does evaluation and
* short-circuiting at C preprocessor time.
*
* This is not the same as the binary @p &&, however; in particular,
* @p a should expand to an integer literal 0 or 1. However, @p b
* can be any value.
*
* This can be useful when @p b is an expression that would cause a
* build error when @p a is 0.
*/
#define UTIL_AND(a, b) COND_CODE_1(UTIL_BOOL(a), (b), (0))
/**
* @brief Generates a sequence of code.
*
* Example:
*
* #define FOO(i, _) MY_PWM ## i ,
* { UTIL_LISTIFY(PWM_COUNT, FOO) }
*
* The above two lines expand to:
*
* { MY_PWM0 , MY_PWM1 , }
*
* @param LEN The length of the sequence. Must be an integer literal less
* than 255.
* @param F A macro function that accepts at least two arguments:
* <tt>F(i, ...)</tt>. @p F is called repeatedly in the expansion.
* Its first argument @p i is the index in the sequence, and
* the variable list of arguments passed to UTIL_LISTIFY are passed
* through to @p F.
*
* @note Calling UTIL_LISTIFY with undefined arguments has undefined
* behavior.
*/
#define UTIL_LISTIFY(LEN, F, ...) UTIL_CAT(Z_UTIL_LISTIFY_, LEN)(F, __VA_ARGS__)
/**
* @brief Call a macro @p F on each provided argument with a given
* separator between each call.
*
* Example:
*
* #define F(x) int a##x
* FOR_EACH(F, (;), 4, 5, 6);
*
* This expands to:
*
* int a4;
* int a5;
* int a6;
*
* @param F Macro to invoke
* @param sep Separator (e.g. comma or semicolon). Must be in parentheses;
* this is required to enable providing a comma as separator.
* @param ... Variable argument list. The macro @p F is invoked as
* <tt>F(element)</tt> for each element in the list.
*/
#define FOR_EACH(F, sep, ...) \
Z_FOR_EACH(F, sep, REVERSE_ARGS(__VA_ARGS__))
/**
* @brief Like FOR_EACH(), but with a terminator instead of a separator,
* and drops empty elements from the argument list
*
* The @p sep argument to <tt>FOR_EACH(F, (sep), a, b)</tt> is a
* separator which is placed between calls to @p F, like this:
*
* FOR_EACH(F, (sep), a, b) // F(a) sep F(b)
* // ^^^ no sep here!
*
* By contrast, the @p term argument to <tt>FOR_EACH_NONEMPTY_TERM(F, (term),
* a, b)</tt> is added after each time @p F appears in the expansion:
*
* FOR_EACH_NONEMPTY_TERM(F, (term), a, b) // F(a) term F(b) term
* // ^^^^
*
* Further, any empty elements are dropped:
*
* FOR_EACH_NONEMPTY_TERM(F, (term), a, EMPTY, b) // F(a) term F(b) term
*
* This is more convenient in some cases, because FOR_EACH_NONEMPTY_TERM()
* expands to nothing when given an empty argument list, and it's
* often cumbersome to write a macro @p F that does the right thing
* even when given an empty argument.
*
* One example is when <tt>__VA_ARGS__</tt> may or may not be empty,
* and the results are embedded in a larger initializer:
*
* #define SQUARE(x) ((x)*(x))
*
* int my_array[] = {
* FOR_EACH_NONEMPTY_TERM(SQUARE, (,), FOO(...))
* FOR_EACH_NONEMPTY_TERM(SQUARE, (,), BAR(...))
* FOR_EACH_NONEMPTY_TERM(SQUARE, (,), BAZ(...))
* };
*
* This is more convenient than:
*
* 1. figuring out whether the @p FOO, @p BAR, and @p BAZ expansions
* are empty and adding a comma manually (or not) between FOR_EACH()
* calls
* 2. rewriting SQUARE so it reacts appropriately when "x" is empty
* (which would be necessary if e.g. @p FOO expands to nothing)
*
* @param F Macro to invoke on each nonempty element of the variable
* arguments
* @param term Terminator (e.g. comma or semicolon) placed after each
* invocation of F. Must be in parentheses; this is required
* to enable providing a comma as separator.
* @param ... Variable argument list. The macro @p F is invoked as
* <tt>F(element)</tt> for each nonempty element in the list.
*/
#define FOR_EACH_NONEMPTY_TERM(F, term, ...) \
COND_CODE_0( \
/* are there zero non-empty arguments ? */ \
NUM_VA_ARGS_LESS_1(LIST_DROP_EMPTY(__VA_ARGS__, _)), \
/* if so, expand to nothing */ \
(), \
/* otherwise, expand to: */ \
(/* FOR_EACH() on nonempty elements, */ \
FOR_EACH(F, term, LIST_DROP_EMPTY(__VA_ARGS__)) \
/* plus a final terminator */ \
__DEBRACKET term \
))
/**
* @brief Call macro @p F on each provided argument, with the argument's index
* as an additional parameter.
*
* This is like FOR_EACH(), except @p F should be a macro which takes two
* arguments: <tt>F(index, variable_arg)</tt>.
*
* Example:
*
* #define F(idx, x) int a##idx = x
* FOR_EACH_IDX(F, (;), 4, 5, 6);
*
* This expands to:
*
* int a0 = 4;
* int a1 = 5;
* int a2 = 6;
*
* @param F Macro to invoke
* @param sep Separator (e.g. comma or semicolon). Must be in parentheses;
* this is required to enable providing a comma as separator.
* @param ... Variable argument list. The macro @p F is invoked as
* <tt>F(index, element)</tt> for each element in the list.
*/
#define FOR_EACH_IDX(F, sep, ...) \
Z_FOR_EACH_IDX(F, sep, REVERSE_ARGS(__VA_ARGS__))
/**
* @brief Call macro @p F on each provided argument, with an additional fixed
* argument as a parameter.
*
* This is like FOR_EACH(), except @p F should be a macro which takes two
* arguments: <tt>F(variable_arg, fixed_arg)</tt>.
*
* Example:
*
* static void func(int val, void *dev);
* FOR_EACH_FIXED_ARG(func, (;), dev, 4, 5, 6);
*
* This expands to:
*
* func(4, dev);
* func(5, dev);
* func(6, dev);
*
* @param F Macro to invoke
* @param sep Separator (e.g. comma or semicolon). Must be in parentheses;
* this is required to enable providing a comma as separator.
* @param fixed_arg Fixed argument passed to @p F as the second macro parameter.
* @param ... Variable argument list. The macro @p F is invoked as
* <tt>F(element, fixed_arg)</tt> for each element in the list.
*/
#define FOR_EACH_FIXED_ARG(F, sep, fixed_arg, ...) \
Z_FOR_EACH_FIXED_ARG(F, sep, fixed_arg, REVERSE_ARGS(__VA_ARGS__))
/**
* @brief Calls macro @p F for each variable argument with an index and fixed
* argument
*
* This is like the combination of FOR_EACH_IDX() with FOR_EACH_FIXED_ARG().
*
* Example:
*
* #define F(idx, x, fixed_arg) int fixed_arg##idx = x
* FOR_EACH_IDX_FIXED_ARG(F, (;), a, 4, 5, 6);
*
* This expands to:
*
* int a0 = 4;
* int a1 = 5;
* int a2 = 6;
*
* @param F Macro to invoke
* @param sep Separator (e.g. comma or semicolon). Must be in parentheses;
* This is required to enable providing a comma as separator.
* @param fixed_arg Fixed argument passed to @p F as the third macro parameter.
* @param ... Variable list of arguments. The macro @p F is invoked as
* <tt>F(index, element, fixed_arg)</tt> for each element in
* the list.
*/
#define FOR_EACH_IDX_FIXED_ARG(F, sep, fixed_arg, ...) \
Z_FOR_EACH_IDX_FIXED_ARG(F, sep, fixed_arg, REVERSE_ARGS(__VA_ARGS__))
/** @brief Reverse arguments order.
*
* @param ... Variable argument list.
*/
#define REVERSE_ARGS(...) \
Z_FOR_EACH_ENGINE(Z_FOR_EACH_EXEC, (,), Z_BYPASS, _, __VA_ARGS__)
/**
* @brief Number of arguments in the variable arguments list minus one.
*
* @param ... List of arguments
* @return Number of variadic arguments in the argument list, minus one
*/
#define NUM_VA_ARGS_LESS_1(...) \
NUM_VA_ARGS_LESS_1_IMPL(__VA_ARGS__, 63, 62, 61, \
60, 59, 58, 57, 56, 55, 54, 53, 52, 51, \
50, 49, 48, 47, 46, 45, 44, 43, 42, 41, \
40, 39, 38, 37, 36, 35, 34, 33, 32, 31, \
30, 29, 28, 27, 26, 25, 24, 23, 22, 21, \
20, 19, 18, 17, 16, 15, 14, 13, 12, 11, \
10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, ~)
/**
* @brief Mapping macro that pastes results together
*
* This is similar to FOR_EACH() in that it invokes a macro repeatedly
* on each element of <tt>__VA_ARGS__</tt>. However, unlike FOR_EACH(),
* MACRO_MAP_CAT() pastes the results together into a single token.
*
* For example, with this macro FOO:
*
* #define FOO(x) item_##x##_
*
* <tt>MACRO_MAP_CAT(FOO, a, b, c),</tt> expands to the token:
*
* item_a_item_b_item_c_
*
* @param ... Macro to expand on each argument, followed by its
* arguments. (The macro should take exactly one argument.)
* @return The results of expanding the macro on each argument, all pasted
* together
*/
#define MACRO_MAP_CAT(...) MACRO_MAP_CAT_(__VA_ARGS__)
/**
* @brief Mapping macro that pastes a fixed number of results together
*
* Similar to @ref MACRO_MAP_CAT(), but expects a fixed number of
* arguments. If more arguments are given than are expected, the rest
* are ignored.
*
* @param N Number of arguments to map
* @param ... Macro to expand on each argument, followed by its
* arguments. (The macro should take exactly one argument.)
* @return The results of expanding the macro on each argument, all pasted
* together
*/
#define MACRO_MAP_CAT_N(N, ...) MACRO_MAP_CAT_N_(N, __VA_ARGS__)
/**
* @}
*/
#ifdef __cplusplus
}
#endif
#endif /* ZEPHYR_INCLUDE_SYS_UTIL_MACROS_H_ */
@@ -0,0 +1,65 @@
/*
* Copyright (c) 2010-2014, Wind River Systems, Inc.
*
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @file
* @brief Macros to abstract toolchain specific capabilities
*
* This file contains various macros to abstract compiler capabilities that
* utilize toolchain specific attributes and/or pragmas.
*/
#ifndef ZEPHYR_INCLUDE_TOOLCHAIN_H_
#define ZEPHYR_INCLUDE_TOOLCHAIN_H_
/**
* @def HAS_BUILTIN(x)
* @brief Check if the compiler supports the built-in function \a x.
*
* This macro is for use with conditional compilation to enable code using a
* builtin function that may not be available in every compiler.
*/
#ifdef __has_builtin
#define HAS_BUILTIN(x) __has_builtin(x)
#else
/*
* The compiler doesn't provide the __has_builtin() macro, so instead we depend
* on the toolchain-specific headers to define HAS_BUILTIN_x for the builtins
* supported.
*/
#define HAS_BUILTIN(x) HAS_BUILTIN_##x
#endif
#if defined(__XCC__)
#include <toolchain/xcc.h>
#elif defined(__CCAC__)
#include <toolchain/mwdt.h>
#elif defined(__llvm__)
#include <toolchain/llvm.h>
#elif defined(__GNUC__) || (defined(_LINKER) && defined(__GCC_LINKER_CMD__))
#include <toolchain/gcc.h>
#else
/* This include line exists for off-tree definitions of compilers,
* and therefore this header is not meant to exist in-tree
*/
#include <toolchain/other.h>
#endif
/*
* Ensure that __BYTE_ORDER__ and related preprocessor definitions are defined,
* as these are often used without checking for definition and doing so can
* cause unexpected behaviours.
*/
#ifndef _LINKER
#if !defined(__BYTE_ORDER__) || !defined(__ORDER_BIG_ENDIAN__) || \
!defined(__ORDER_LITTLE_ENDIAN__)
#error "__BYTE_ORDER__ is not defined"
#endif
#endif /* !_LINKER */
#endif /* ZEPHYR_INCLUDE_TOOLCHAIN_H_ */
@@ -0,0 +1,222 @@
/*
* Copyright (c) 2010-2014 Wind River Systems, Inc.
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef ZEPHYR_INCLUDE_TOOLCHAIN_COMMON_H_
#define ZEPHYR_INCLUDE_TOOLCHAIN_COMMON_H_
/**
* @file
* @brief Common toolchain abstraction
*
* Macros to abstract compiler capabilities (common to all toolchains).
*/
/* Abstract use of extern keyword for compatibility between C and C++ */
#ifdef __cplusplus
#define EXTERN_C extern "C"
#else
#define EXTERN_C extern
#endif
/* Use TASK_ENTRY_CPP to tag task entry points defined in C++ files. */
#ifdef __cplusplus
#define TASK_ENTRY_CPP extern "C"
#endif
/*
* Generate a reference to an external symbol.
* The reference indicates to the linker that the symbol is required
* by the module containing the reference and should be included
* in the image if the module is in the image.
*
* The assembler directive ".set" is used to define a local symbol.
* No memory is allocated, and the local symbol does not appear in
* the symbol table.
*/
#ifdef _ASMLANGUAGE
#define REQUIRES(sym) .set sym ## _Requires, sym
#else
#define REQUIRES(sym) __asm__ (".set " # sym "_Requires, " # sym "\n\t");
#endif
#ifdef _ASMLANGUAGE
#define SECTION .section
#endif
/*
* If the project is being built for speed (i.e. not for minimum size) then
* align functions and branches in executable sections to improve performance.
*/
#ifdef _ASMLANGUAGE
#if defined(CONFIG_X86)
#ifdef PERF_OPT
#define PERFOPT_ALIGN .balign 16
#else
#define PERFOPT_ALIGN .balign 1
#endif
#elif defined(CONFIG_ARM) || defined(CONFIG_ARM64)
#define PERFOPT_ALIGN .balign 4
#elif defined(CONFIG_ARC)
/* .align assembler directive is supposed by all ARC toolchains and it is
* implemented in a same way across ARC toolchains.
*/
#define PERFOPT_ALIGN .align 4
#elif defined(CONFIG_NIOS2) || defined(CONFIG_RISCV) || \
defined(CONFIG_XTENSA)
#define PERFOPT_ALIGN .balign 4
#elif defined(CONFIG_ARCH_POSIX)
#elif defined(CONFIG_SPARC)
#define PERFOPT_ALIGN .align 4
#else
#error Architecture unsupported
#endif
#define GC_SECTION(sym) SECTION .text.##sym, "ax"
#endif /* _ASMLANGUAGE */
/* force inlining a function */
#if !defined(_ASMLANGUAGE)
#ifdef CONFIG_COVERAGE
/*
* The always_inline attribute forces a function to be inlined,
* even ignoring -fno-inline. So for code coverage, do not
* force inlining of these functions to keep their bodies around
* so their number of executions can be counted.
*
* Note that "inline" is kept here for kobject_hash.c and
* priv_stacks_hash.c. These are built without compiler flags
* used for coverage. ALWAYS_INLINE cannot be empty as compiler
* would complain about unused functions. Attaching unused
* attribute would result in their text sections balloon more than
* 10 times in size, as those functions are kept in text section.
* So just keep "inline" here.
*/
#define ALWAYS_INLINE inline
#else
#define ALWAYS_INLINE inline __attribute__((always_inline))
#endif
#endif
#define Z_STRINGIFY(x) #x
#define STRINGIFY(s) Z_STRINGIFY(s)
/* concatenate the values of the arguments into one */
#define _DO_CONCAT(x, y) x ## y
#define _CONCAT(x, y) _DO_CONCAT(x, y)
/* Additionally used as a sentinel by gen_syscalls.py to identify what
* functions are system calls
*
* Note POSIX unit tests don't still generate the system call stubs, so
* until https://github.com/zephyrproject-rtos/zephyr/issues/5006 is
* fixed via possibly #4174, we introduce this hack -- which will
* disallow us to test system calls in POSIX unit testing (currently
* not used).
*/
#ifndef ZTEST_UNITTEST
#define __syscall static inline
#else
#define __syscall
#endif /* ZTEST_UNITTEST */
/* Definitions for struct declaration tags. These are sentinel values used by
* parse_syscalls.py to gather a list of names of struct declarations that
* have these tags applied for them.
*/
/* Indicates this is a driver subsystem */
#define __subsystem
/* Indicates this is a network socket object */
#define __net_socket
#ifndef BUILD_ASSERT
/* Compile-time assertion that makes the build to fail.
* Common implementation swallows the message.
*/
#define BUILD_ASSERT(EXPR, MSG...) \
enum _CONCAT(__build_assert_enum, __COUNTER__) { \
_CONCAT(__build_assert, __COUNTER__) = 1 / !!(EXPR) \
}
#endif
#ifndef BUILD_ASSERT_MSG
#define BUILD_ASSERT_MSG(EXPR, MSG) __DEPRECATED_MACRO BUILD_ASSERT(EXPR, MSG)
#endif
/*
* This is meant to be used in conjunction with __in_section() and similar
* where scattered structure instances are concatenated together by the linker
* and walked by the code at run time just like a contiguous array of such
* structures.
*
* Assemblers and linkers may insert alignment padding by default whose
* size is larger than the natural alignment for those structures when
* gathering various section segments together, messing up the array walk.
* To prevent this, we need to provide an explicit alignment not to rely
* on the default that might just work by luck.
*
* Alignment statements in linker scripts are not sufficient as
* the assembler may add padding by itself to each segment when switching
* between sections within the same file even if it merges many such segments
* into a single section in the end.
*/
#define Z_DECL_ALIGN(type) __aligned(__alignof(type)) type
/*
* Convenience helper combining __in_section() and Z_DECL_ALIGN().
* The section name is the struct type prepended with an underscore.
* The subsection is "static" and the subsubsection is the variable name.
*
* In the linker script, create output sections for these using
* Z_ITERABLE_SECTION_ROM or Z_ITERABLE_SECTION_RAM.
*/
#define Z_STRUCT_SECTION_ITERABLE(struct_type, name) \
Z_DECL_ALIGN(struct struct_type) name \
__in_section(_##struct_type, static, name) __used
/* Special variant of Z_STRUCT_SECTION_ITERABLE, for placing alternate
* data types within the iterable section of a specific data type. The
* data type sizes and semantics must be equivalent!
*/
#define Z_STRUCT_SECTION_ITERABLE_ALTERNATE(out_type, struct_type, name) \
Z_DECL_ALIGN(struct struct_type) name \
__in_section(_##out_type, static, name) __used
/*
* Iterator for structure instances gathered by Z_STRUCT_SECTION_ITERABLE().
* The linker must provide a _<struct_type>_list_start symbol and a
* _<struct_type>_list_end symbol to mark the start and the end of the
* list of struct objects to iterate over.
*/
#define Z_STRUCT_SECTION_FOREACH(struct_type, iterator) \
extern struct struct_type _CONCAT(_##struct_type, _list_start)[]; \
extern struct struct_type _CONCAT(_##struct_type, _list_end)[]; \
for (struct struct_type *iterator = \
_CONCAT(_##struct_type, _list_start); \
({ __ASSERT(iterator <= _CONCAT(_##struct_type, _list_end), \
"unexpected list end location"); \
iterator < _CONCAT(_##struct_type, _list_end); }); \
iterator++)
#endif /* ZEPHYR_INCLUDE_TOOLCHAIN_COMMON_H_ */
@@ -0,0 +1,573 @@
/*
* Copyright (c) 2010-2014,2017 Wind River Systems, Inc.
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef ZEPHYR_INCLUDE_TOOLCHAIN_GCC_H_
#define ZEPHYR_INCLUDE_TOOLCHAIN_GCC_H_
/**
* @file
* @brief GCC toolchain abstraction
*
* Macros to abstract compiler capabilities for GCC toolchain.
*/
/*
* Older versions of GCC do not define __BYTE_ORDER__, so it must be manually
* detected and defined using arch-specific definitions.
*/
#ifndef _LINKER
#ifndef __ORDER_BIG_ENDIAN__
#define __ORDER_BIG_ENDIAN__ (1)
#endif
#ifndef __ORDER_LITTLE_ENDIAN__
#define __ORDER_LITTLE_ENDIAN__ (2)
#endif
#ifndef __BYTE_ORDER__
#if defined(__BIG_ENDIAN__) || defined(__ARMEB__) || \
defined(__THUMBEB__) || defined(__AARCH64EB__) || \
defined(__MIPSEB__) || defined(__TC32EB__)
#define __BYTE_ORDER__ __ORDER_BIG_ENDIAN__
#elif defined(__LITTLE_ENDIAN__) || defined(__ARMEL__) || \
defined(__THUMBEL__) || defined(__AARCH64EL__) || \
defined(__MIPSEL__) || defined(__TC32EL__)
#define __BYTE_ORDER__ __ORDER_LITTLE_ENDIAN__
#else
#error "__BYTE_ORDER__ is not defined and cannot be automatically resolved"
#endif
#endif
/* C++11 has static_assert built in */
#ifdef __cplusplus
#define BUILD_ASSERT(EXPR, MSG...) static_assert(EXPR, "" MSG)
#define BUILD_ASSERT_MSG(EXPR, MSG) __DEPRECATED_MACRO BUILD_ASSERT(EXPR, MSG)
/*
* GCC 4.6 and higher have the C11 _Static_assert built in, and its
* output is easier to understand than the common BUILD_ASSERT macros.
*/
#elif (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) || \
(__STDC_VERSION__) >= 201100
#define BUILD_ASSERT(EXPR, MSG...) _Static_assert(EXPR, "" MSG)
#define BUILD_ASSERT_MSG(EXPR, MSG) __DEPRECATED_MACRO BUILD_ASSERT(EXPR, MSG)
#else
#define BUILD_ASSERT(EXPR, MSG...)
#define BUILD_ASSERT_MSG(EXPR, MSG)
#endif
#include <toolchain/common.h>
#include <stdbool.h>
#define ALIAS_OF(of) __attribute__((alias(#of)))
#define FUNC_ALIAS(real_func, new_alias, return_type) \
return_type new_alias() ALIAS_OF(real_func)
#if defined(CONFIG_ARCH_POSIX)
#include <arch/posix/posix_trace.h>
/*let's not segfault if this were to happen for some reason*/
#define CODE_UNREACHABLE \
{\
posix_print_error_and_exit("CODE_UNREACHABLE reached from %s:%d\n",\
__FILE__, __LINE__);\
__builtin_unreachable(); \
}
#else
#define CODE_UNREACHABLE __builtin_unreachable()
#endif
#define FUNC_NORETURN __attribute__((__noreturn__))
/* The GNU assembler for Cortex-M3 uses # for immediate values, not
* comments, so the @nobits# trick does not work.
*/
#if defined(CONFIG_ARM) || defined(CONFIG_ARM64)
#define _NODATA_SECTION(segment) __attribute__((section(#segment)))
#else
#define _NODATA_SECTION(segment) \
__attribute__((section(#segment ",\"wa\",@nobits#")))
#endif
/* Unaligned access */
#define UNALIGNED_GET(p) \
__extension__ ({ \
struct __attribute__((__packed__)) { \
__typeof__(*(p)) __v; \
} *__p = (__typeof__(__p)) (p); \
__p->__v; \
})
#if __GNUC__ >= 7 && (defined(CONFIG_ARM) || defined(CONFIG_ARM64))
/* Version of UNALIGNED_PUT() which issues a compiler_barrier() after
* the store. It is required to workaround an apparent optimization
* bug in GCC for ARM Cortex-M3 and higher targets, when multiple
* byte, half-word and word stores (strb, strh, str instructions),
* which support unaligned access, can be coalesced into store double
* (strd) instruction, which doesn't support unaligned access (the
* compilers in question do this optimization ignoring __packed__
* attribute).
*/
#define UNALIGNED_PUT(v, p) \
do { \
struct __attribute__((__packed__)) { \
__typeof__(*p) __v; \
} *__p = (__typeof__(__p)) (p); \
__p->__v = (v); \
compiler_barrier(); \
} while (false)
#else
#define UNALIGNED_PUT(v, p) \
do { \
struct __attribute__((__packed__)) { \
__typeof__(*p) __v; \
} *__p = (__typeof__(__p)) (p); \
__p->__v = (v); \
} while (false)
#endif
/* Double indirection to ensure section names are expanded before
* stringification
*/
#define __GENERIC_SECTION(segment) __attribute__((section(STRINGIFY(segment))))
#define Z_GENERIC_SECTION(segment) __GENERIC_SECTION(segment)
#define __GENERIC_DOT_SECTION(segment) \
__attribute__((section("." STRINGIFY(segment))))
#define Z_GENERIC_DOT_SECTION(segment) __GENERIC_DOT_SECTION(segment)
#define ___in_section(a, b, c) \
__attribute__((section("." Z_STRINGIFY(a) \
"." Z_STRINGIFY(b) \
"." Z_STRINGIFY(c))))
#define __in_section(a, b, c) ___in_section(a, b, c)
#define __in_section_unique(seg) ___in_section(seg, __FILE__, __COUNTER__)
/* When using XIP, using '__ramfunc' places a function into RAM instead
* of FLASH. Make sure '__ramfunc' is defined only when
* CONFIG_ARCH_HAS_RAMFUNC_SUPPORT is defined, so that the compiler can
* report an error if '__ramfunc' is used but the architecture does not
* support it.
*/
#if !defined(CONFIG_XIP)
#define __ramfunc
#elif defined(CONFIG_ARCH_HAS_RAMFUNC_SUPPORT)
#define __ramfunc __attribute__((noinline)) \
__attribute__((long_call, section(".ramfunc")))
#endif /* !CONFIG_XIP */
#ifndef __fallthrough
#if __GNUC__ >= 7
#define __fallthrough __attribute__((fallthrough))
#else
#define __fallthrough
#endif /* __GNUC__ >= 7 */
#endif
#ifndef __packed
#define __packed __attribute__((__packed__))
#endif
#ifndef __aligned
#define __aligned(x) __attribute__((__aligned__(x)))
#endif
#define __may_alias __attribute__((__may_alias__))
#ifndef __printf_like
#define __printf_like(f, a) __attribute__((format (printf, f, a)))
#endif
#define __used __attribute__((__used__))
#ifndef __deprecated
#define __deprecated __attribute__((deprecated))
#endif
#ifndef __attribute_const__
#define __attribute_const__ __attribute__((__const__))
#endif
#define ARG_UNUSED(x) (void)(x)
#define likely(x) __builtin_expect((bool)!!(x), true)
#define unlikely(x) __builtin_expect((bool)!!(x), false)
#define popcount(x) __builtin_popcount(x)
#ifndef __no_optimization
#define __no_optimization __attribute__((optimize("-O0")))
#endif
#ifndef __weak
#define __weak __attribute__((__weak__))
#endif
#define __unused __attribute__((__unused__))
/* Builtins with availability that depend on the compiler version. */
#if __GNUC__ >= 5
#define HAS_BUILTIN___builtin_add_overflow 1
#define HAS_BUILTIN___builtin_sub_overflow 1
#define HAS_BUILTIN___builtin_mul_overflow 1
#define HAS_BUILTIN___builtin_div_overflow 1
#endif
#if __GNUC__ >= 4
#define HAS_BUILTIN___builtin_clz 1
#define HAS_BUILTIN___builtin_clzl 1
#define HAS_BUILTIN___builtin_clzll 1
#define HAS_BUILTIN___builtin_ctz 1
#define HAS_BUILTIN___builtin_ctzl 1
#define HAS_BUILTIN___builtin_ctzll 1
#endif
/*
* Be *very* careful with these. You cannot filter out __DEPRECATED_MACRO with
* -wno-deprecated, which has implications for -Werror.
*/
/*
* Expands to nothing and generates a warning. Used like
*
* #define FOO __WARN("Please use BAR instead") ...
*
* The warning points to the location where the macro is expanded.
*/
#define __WARN(msg) __WARN1(GCC warning msg)
#define __WARN1(s) _Pragma(#s)
/* Generic message */
#ifndef __DEPRECATED_MACRO
#define __DEPRECATED_MACRO __WARN("Macro is deprecated")
#endif
/* These macros allow having ARM asm functions callable from thumb */
#if defined(_ASMLANGUAGE)
#if defined(CONFIG_ARM)
#if defined(CONFIG_ASSEMBLER_ISA_THUMB2)
#define FUNC_CODE() .thumb;
#define FUNC_INSTR(a)
#else
#define FUNC_CODE() .code 32
#define FUNC_INSTR(a)
#endif /* CONFIG_ASSEMBLER_ISA_THUMB2 */
#else
#define FUNC_CODE()
#define FUNC_INSTR(a)
#endif /* CONFIG_ARM */
#endif /* _ASMLANGUAGE */
/*
* These macros are used to declare assembly language symbols that need
* to be typed properly(func or data) to be visible to the OMF tool.
* So that the build tool could mark them as an entry point to be linked
* correctly. This is an elfism. Use #if 0 for a.out.
*/
#if defined(_ASMLANGUAGE)
#if defined(CONFIG_ARM) || defined(CONFIG_NIOS2) || defined(CONFIG_RISCV) \
|| defined(CONFIG_XTENSA) || defined(CONFIG_ARM64)
#define GTEXT(sym) .global sym; .type sym, %function
#define GDATA(sym) .global sym; .type sym, %object
#define WTEXT(sym) .weak sym; .type sym, %function
#define WDATA(sym) .weak sym; .type sym, %object
#elif defined(CONFIG_ARC)
/*
* Need to use assembly macros because ';' is interpreted as the start of
* a single line comment in the ARC assembler.
*/
.macro glbl_text symbol
.globl \symbol
.type \symbol, %function
.endm
.macro glbl_data symbol
.globl \symbol
.type \symbol, %object
.endm
.macro weak_data symbol
.weak \symbol
.type \symbol, %object
.endm
#define GTEXT(sym) glbl_text sym
#define GDATA(sym) glbl_data sym
#define WDATA(sym) weak_data sym
#else /* !CONFIG_ARM && !CONFIG_ARC */
#define GTEXT(sym) .globl sym; .type sym, @function
#define GDATA(sym) .globl sym; .type sym, @object
#endif
/*
* These macros specify the section in which a given function or variable
* resides.
*
* - SECTION_FUNC allows only one function to reside in a sub-section
* - SECTION_SUBSEC_FUNC allows multiple functions to reside in a sub-section
* This ensures that garbage collection only discards the section
* if all functions in the sub-section are not referenced.
*/
#if defined(CONFIG_ARC)
/*
* Need to use assembly macros because ';' is interpreted as the start of
* a single line comment in the ARC assembler.
*
* Also, '\()' is needed in the .section directive of these macros for
* correct substitution of the 'section' variable.
*/
.macro section_var section, symbol
.section .\section\().\symbol
\symbol :
.endm
.macro section_func section, symbol
.section .\section\().\symbol, "ax"
FUNC_CODE()
PERFOPT_ALIGN
\symbol :
FUNC_INSTR(\symbol)
.endm
.macro section_subsec_func section, subsection, symbol
.section .\section\().\subsection, "ax"
PERFOPT_ALIGN
\symbol :
.endm
#define SECTION_VAR(sect, sym) section_var sect, sym
#define SECTION_FUNC(sect, sym) section_func sect, sym
#define SECTION_SUBSEC_FUNC(sect, subsec, sym) \
section_subsec_func sect, subsec, sym
#else /* !CONFIG_ARC */
#define SECTION_VAR(sect, sym) .section .sect.sym; sym:
#define SECTION_FUNC(sect, sym) \
.section .sect.sym, "ax"; \
FUNC_CODE() \
PERFOPT_ALIGN; sym : \
FUNC_INSTR(sym)
#define SECTION_SUBSEC_FUNC(sect, subsec, sym) \
.section .sect.subsec, "ax"; PERFOPT_ALIGN; sym :
#endif /* CONFIG_ARC */
#endif /* _ASMLANGUAGE */
#if defined(_ASMLANGUAGE)
#if defined(CONFIG_ARM)
#if defined(CONFIG_ASSEMBLER_ISA_THUMB2)
/* '.syntax unified' is a gcc-ism used in thumb-2 asm files */
#define _ASM_FILE_PROLOGUE .text; .syntax unified; .thumb
#else
#define _ASM_FILE_PROLOGUE .text; .code 32
#endif /* CONFIG_ASSEMBLER_ISA_THUMB2 */
#elif defined(CONFIG_ARM64)
#define _ASM_FILE_PROLOGUE .text
#endif /* CONFIG_ARM64 || CONFIG_ARM */
#endif /* _ASMLANGUAGE */
/*
* These macros generate absolute symbols for GCC
*/
/* create an extern reference to the absolute symbol */
#define GEN_OFFSET_EXTERN(name) extern const char name[]
#define GEN_ABS_SYM_BEGIN(name) \
EXTERN_C void name(void); \
void name(void) \
{
#define GEN_ABS_SYM_END }
/*
* Note that GEN_ABSOLUTE_SYM(), depending on the architecture
* and toolchain, may restrict the range of values permitted
* for assignment to the named symbol.
*
* For example, on x86, "value" is interpreated as signed
* 32-bit integer. Passing in an unsigned 32-bit integer
* with MSB set would result in a negative integer.
* Moreover, GCC would error out if an integer larger
* than 2^32-1 is passed as "value".
*/
/*
* GEN_ABSOLUTE_SYM_KCONFIG() is outputted by the build system
* to generate named symbol/value pairs for kconfigs.
*/
#if defined(CONFIG_ARM)
/*
* GNU/ARM backend does not have a proper operand modifier which does not
* produces prefix # followed by value, such as %0 for PowerPC, Intel, and
* MIPS. The workaround performed here is using %B0 which converts
* the value to ~(value). Thus "n"(~(value)) is set in operand constraint
* to output (value) in the ARM specific GEN_OFFSET macro.
*/
#define GEN_ABSOLUTE_SYM(name, value) \
__asm__(".globl\t" #name "\n\t.equ\t" #name \
",%B0" \
"\n\t.type\t" #name ",%%object" : : "n"(~(value)))
#define GEN_ABSOLUTE_SYM_KCONFIG(name, value) \
__asm__(".globl\t" #name \
"\n\t.equ\t" #name "," #value \
"\n\t.type\t" #name ",%object")
#elif defined(CONFIG_X86)
#define GEN_ABSOLUTE_SYM(name, value) \
__asm__(".globl\t" #name "\n\t.equ\t" #name \
",%c0" \
"\n\t.type\t" #name ",@object" : : "n"(value))
#define GEN_ABSOLUTE_SYM_KCONFIG(name, value) \
__asm__(".globl\t" #name \
"\n\t.equ\t" #name "," #value \
"\n\t.type\t" #name ",@object")
#elif defined(CONFIG_ARC) || defined(CONFIG_ARM64)
#define GEN_ABSOLUTE_SYM(name, value) \
__asm__(".globl\t" #name "\n\t.equ\t" #name \
",%c0" \
"\n\t.type\t" #name ",@object" : : "n"(value))
#define GEN_ABSOLUTE_SYM_KCONFIG(name, value) \
__asm__(".globl\t" #name \
"\n\t.equ\t" #name "," #value \
"\n\t.type\t" #name ",@object")
#elif defined(CONFIG_NIOS2) || defined(CONFIG_RISCV) || defined(CONFIG_XTENSA)
/* No special prefixes necessary for constants in this arch AFAICT */
#define GEN_ABSOLUTE_SYM(name, value) \
__asm__(".globl\t" #name "\n\t.equ\t" #name \
",%0" \
"\n\t.type\t" #name ",%%object" : : "n"(value))
#define GEN_ABSOLUTE_SYM_KCONFIG(name, value) \
__asm__(".globl\t" #name \
"\n\t.equ\t" #name "," #value \
"\n\t.type\t" #name ",%object")
#elif defined(CONFIG_ARCH_POSIX)
#define GEN_ABSOLUTE_SYM(name, value) \
__asm__(".globl\t" #name "\n\t.equ\t" #name \
",%c0" \
"\n\t.type\t" #name ",@object" : : "n"(value))
#define GEN_ABSOLUTE_SYM_KCONFIG(name, value) \
__asm__(".globl\t" #name \
"\n\t.equ\t" #name "," #value \
"\n\t.type\t" #name ",@object")
#elif defined(CONFIG_SPARC)
#define GEN_ABSOLUTE_SYM(name, value) \
__asm__(".global\t" #name "\n\t.equ\t" #name \
",%0" \
"\n\t.type\t" #name ",#object" : : "n"(value))
#define GEN_ABSOLUTE_SYM_KCONFIG(name, value) \
__asm__(".globl\t" #name \
"\n\t.equ\t" #name "," #value \
"\n\t.type\t" #name ",#object")
#else
#error processor architecture not supported
#endif
#define compiler_barrier() do { \
__asm__ __volatile__ ("" ::: "memory"); \
} while (false)
/** @brief Return larger value of two provided expressions.
*
* Macro ensures that expressions are evaluated only once.
*
* @note Macro has limited usage compared to the standard macro as it cannot be
* used:
* - to generate constant integer, e.g. __aligned(Z_MAX(4,5))
* - static variable, e.g. array like static uint8_t array[Z_MAX(...)];
*/
#define Z_MAX(a, b) ({ \
/* random suffix to avoid naming conflict */ \
__typeof__(a) _value_a_ = (a); \
__typeof__(b) _value_b_ = (b); \
_value_a_ > _value_b_ ? _value_a_ : _value_b_; \
})
/** @brief Return smaller value of two provided expressions.
*
* Macro ensures that expressions are evaluated only once. See @ref Z_MAX for
* macro limitations.
*/
#define Z_MIN(a, b) ({ \
/* random suffix to avoid naming conflict */ \
__typeof__(a) _value_a_ = (a); \
__typeof__(b) _value_b_ = (b); \
_value_a_ < _value_b_ ? _value_a_ : _value_b_; \
})
/** @brief Return a value clamped to a given range.
*
* Macro ensures that expressions are evaluated only once. See @ref Z_MAX for
* macro limitations.
*/
#define Z_CLAMP(val, low, high) ({ \
/* random suffix to avoid naming conflict */ \
__typeof__(val) _value_val_ = (val); \
__typeof__(low) _value_low_ = (low); \
__typeof__(high) _value_high_ = (high); \
(_value_val_ < _value_low_) ? _value_low_ : \
(_value_val_ > _value_high_) ? _value_high_ : \
_value_val_; \
})
/**
* @brief Calculate power of two ceiling for some nonzero value
*
* @param x Nonzero unsigned long value
* @return X rounded up to the next power of two
*/
#ifdef CONFIG_64BIT
#define Z_POW2_CEIL(x) ((1UL << (63U - __builtin_clzl(x))) < x ? \
1UL << (63U - __builtin_clzl(x) + 1U) : \
1UL << (63U - __builtin_clzl(x)))
#else
#define Z_POW2_CEIL(x) ((1UL << (31U - __builtin_clzl(x))) < x ? \
1UL << (31U - __builtin_clzl(x) + 1U) : \
1UL << (31U - __builtin_clzl(x)))
#endif
#endif /* !_LINKER */
#endif /* ZEPHYR_INCLUDE_TOOLCHAIN_GCC_H_ */
@@ -0,0 +1,39 @@
/*
* Copyright (c) 2017 Linaro Limited
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef ZEPHYR_INCLUDE_ZEPHYR_TYPES_H_
#define ZEPHYR_INCLUDE_ZEPHYR_TYPES_H_
#include <stddef.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
/* 32 bits on ILP32 builds, 64 bits on LP64 builts */
typedef unsigned long ulong_t;
/*
* A type with strong alignment requiremnts, similar to C11 max_align_t. It can
* be used to force alignment of data structures allocated on the stack or as
* return * type for heap allocators.
*/
typedef union {
long long thelonglong;
long double thelongdouble;
uintmax_t theuintmax_t;
size_t thesize_t;
uintptr_t theuintptr_t;
void *thepvoid;
void (*thepfunc)(void);
} z_max_align_t;
#ifdef __cplusplus
}
#endif
#endif /* ZEPHYR_INCLUDE_ZEPHYR_TYPES_H_ */
@@ -0,0 +1,220 @@
/*
* Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd.
*
* Attention: This software (modified or not) and binary are used for
* microcontroller manufactured by Nanjing Qinheng Microelectronics.
*/
#include "config.h"
#include "ancs_client.h"
#include "ancs_client_internal.h"
#include "ancs_app_attr_get.h"
#ifdef CONFIG_ANCS_APP_ATTR_GET_DBG
#define LOG_DBG PRINT
#else
#define LOG_DBG
#endif
struct buf_simple {
/** Pointer to the start of data in the buffer. */
uint8_t *data;
/**
* Length of the data behind the data pointer.
*
* #size not determine the max length!
*/
uint16_t len;
/** Amount of data that buf_simple#__buf can store. */
uint16_t size;
/** Start of the data storage. Not to be accessed directly
* (the data pointer should be used instead).
*/
uint8_t *__buf;
};
/**@brief Enumeration for keeping track of the state-based encoding while requesting
* app attributes.
*/
enum encode_app_attr {
/** Currently encoding the command ID. */
APP_ATTR_COMMAND_ID,
/** Currently encoding the app ID. */
APP_ATTR_APP_ID,
/** Currently encoding the attribute ID. */
APP_ATTR_ATTR_ID,
/** Encoding done. */
APP_ATTR_DONE,
/** Encoding aborted. */
APP_ATTR_ABORT
};
/**@brief Function for determining whether an attribute is requested.
*
* @param[in] ancs_c iOS notification structure. This structure must be supplied by
* the application. It identifies the particular client instance to use.
*
* @return True If it is requested
* @return False If it is not requested.
*/
static bool app_attr_is_requested(struct bt_ancs_client *ancs_c,
uint32_t attr_id)
{
return ancs_c->ancs_app_attr_list[attr_id].get;
}
/**@brief Function for encoding the command ID as part of assembling a "get app attributes" command.
*
* @param[in,out] buf Pointer to the BLE GATT write buffer structure.
*/
static enum encode_app_attr app_attr_encode_cmd_id(struct buf_simple *buf)
{
LOG_DBG("Encoding command ID.");
size_t tailroom = buf->size - (buf->data - buf->__buf) - buf->len;
if(tailroom < 1) {
return APP_ATTR_ABORT;
}
/* Encode Command ID. */
*(buf->data + buf->len) = BT_ANCS_COMMAND_ID_GET_APP_ATTRIBUTES;
buf->len += 1;
return APP_ATTR_APP_ID;
}
/**@brief Function for encoding the app ID as part of assembling a "get app attributes" command.
*
* @param[in,out] buf Pointer to the BLE GATT write buffer structure.
* @param[in] app_id The app ID of the app for which to request app attributes.
* @param[in] app_id_len Length of the app ID.
*/
static enum encode_app_attr app_attr_encode_app_id(struct buf_simple *buf,
const uint8_t *app_id,
const uint32_t app_id_len)
{
LOG_DBG("Encoding app ID.");
size_t tailroom = buf->size - (buf->data - buf->__buf) - buf->len;
if (tailroom < app_id_len + 1) {
return APP_ATTR_ABORT;
}
/* Encode app identifier. */
tmos_memcpy((buf->data + buf->len), app_id, app_id_len);
buf->len += app_id_len;
*(buf->data + buf->len) = '\0';
buf->len += 1;
return APP_ATTR_ATTR_ID;
}
/**@brief Function for encoding the attribute ID as part of assembling a
* "get app attributes" command.
*
* @param[in] ancs_c iOS notification structure. This structure must be supplied by
* the application. It identifies the particular client instance to use.
* @param[in,out] buf Pointer to the BLE GATT write buffer structure.
*/
static enum encode_app_attr app_attr_encode_attr_id(
struct bt_ancs_client *ancs_c, struct buf_simple *buf)
{
uint32_t i;
LOG_DBG("Encoding attribute ID.");
ancs_c->number_of_requested_attr = 0;
/* Encode Attribute ID. */
for (i = 0; i < BT_ANCS_APP_ATTR_COUNT; i++) {
if (app_attr_is_requested(ancs_c, i)) {
size_t tailroom = buf->size - (buf->data - buf->__buf) - buf->len;
if (tailroom < 1) {
return APP_ATTR_ABORT;
}
*(buf->data + buf->len) = i;
buf->len += 1;
ancs_c->number_of_requested_attr++;
}
}
return APP_ATTR_DONE;
}
/**@brief Function for sending a "get app attributes" request.
*
* @details Since the app ID may not fit in a single write, long write
* with a state machine is used to encode the "get app attributes" request.
*
* @param[in] ancs_c iOS notification structure. This structure must be supplied by
* the application. It identifies the particular client instance to use.
* @param[in] app_id The app ID of the app for which to request app attributes.
* @param[in] app_id_len Length of the app ID.
*/
static int app_attr_get(struct bt_ancs_client *ancs_c, const uint8_t *app_id,
uint32_t app_id_len, bt_ancs_write_cb func)
{
enum encode_app_attr state = APP_ATTR_COMMAND_ID;
int err;
if (atomic_test_and_set_bit(&ancs_c->state, ANCS_CP_WRITE_PENDING)) {
return -EBUSY;
}
struct buf_simple buf;
buf.__buf = ancs_c->cp_data;
buf.len = 0U;
buf.size = sizeof(ancs_c->cp_data);
while (state != APP_ATTR_DONE && state != APP_ATTR_ABORT) {
switch (state) {
case APP_ATTR_COMMAND_ID:
state = app_attr_encode_cmd_id(&buf);
break;
case APP_ATTR_APP_ID:
state = app_attr_encode_app_id(&buf,
app_id, app_id_len);
break;
case APP_ATTR_ATTR_ID:
state = app_attr_encode_attr_id(ancs_c, &buf);
break;
case APP_ATTR_DONE:
break;
case APP_ATTR_ABORT:
break;
default:
break;
}
}
if (state == APP_ATTR_DONE) {
bt_ancs_cp_write(ancs_c, buf.len, func);
ancs_c->parse_info.expected_number_of_attrs =
ancs_c->number_of_requested_attr;
} else {
err = -ENOMEM;
}
return err;
}
int bt_ancs_app_attr_request(struct bt_ancs_client *ancs_c,
const uint8_t *app_id, uint32_t len,
bt_ancs_write_cb func)
{
if (!len) {
return -EINVAL;
}
ancs_c->parse_info.parse_state = BT_ANCS_PARSE_STATE_COMMAND_ID;
return app_attr_get(ancs_c, app_id, len, func);
}
@@ -0,0 +1,45 @@
/*
* Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd.
*
* Attention: This software (modified or not) and binary are used for
* microcontroller manufactured by Nanjing Qinheng Microelectronics.
*/
#ifndef BT_ANCS_APP_ATTR_GET_H__
#define BT_ANCS_APP_ATTR_GET_H__
/** @file
*
* @addtogroup bt_ancs_client
* @{
*/
#include "ancs_client.h"
#ifdef __cplusplus
extern "C" {
#endif
/**@brief Function for requesting attributes for an app.
*
* @param[in] ancs_c ANCS client instance.
* @param[in] app_id App identifier of the app for which to request app attributes.
* @param[in] len Length of the app identifier.
* @param[in] func Callback function for handling NP write response.
*
* @retval 0 If all operations were successful.
* Otherwise, a (negative) error code is returned.
*/
int bt_ancs_app_attr_request(struct bt_ancs_client *ancs_c,
const uint8_t *app_id, uint32_t len,
bt_ancs_write_cb func);
#ifdef __cplusplus
}
#endif
/**
* @}
*/
#endif /* BT_ANCS_APP_ATTR_GET_H__ */
@@ -0,0 +1,366 @@
/*
* Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd.
*
* Attention: This software (modified or not) and binary are used for
* microcontroller manufactured by Nanjing Qinheng Microelectronics.
*/
#include <sys/byteorder.h>
#include "ancs_client.h"
#include "ancs_client_internal.h"
#include "ancs_attr_parser.h"
#include "config.h"
#ifdef CONFIG_ANCS_ATTR_PARSER_DBG
#define LOG_DBG PRINT
#else
#define LOG_DBG
#endif
static bool all_req_attrs_parsed(struct bt_ancs_client *ancs_c)
{
return !ancs_c->parse_info.expected_number_of_attrs;
}
static bool attr_is_requested(struct bt_ancs_client *ancs_c,
struct bt_ancs_attr attr)
{
return ancs_c->parse_info.attr_list[attr.attr_id].get;
}
/**@brief Function for parsing command id and notification id.
* Used in the @ref parse_get_notif_attrs_response state machine.
*
* @details UID and command ID will be received only once at the beginning of the first
* GATT notification of a new attribute request for a given iOS notification.
*
* @param[in] ancs_c Pointer to an ANCS instance to which the event belongs.
* @param[in] data_src Pointer to data that was received from the Notification Provider.
* @param[in] index Pointer to an index that helps us keep track of the current data
* to be parsed.
*
* @return The next parse state.
*/
static enum bt_ancs_parse_state command_id_parse(struct bt_ancs_client *ancs_c,
const uint8_t *data_src,
uint32_t *index)
{
enum bt_ancs_parse_state parse_state;
ancs_c->parse_info.command_id =
(enum bt_ancs_cmd_id_val)data_src[(*index)++];
switch (ancs_c->parse_info.command_id) {
case BT_ANCS_COMMAND_ID_GET_NOTIF_ATTRIBUTES:
ancs_c->attr_response.command_id = BT_ANCS_COMMAND_ID_GET_NOTIF_ATTRIBUTES;
ancs_c->parse_info.attr_list = ancs_c->ancs_notif_attr_list;
ancs_c->parse_info.attr_count = BT_ANCS_NOTIF_ATTR_COUNT;
parse_state = BT_ANCS_PARSE_STATE_NOTIF_UID;
break;
case BT_ANCS_COMMAND_ID_GET_APP_ATTRIBUTES:
ancs_c->attr_response.command_id = BT_ANCS_COMMAND_ID_GET_APP_ATTRIBUTES;
ancs_c->parse_info.attr_list = ancs_c->ancs_app_attr_list;
ancs_c->parse_info.attr_count = BT_ANCS_APP_ATTR_COUNT;
ancs_c->parse_info.current_app_id_index = 0;
parse_state = BT_ANCS_PARSE_STATE_APP_ID;
break;
default:
/* no valid command_id, abort the rest of the parsing procedure. */
LOG_DBG("Invalid Command ID\n");
parse_state = BT_ANCS_PARSE_STATE_DONE;
break;
}
return parse_state;
}
static enum bt_ancs_parse_state notif_uid_parse(struct bt_ancs_client *ancs_c,
const uint8_t *data_src,
uint32_t *index)
{
ancs_c->attr_response.notif_uid = sys_get_le32(&data_src[*index]);
*index += sizeof(uint32_t);
return BT_ANCS_PARSE_STATE_ATTR_ID;
}
static enum bt_ancs_parse_state app_id_parse(struct bt_ancs_client *ancs_c,
const uint8_t *data_src,
uint32_t *index)
{
if (ancs_c->parse_info.current_app_id_index >= sizeof(ancs_c->attr_response.app_id)) {
LOG_DBG("App ID cannot be stored in response buffer.\n");
return BT_ANCS_PARSE_STATE_DONE;
}
ancs_c->attr_response.app_id[ancs_c->parse_info.current_app_id_index] =
data_src[(*index)++];
if (ancs_c->attr_response.app_id[ancs_c->parse_info.current_app_id_index] !=
'\0') {
ancs_c->parse_info.current_app_id_index++;
return BT_ANCS_PARSE_STATE_APP_ID;
} else {
return BT_ANCS_PARSE_STATE_ATTR_ID;
}
}
/**@brief Function for parsing the id of an iOS attribute.
* Used in the @ref parse_get_notif_attrs_response state machine.
*
* @details We only request attributes that are registered with @ref bt_ancs_attr_add
* once they have been reveiced we stop parsing.
*
* @param[in] ancs_c Pointer to an ANCS instance to which the event belongs.
* @param[in] data_src Pointer to data that was received from the Notification Provider.
* @param[in] index Pointer to an index that helps us keep track of the current data
* to be parsed.
*
* @return The next parse state.
*/
static enum bt_ancs_parse_state attr_id_parse(struct bt_ancs_client *ancs_c,
const uint8_t *data_src,
uint32_t *index)
{
ancs_c->attr_response.attr.attr_id = data_src[(*index)++];
if (ancs_c->attr_response.attr.attr_id >= ancs_c->parse_info.attr_count) {
LOG_DBG("Attribute ID Invalid.\n");
return BT_ANCS_PARSE_STATE_DONE;
}
ancs_c->attr_response.attr.attr_data =
ancs_c->parse_info.attr_list[ancs_c->attr_response.attr.attr_id]
.attr_data;
if (all_req_attrs_parsed(ancs_c)) {
LOG_DBG("All requested attributes received. \n");
return BT_ANCS_PARSE_STATE_DONE;
}
if (attr_is_requested(ancs_c, ancs_c->attr_response.attr)) {
ancs_c->parse_info.expected_number_of_attrs--;
}
LOG_DBG("Attribute ID %i \n", ancs_c->attr_response.attr.attr_id);
return BT_ANCS_PARSE_STATE_ATTR_LEN1;
}
/**@brief Function for parsing the length of an iOS attribute.
* Used in the @ref parse_get_notif_attrs_response state machine.
*
* @details The Length is 2 bytes. Since there is a chance we reveice the bytes in two different
* GATT notifications, we parse only the first byte here and then set the state machine
* ready to parse the next byte.
*
* @param[in] ancs_c Pointer to an ANCS instance to which the event belongs.
* @param[in] data_src Pointer to data that was received from the Notification Provider.
* @param[in] index Pointer to an index that helps us keep track of the current data
* to be parsed.
*
* @return The next parse state.
*/
static enum bt_ancs_parse_state attr_len1_parse(struct bt_ancs_client *ancs_c,
const uint8_t *data_src,
uint32_t *index)
{
ancs_c->attr_response.attr.attr_len = data_src[(*index)++];
return BT_ANCS_PARSE_STATE_ATTR_LEN2;
}
/**@brief Function for parsing the length of an iOS attribute.
* Used in the @ref parse_get_notif_attrs_response state machine.
*
* @details Second byte of the length field. If the length is zero, it means that the attribute
* is not present and the state machine is set to parse the next attribute.
*
* @param[in] ancs_c Pointer to an ANCS instance to which the event belongs.
* @param[in] data_src Pointer to data that was received from the Notification Provider.
* @param[in] index Pointer to an index that helps us keep track of the current data
* to be parsed.
*
* @return The next parse state.
*/
static enum bt_ancs_parse_state attr_len2_parse(struct bt_ancs_client *ancs_c,
const uint8_t *data_src,
uint32_t *index)
{
ancs_c->attr_response.attr.attr_len |= (data_src[(*index)++] << 8);
ancs_c->parse_info.current_attr_index = 0;
if (ancs_c->attr_response.attr.attr_len != 0) {
/* If the attribute has a length but there is no allocated
* space for this attribute
*/
if (!ancs_c->parse_info.attr_list[ancs_c->attr_response.attr.attr_id].attr_len ||
!ancs_c->parse_info.attr_list[ancs_c->attr_response.attr.attr_id].attr_data) {
return BT_ANCS_PARSE_STATE_ATTR_SKIP;
} else {
return BT_ANCS_PARSE_STATE_ATTR_DATA;
}
} else {
LOG_DBG("Attribute LEN %i \n", ancs_c->attr_response.attr.attr_len);
if (attr_is_requested(ancs_c, ancs_c->attr_response.attr)) {
bt_ancs_do_ds_notif_cb(ancs_c, &ancs_c->attr_response);
}
if (all_req_attrs_parsed(ancs_c)) {
return BT_ANCS_PARSE_STATE_DONE;
} else {
return BT_ANCS_PARSE_STATE_ATTR_ID;
}
}
}
/**@brief Function for parsing the data of an iOS attribute.
* Used in the @ref parse_get_notif_attrs_response state machine.
*
* @details Read the data of the attribute into our local buffer.
*
* @param[in] ancs_c Pointer to an ANCS instance to which the event belongs.
* @param[in] data_src Pointer to data that was received from the Notification Provider.
* @param[in] index Pointer to an index that helps us keep track of the current data
* to be parsed.
*
* @return The next parse state.
*/
static enum bt_ancs_parse_state attr_data_parse(struct bt_ancs_client *ancs_c,
const uint8_t *data_src,
uint32_t *index)
{
/* We have not reached the end of the attribute, nor our max allocated internal size.
* Proceed with copying data over to our buffer.
*/
if ((ancs_c->parse_info.current_attr_index <
ancs_c->parse_info.attr_list[ancs_c->attr_response.attr.attr_id].attr_len) &&
(ancs_c->parse_info.current_attr_index <
ancs_c->attr_response.attr.attr_len)) {
/* Un-comment this line to see every byte of an attribute as it is parsed.
* Commented out by default since it can overflow the uart buffer.
*/
/* LOG_DBG("Byte copied to buffer: %c\n", data_src[(*index)]); */
ancs_c->attr_response.attr
.attr_data[ancs_c->parse_info.current_attr_index++] =
data_src[(*index)++];
}
/* We have reached the end of the attribute, or our max allocated internal size.
* Stop copying data over to our buffer. NUL-terminate at the current index.
*/
if ((ancs_c->parse_info.current_attr_index ==
ancs_c->attr_response.attr.attr_len) ||
(ancs_c->parse_info.current_attr_index ==
ancs_c->parse_info.attr_list[ancs_c->attr_response.attr.attr_id].attr_len -
1)) {
if (attr_is_requested(ancs_c, ancs_c->attr_response.attr)) {
ancs_c->attr_response.attr.attr_data
[ancs_c->parse_info.current_attr_index] = '\0';
}
/* If our max buffer size is smaller than the remaining attribute data, we must
* increase index to skip the data until the start of the next attribute.
*/
if (ancs_c->parse_info.current_attr_index <
ancs_c->attr_response.attr.attr_len) {
return BT_ANCS_PARSE_STATE_ATTR_SKIP;
}
LOG_DBG("Attribute finished!\n");
if (attr_is_requested(ancs_c, ancs_c->attr_response.attr)) {
bt_ancs_do_ds_notif_cb(ancs_c, &ancs_c->attr_response);
}
if (all_req_attrs_parsed(ancs_c)) {
return BT_ANCS_PARSE_STATE_DONE;
} else {
return BT_ANCS_PARSE_STATE_ATTR_ID;
}
}
return BT_ANCS_PARSE_STATE_ATTR_DATA;
}
static enum bt_ancs_parse_state attr_skip(struct bt_ancs_client *ancs_c,
const uint8_t *data_src,
uint32_t *index)
{
/* We have not reached the end of the attribute, nor our max allocated internal size.
* Proceed with copying data over to our buffer.
*/
if (ancs_c->parse_info.current_attr_index < ancs_c->attr_response.attr.attr_len) {
ancs_c->parse_info.current_attr_index++;
(*index)++;
}
/* At the end of the attribute, determine if it should be passed to event handler and
* continue parsing the next attribute ID if we are not done with all the attributes.
*/
if (ancs_c->parse_info.current_attr_index ==
ancs_c->attr_response.attr.attr_len) {
if (attr_is_requested(ancs_c, ancs_c->attr_response.attr)) {
bt_ancs_do_ds_notif_cb(ancs_c, &ancs_c->attr_response);
}
if (all_req_attrs_parsed(ancs_c)) {
return BT_ANCS_PARSE_STATE_DONE;
} else {
return BT_ANCS_PARSE_STATE_ATTR_ID;
}
}
return BT_ANCS_PARSE_STATE_ATTR_SKIP;
}
void bt_ancs_parse_get_attrs_response(struct bt_ancs_client *ancs_c,
const uint8_t *data_src,
const uint16_t data_len)
{
uint32_t index;
for (index = 0; index < data_len;) {
switch (ancs_c->parse_info.parse_state) {
case BT_ANCS_PARSE_STATE_COMMAND_ID:
ancs_c->parse_info.parse_state =
command_id_parse(ancs_c, data_src, &index);
break;
case BT_ANCS_PARSE_STATE_NOTIF_UID:
ancs_c->parse_info.parse_state =
notif_uid_parse(ancs_c, data_src, &index);
break;
case BT_ANCS_PARSE_STATE_APP_ID:
ancs_c->parse_info.parse_state =
app_id_parse(ancs_c, data_src, &index);
break;
case BT_ANCS_PARSE_STATE_ATTR_ID:
ancs_c->parse_info.parse_state =
attr_id_parse(ancs_c, data_src, &index);
break;
case BT_ANCS_PARSE_STATE_ATTR_LEN1:
ancs_c->parse_info.parse_state =
attr_len1_parse(ancs_c, data_src, &index);
break;
case BT_ANCS_PARSE_STATE_ATTR_LEN2:
ancs_c->parse_info.parse_state =
attr_len2_parse(ancs_c, data_src, &index);
break;
case BT_ANCS_PARSE_STATE_ATTR_DATA:
ancs_c->parse_info.parse_state =
attr_data_parse(ancs_c, data_src, &index);
break;
case BT_ANCS_PARSE_STATE_ATTR_SKIP:
ancs_c->parse_info.parse_state =
attr_skip(ancs_c, data_src, &index);
break;
case BT_ANCS_PARSE_STATE_DONE:
LOG_DBG("Parse state: Done \n");
index = data_len;
break;
default:
/* Default case will never trigger intentionally.
* Go to the DONE state to minimize the consequences.
*/
ancs_c->parse_info.parse_state = BT_ANCS_PARSE_STATE_DONE;
break;
}
}
}
@@ -0,0 +1,54 @@
/*
* Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd.
*
* Attention: This software (modified or not) and binary are used for
* microcontroller manufactured by Nanjing Qinheng Microelectronics.
*/
#ifndef BT_ANCS_ATTR_PARSER_H__
#define BT_ANCS_ATTR_PARSER_H__
/** @file
*
* @addtogroup bt_ancs_client
* @{
*/
#include "ancs_client.h"
#ifdef __cplusplus
extern "C" {
#endif
/**@brief Function for parsing notification or app attribute response data.
*
* @details The data that comes from the Notification Provider can be much longer than what
* would fit in a single GATT notification. Therefore, this function relies on a
* state-oriented switch case.
* UID and command ID will be received only once at the beginning of the first
* GATT notification of a new attribute request for a given iOS notification.
* After this, we can loop several ATTR_ID > LENGTH > DATA > ATTR_ID > LENGTH > DATA until
* we have received all attributes we wanted as a Notification Consumer.
* The Notification Provider can also simply stop sending attributes.
*
* 1 byte | 4 bytes |1 byte |2 bytes |... X bytes ... |1 bytes| 2 bytes| ... X bytes ...
* --------|-------------|-------|--------|----------------|-------|--------|----------------
* CMD_ID | NOTIF_UID |ATTR_ID| LENGTH | DATA |ATTR_ID| LENGTH | DATA
*
* @param[in] ancs_c ANCS client instance to which the event belongs.
* @param[in] data_src Pointer to data that was received from the Notification Provider.
* @param[in] data_len Length of the data that was received from the Notification Provider.
*/
void bt_ancs_parse_get_attrs_response(struct bt_ancs_client *ancs_c,
const uint8_t *data_src,
const uint16_t data_len);
#ifdef __cplusplus
}
#endif
/**
* @}
*/
#endif /* BT_ANCS_ATTR_PARSER_H__ */
@@ -0,0 +1,545 @@
/*
* Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd.
*
* Attention: This software (modified or not) and binary are used for
* microcontroller manufactured by Nanjing Qinheng Microelectronics.
*/
/*********************************************************************
* INCLUDES
*/
#include <stdlib.h>
#include <sys/byteorder.h>
#include "string.h"
#include "config.h"
#include "ancs.h"
#include "ancs_client.h"
#include "ancs_client_internal.h"
#include "ancs_attr_parser.h"
#include "ancs_app_attr_get.h"
/*********************************************************************
* MACROS
*/
/**< Index of the Event ID field when parsing notifications. */
#define BT_ANCS_NOTIF_EVT_ID_INDEX 0
/**< Index of the Flags field when parsing notifications. */
#define BT_ANCS_NOTIF_FLAGS_INDEX 1
/**< Index of the Category ID field when parsing notifications. */
#define BT_ANCS_NOTIF_CATEGORY_ID_INDEX 2
/**< Index of the Category Count field when parsing notifications. */
#define BT_ANCS_NOTIF_CATEGORY_CNT_INDEX 3
/**< Index of the Notification UID field when parsing notifications. */
#define BT_ANCS_NOTIF_NOTIF_UID 4
/*********************************************************************
* CONSTANTS
*/
/*********************************************************************
* TYPEDEFS
*/
/*********************************************************************
* GLOBAL VARIABLES
*/
/*********************************************************************
* EXTERNAL VARIABLES
*/
/*********************************************************************
* EXTERNAL FUNCTIONS
*/
/*********************************************************************
* LOCAL VARIABLES
*/
/*********************************************************************
* LOCAL FUNCTIONS
*/
/**@brief Function for checking whether the data in an iOS notification is out of bounds.
*
* @param[in] notif An iOS notification.
*
* @retval 0 If the notification is within bounds.
* @retval -1 If the notification is out of bounds.
*/
static int bt_ancs_verify_notification_format(const struct bt_ancs_evt_notif *notif)
{
if ((notif->evt_id >= BT_ANCS_EVT_ID_COUNT) ||
(notif->category_id >= BT_ANCS_CATEGORY_ID_COUNT)) {
return -1;
}
return 0;
}
/**@brief Function for receiving and validating notifications received from
* the Notification Provider.
*
* @param[in] ancs_c Pointer to an ANCS instance to which the event belongs.
* @param[in] data_src Pointer to the data that was received from the Notification Provider.
* @param[in] len Length of the data that was received from the Notification Provider.
*/
static void parse_notif(struct bt_ancs_client *ancs_c,
const uint8_t *data_src, const uint16_t len)
{
int err;
struct bt_ancs_evt_notif notif;
if (len != BT_ANCS_NOTIF_DATA_LENGTH) {
bt_ancs_do_ns_notif_cb(ancs_c, -EINVAL, &notif);
return;
}
notif.evt_id = (enum bt_ancs_evt_id_values)
data_src[BT_ANCS_NOTIF_EVT_ID_INDEX];
notif.evt_flags.silent =
(data_src[BT_ANCS_NOTIF_FLAGS_INDEX] >>
BT_ANCS_EVENT_FLAG_SILENT) &
0x01;
notif.evt_flags.important =
(data_src[BT_ANCS_NOTIF_FLAGS_INDEX] >>
BT_ANCS_EVENT_FLAG_IMPORTANT) &
0x01;
notif.evt_flags.pre_existing =
(data_src[BT_ANCS_NOTIF_FLAGS_INDEX] >>
BT_ANCS_EVENT_FLAG_PREEXISTING) &
0x01;
notif.evt_flags.positive_action =
(data_src[BT_ANCS_NOTIF_FLAGS_INDEX] >>
BT_ANCS_EVENT_FLAG_POSITIVE_ACTION) &
0x01;
notif.evt_flags.negative_action =
(data_src[BT_ANCS_NOTIF_FLAGS_INDEX] >>
BT_ANCS_EVENT_FLAG_NEGATIVE_ACTION) &
0x01;
notif.category_id = (enum bt_ancs_category_id_val)
data_src[BT_ANCS_NOTIF_CATEGORY_ID_INDEX];
notif.category_count =
data_src[BT_ANCS_NOTIF_CATEGORY_CNT_INDEX];
notif.notif_uid =
sys_get_le32(&data_src[BT_ANCS_NOTIF_NOTIF_UID]);
err = bt_ancs_verify_notification_format(&notif);
bt_ancs_do_ns_notif_cb(ancs_c, err, &notif);
}
int bt_ancs_client_init(struct bt_ancs_client *ancs_c)
{
if (!ancs_c) {
return -EINVAL;
}
memset(ancs_c, 0, sizeof(struct bt_ancs_client));
return 0;
}
void ancs_reinit(struct bt_ancs_client *ancs_c)
{
ancs_c->conn = 0;
ancs_c->handle_cp = 0;
ancs_c->handle_ns = 0;
ancs_c->handle_ns_ccc = 0;
ancs_c->handle_ds = 0;
ancs_c->handle_ds_ccc = 0;
ancs_c->state = 0;
}
int bt_ancs_subscribe_notification_source(struct bt_ancs_client *ancs_c,
bt_ancs_ns_notif_cb func)
{
int err;
if (!ancs_c || !func) {
return -EINVAL;
}
if (atomic_test_and_set_bit(&ancs_c->state, ANCS_NS_NOTIF_ENABLED)) {
return -EALREADY;
}
ancs_c->ns_notif_cb = func;
ancs_c->ns_notif_params.pValue = GATT_bm_alloc(ancs_c->conn, ATT_WRITE_REQ, 2, NULL, 0);
if(!ancs_c->ns_notif_params.pValue){
return -ENOBUFS;
}
ancs_c->ns_notif_params.len = 2;
ancs_c->ns_notif_params.pValue[0] = LO_UINT16(GATT_CLIENT_CFG_NOTIFY);
ancs_c->ns_notif_params.pValue[1] = HI_UINT16(GATT_CLIENT_CFG_NOTIFY);
ancs_c->ns_notif_params.handle = ancs_c->handle_ns_ccc;
ancs_c->ns_notif_params.cmd = 0;
ancs_c->ns_notif_params.sig = 0;
err = GATT_WriteCharValue(ancs_c->conn, &ancs_c->ns_notif_params, ancs_taskid);
if (err) {
atomic_clear_bit(&ancs_c->state, ANCS_NS_NOTIF_ENABLED);
GATT_bm_free((gattMsg_t *)&ancs_c->ns_notif_params, ATT_WRITE_REQ);
PRINT("Subscribe Notification Source failed (err %d)\n", err);
} else {
PRINT("Notification Source subscribed\n");
}
return err;
}
int bt_ancs_subscribe_data_source(struct bt_ancs_client *ancs_c,
bt_ancs_ds_notif_cb func)
{
int err;
if (!ancs_c || !func) {
return -EINVAL;
}
if (atomic_test_and_set_bit(&ancs_c->state, ANCS_DS_NOTIF_ENABLED)) {
return -EALREADY;
}
ancs_c->ds_notif_cb = func;
ancs_c->ds_notif_params.pValue = GATT_bm_alloc(ancs_c->conn, ATT_WRITE_REQ, 2, NULL, 0);
if(!ancs_c->ds_notif_params.pValue){
return -ENOBUFS;
}
ancs_c->ds_notif_params.len = 2;
ancs_c->ds_notif_params.pValue[0] = LO_UINT16(GATT_CLIENT_CFG_NOTIFY);
ancs_c->ds_notif_params.pValue[1] = HI_UINT16(GATT_CLIENT_CFG_NOTIFY);
ancs_c->ds_notif_params.handle = ancs_c->handle_ds_ccc;
ancs_c->ds_notif_params.cmd = 0;
ancs_c->ds_notif_params.sig = 0;
err = GATT_WriteCharValue(ancs_c->conn, &ancs_c->ds_notif_params, ancs_taskid);
if (err) {
atomic_clear_bit(&ancs_c->state, ANCS_DS_NOTIF_ENABLED);
GATT_bm_free((gattMsg_t *)&ancs_c->ds_notif_params, ATT_WRITE_REQ);
PRINT("Subscribe Notification Source failed (err %d)\n", err);
} else {
PRINT("Data Source subscribed\n");
}
return err;
}
int bt_ancs_unsubscribe_notification_source(struct bt_ancs_client *ancs_c)
{
int err;
if (!ancs_c) {
return -EINVAL;
}
if (!atomic_test_bit(&ancs_c->state, ANCS_NS_NOTIF_ENABLED)) {
return -EFAULT;
}
ancs_c->ns_notif_params.pValue = GATT_bm_alloc(ancs_c->conn, ATT_WRITE_REQ, 2, NULL, 0);
if(!ancs_c->ns_notif_params.pValue){
return -ENOBUFS;
}
ancs_c->ns_notif_params.len = 2;
ancs_c->ns_notif_params.pValue[0] = 0;
ancs_c->ns_notif_params.pValue[1] = 0;
ancs_c->ns_notif_params.handle = ancs_c->handle_ds;
ancs_c->ns_notif_params.cmd = 0;
ancs_c->ns_notif_params.sig = 0;
err = GATT_WriteCharValue(ancs_c->conn, &ancs_c->ns_notif_params, ancs_taskid);
if (err) {
PRINT("Unsubscribe Notification Source failed (err %d)\n", err);
GATT_bm_free((gattMsg_t *)&ancs_c->ns_notif_params, ATT_WRITE_REQ);
} else {
atomic_clear_bit(&ancs_c->state, ANCS_NS_NOTIF_ENABLED);
PRINT("Notification Source unsubscribed\n");
}
return err;
}
int bt_ancs_unsubscribe_data_source(struct bt_ancs_client *ancs_c)
{
int err;
if (!ancs_c) {
return -EINVAL;
}
if (!atomic_test_bit(&ancs_c->state, ANCS_DS_NOTIF_ENABLED)) {
return -EFAULT;
}
ancs_c->ds_notif_params.pValue = GATT_bm_alloc(ancs_c->conn, ATT_WRITE_REQ, 2, NULL, 0);
if(!ancs_c->ds_notif_params.pValue){
return -ENOBUFS;
}
ancs_c->ds_notif_params.len = 2;
ancs_c->ds_notif_params.pValue[0] = 0;
ancs_c->ds_notif_params.pValue[1] = 0;
ancs_c->ds_notif_params.handle = ancs_c->handle_ns;
ancs_c->ds_notif_params.cmd = 0;
ancs_c->ds_notif_params.sig = 0;
err = GATT_WriteCharValue(ancs_c->conn, &ancs_c->ds_notif_params, ancs_taskid);
if (err) {
PRINT("Unsubscribe Data Source failed (err %d)\n", err);
GATT_bm_free((gattMsg_t *)&ancs_c->ds_notif_params, ATT_WRITE_REQ);
} else {
atomic_clear_bit(&ancs_c->state, ANCS_DS_NOTIF_ENABLED);
PRINT("Data Source unsubscribed\n");
}
return err;
}
static uint16_t encode_notif_action(uint8_t *encoded_data, uint32_t uid,
enum bt_ancs_action_id_values action_id)
{
uint8_t index = 0;
encoded_data[index++] = BT_ANCS_COMMAND_ID_PERFORM_NOTIF_ACTION;
sys_put_le32(uid, &encoded_data[index]);
index += sizeof(uint32_t);
encoded_data[index++] = (uint8_t)action_id;
return index;
}
int bt_ancs_cp_write(struct bt_ancs_client *ancs_c, uint16_t len,
bt_ancs_write_cb func)
{
int err;
attWriteReq_t *write_params = &ancs_c->cp_write_params;;
write_params->pValue = GATT_bm_alloc(ancs_c->conn, ATT_WRITE_REQ, len, NULL, 0);
if(!write_params->pValue){
return -ENOBUFS;
}
ancs_c->cp_write_cb = func;
write_params->handle = ancs_c->handle_cp;
write_params->len = len;
tmos_memcpy(write_params->pValue, ancs_c->cp_data, len);
write_params->sig = 0;
write_params->cmd = 0;
err = GATT_WriteCharValue(ancs_c->conn, write_params, ancs_taskid);
if(err) {
atomic_clear_bit(&ancs_c->state, ANCS_CP_WRITE_PENDING);
GATT_bm_free((gattMsg_t *)write_params, ATT_WRITE_REQ);
PRINT("write error: %#x\n", err);
}
return err;
}
int bt_ancs_notification_action(struct bt_ancs_client *ancs_c, uint32_t uuid,
enum bt_ancs_action_id_values action_id,
bt_ancs_write_cb func)
{
if (atomic_test_and_set_bit(&ancs_c->state, ANCS_CP_WRITE_PENDING)) {
return -EBUSY;
}
uint8_t *data = ancs_c->cp_data;
uint16_t len = encode_notif_action(data, uuid, action_id);
return bt_ancs_cp_write(ancs_c, len, func);
}
static int bt_ancs_get_notif_attrs(struct bt_ancs_client *ancs_c,
const uint32_t uid, bt_ancs_write_cb func)
{
if (atomic_test_and_set_bit(&ancs_c->state, ANCS_CP_WRITE_PENDING)) {
return -EBUSY;
}
uint32_t index = 0;
uint8_t *data = ancs_c->cp_data;
ancs_c->number_of_requested_attr = 0;
/* Encode Command ID. */
*(data + index++) = BT_ANCS_COMMAND_ID_GET_NOTIF_ATTRIBUTES;
/* Encode Notification UID. */
sys_put_le32(uid, data + index);
index += sizeof(uint32_t);
/* Encode Attribute ID. */
for(uint32_t attr = 0; attr < BT_ANCS_NOTIF_ATTR_COUNT; attr++) {
if (ancs_c->ancs_notif_attr_list[attr].get) {
*(data + index++) = (uint8_t)attr;
if ((attr == BT_ANCS_NOTIF_ATTR_ID_TITLE) ||
(attr == BT_ANCS_NOTIF_ATTR_ID_SUBTITLE) ||
(attr == BT_ANCS_NOTIF_ATTR_ID_MESSAGE)) {
/* Encode Length field. Only applicable for
* Title, Subtitle, and Message.
*/
sys_put_le16(ancs_c->ancs_notif_attr_list[attr]
.attr_len,
data + index);
index += sizeof(uint16_t);
}
ancs_c->number_of_requested_attr++;
}
}
ancs_c->parse_info.expected_number_of_attrs =
ancs_c->number_of_requested_attr;
return bt_ancs_cp_write(ancs_c, index, func);
}
int bt_ancs_request_attrs(struct bt_ancs_client *ancs_c,
const struct bt_ancs_evt_notif *notif,
bt_ancs_write_cb func)
{
int err;
err = bt_ancs_verify_notification_format(notif);
if (err) {
return err;
}
ancs_c->parse_info.parse_state = BT_ANCS_PARSE_STATE_COMMAND_ID;
return bt_ancs_get_notif_attrs(ancs_c, notif->notif_uid, func);
}
int bt_ancs_register_attr(struct bt_ancs_client *ancs_c,
const enum bt_ancs_notif_attr_id_val id,
uint8_t *data, const uint16_t len)
{
if (!ancs_c || !data) {
return -EINVAL;
}
if (!len || len > BT_ANCS_ATTR_DATA_MAX) {
return -EINVAL;
}
if ((size_t)id >= BT_ANCS_NOTIF_ATTR_COUNT) {
return -EINVAL;
}
ancs_c->ancs_notif_attr_list[id].get = true;
ancs_c->ancs_notif_attr_list[id].attr_len = len;
ancs_c->ancs_notif_attr_list[id].attr_data = data;
return 0;
}
int bt_ancs_register_app_attr(struct bt_ancs_client *ancs_c,
const enum bt_ancs_app_attr_id_val id,
uint8_t *data, const uint16_t len)
{
if (!ancs_c || !data) {
return -EINVAL;
}
if (!len || len > BT_ANCS_ATTR_DATA_MAX) {
return -EINVAL;
}
if ((size_t)id >= BT_ANCS_APP_ATTR_COUNT) {
return -EINVAL;
}
ancs_c->ancs_app_attr_list[id].get = true;
ancs_c->ancs_app_attr_list[id].attr_len = len;
ancs_c->ancs_app_attr_list[id].attr_data = data;
return 0;
}
int bt_ancs_request_app_attr(struct bt_ancs_client *ancs_c,
const uint8_t *app_id, uint32_t len,
bt_ancs_write_cb func)
{
return bt_ancs_app_attr_request(ancs_c, app_id, len, func);
}
/*********************************************************************
* @fn Ancs_handleNotification
*
* @brief Handle notifications.
*
* @param pMsg - GATT message.
*
* @return none
*/
void Ancs_handleNotification(gattMsgEvent_t *pMsg)
{
static uint8_t importantAlertCnt = 0;
uint8_t i;
// Look up the handle in the handle cache
for (i = 0; i < HDL_CACHE_LEN; i++)
{
if (pMsg->msg.handleValueNoti.handle == Ancs_handleCache[i])
{
break;
}
}
// Perform processing for this handle
switch (i)
{
case HDL_ANCS_NTF_NOTIF_START:
{
parse_notif(&ancs_c, pMsg->msg.handleValueNoti.pValue,
pMsg->msg.handleValueNoti.len);
}
break;
case HDL_ANCS_DATA_SRC_START:
{
bt_ancs_parse_get_attrs_response(&ancs_c,
pMsg->msg.handleValueNoti.pValue, pMsg->msg.handleValueNoti.len);
}
break;
default:
break;
}
}
/*********************************************************************
*********************************************************************/
@@ -0,0 +1,605 @@
/*
* Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd.
*
* Attention: This software (modified or not) and binary are used for
* microcontroller manufactured by Nanjing Qinheng Microelectronics.
*/
#ifndef ANCS_CLIENT_H_
#define ANCS_CLIENT_H_
#include <stdbool.h>
#include <sys/atomic.h>
#include "config.h"
#define EPERM 1 /**< Not owner */
#define ENOENT 2 /**< No such file or directory */
#define ESRCH 3 /**< No such context */
#define EINTR 4 /**< Interrupted system call */
#define EIO 5 /**< I/O error */
#define ENXIO 6 /**< No such device or address */
#define E2BIG 7 /**< Arg list too long */
#define ENOEXEC 8 /**< Exec format error */
#define EBADF 9 /**< Bad file number */
#define ECHILD 10 /**< No children */
#define EAGAIN 11 /**< No more contexts */
#define ENOMEM 12 /**< Not enough core */
#define EACCES 13 /**< Permission denied */
#define EFAULT 14 /**< Bad address */
#define ENOTBLK 15 /**< Block device required */
#define EBUSY 16 /**< Mount device 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 /**< Argument too large */
#define ERANGE 34 /**< Result too large */
#define ENOMSG 35 /**< Unexpected message type */
#define EDEADLK 45 /**< Resource deadlock avoided */
#define ENOLCK 46 /**< No locks available */
#define ENOSTR 60 /**< STREAMS device required */
#define ENODATA 61 /**< Missing expected message data */
#define ETIME 62 /**< STREAMS timeout occurred */
#define ENOSR 63 /**< Insufficient memory */
#define EPROTO 71 /**< Generic STREAMS error */
#define EBADMSG 77 /**< Invalid STREAMS message */
#define ENOSYS 88 /**< Function not implemented */
#define ENOTEMPTY 90 /**< Directory not empty */
#define ENAMETOOLONG 91 /**< File name too long */
#define ELOOP 92 /**< Too many levels of symbolic links */
#define EOPNOTSUPP 95 /**< Operation not supported on socket */
#define EPFNOSUPPORT 96 /**< Protocol family not supported */
#define ECONNRESET 104 /**< Connection reset by peer */
#define ENOBUFS 105 /**< No buffer space available */
#define EAFNOSUPPORT 106 /**< Addr family not supported */
#define EPROTOTYPE 107 /**< Protocol wrong type for socket */
#define ENOTSOCK 108 /**< Socket operation on non-socket */
#define ENOPROTOOPT 109 /**< Protocol not available */
#define ESHUTDOWN 110 /**< Can't send after socket shutdown */
#define ECONNREFUSED 111 /**< Connection refused */
#define EADDRINUSE 112 /**< Address already in use */
#define ECONNABORTED 113 /**< Software caused connection abort */
#define ENETUNREACH 114 /**< Network is unreachable */
#define ENETDOWN 115 /**< Network is down */
#define ETIMEDOUT 116 /**< Connection timed out */
#define EHOSTDOWN 117 /**< Host is down */
#define EHOSTUNREACH 118 /**< No route to host */
#define EINPROGRESS 119 /**< Operation now in progress */
#define EALREADY 120 /**< Operation already in progress */
#define EDESTADDRREQ 121 /**< Destination address required */
#define EMSGSIZE 122 /**< Message size */
#define EPROTONOSUPPORT 123 /**< Protocol not supported */
#define ESOCKTNOSUPPORT 124 /**< Socket type not supported */
#define EADDRNOTAVAIL 125 /**< Can't assign requested address */
#define ENETRESET 126 /**< Network dropped connection on reset */
#define EISCONN 127 /**< Socket is already connected */
#define ENOTCONN 128 /**< Socket is not connected */
#define ETOOMANYREFS 129 /**< Too many references: can't splice */
#define ENOTSUP 134 /**< Unsupported value */
#define EILSEQ 138 /**< Illegal byte sequence */
#define EOVERFLOW 139 /**< Value overflow */
#define ECANCELED 140 /**< Operation canceled */
#define EWOULDBLOCK EAGAIN /**< Operation would block */
/** Maximum data length of an iOS notification attribute. */
#define BT_ANCS_ATTR_DATA_MAX 32
/** Number of iOS notification categories: Other, Incoming Call, Missed Call,
* Voice Mail, Social, Schedule, Email, News, Health and Fitness, Business and
* Finance, Location, Entertainment.
*/
#define BT_ANCS_CATEGORY_ID_COUNT 12
/** Number of iOS notification attributes: AppIdentifier, Title, Subtitle,
* Message, MessageSize, Date, PositiveActionLabel, NegativeActionLabel.
*/
#define BT_ANCS_NOTIF_ATTR_COUNT 8
/** Number of iOS application attributes: DisplayName. */
#define BT_ANCS_APP_ATTR_COUNT 1
/** Number of iOS notification events: Added, Modified, Removed. */
#define BT_ANCS_EVT_ID_COUNT 3
/** @brief Length of the iOS notification data. */
#define BT_ANCS_NOTIF_DATA_LENGTH 8
/** 0b.......1 Silent: First (LSB) bit is set. All flags can be active
* at the same time.
*/
#define BT_ANCS_EVENT_FLAG_SILENT 0
/** 0b......1. Important: Second (LSB) bit is set. All flags can be active
* at the same time.
*/
#define BT_ANCS_EVENT_FLAG_IMPORTANT 1
/** 0b.....1.. Pre-existing: Third (LSB) bit is set. All flags can be active
* at the same time.
*/
#define BT_ANCS_EVENT_FLAG_PREEXISTING 2
/** 0b....1... Positive action: Fourth (LSB) bit is set. All flags can be active
* at the same time.
*/
#define BT_ANCS_EVENT_FLAG_POSITIVE_ACTION 3
/** 0b...1.... Negative action: Fifth (LSB) bit is set. All flags can be active
* at the same time.
*/
#define BT_ANCS_EVENT_FLAG_NEGATIVE_ACTION 4
/** @defgroup BT_ATT_ANCS_NP_ERROR_CODES Notification Provider (iOS) Error Codes
* @{
*/
/** The command ID is unknown to the NP. */
#define BT_ATT_ERR_ANCS_NP_UNKNOWN_COMMAND 0xA0
/** The command format is invalid. */
#define BT_ATT_ERR_ANCS_NP_INVALID_COMMAND 0xA1
/** One or more parameters do not exist in the NP. */
#define BT_ATT_ERR_ANCS_NP_INVALID_PARAMETER 0xA2
/** The action failed to be performed by the NP. */
#define BT_ATT_ERR_ANCS_NP_ACTION_FAILED 0xA3
/** @} */
/**@brief Category IDs for iOS notifications. */
enum bt_ancs_category_id_val {
/** The iOS notification belongs to the "Other" category. */
BT_ANCS_CATEGORY_ID_OTHER,
/** The iOS notification belongs to the "Incoming Call" category. */
BT_ANCS_CATEGORY_ID_INCOMING_CALL,
/** The iOS notification belongs to the "Missed Call" category. */
BT_ANCS_CATEGORY_ID_MISSED_CALL,
/** The iOS notification belongs to the "Voice Mail" category. */
BT_ANCS_CATEGORY_ID_VOICE_MAIL,
/** The iOS notification belongs to the "Social" category. */
BT_ANCS_CATEGORY_ID_SOCIAL,
/** The iOS notification belongs to the "Schedule" category. */
BT_ANCS_CATEGORY_ID_SCHEDULE,
/** The iOS notification belongs to the "Email" category. */
BT_ANCS_CATEGORY_ID_EMAIL,
/** The iOS notification belongs to the "News" category. */
BT_ANCS_CATEGORY_ID_NEWS,
/** The iOS notification belongs to the "Health and Fitness" category. */
BT_ANCS_CATEGORY_ID_HEALTH_AND_FITNESS,
/** The iOS notification belongs to the "Business and Finance" category. */
BT_ANCS_CATEGORY_ID_BUSINESS_AND_FINANCE,
/** The iOS notification belongs to the "Location" category. */
BT_ANCS_CATEGORY_ID_LOCATION,
/** The iOS notification belongs to the "Entertainment" category. */
BT_ANCS_CATEGORY_ID_ENTERTAINMENT
};
/**@brief Event IDs for iOS notifications. */
enum bt_ancs_evt_id_values {
/** The iOS notification was added. */
BT_ANCS_EVENT_ID_NOTIFICATION_ADDED,
/** The iOS notification was modified. */
BT_ANCS_EVENT_ID_NOTIFICATION_MODIFIED,
/** The iOS notification was removed. */
BT_ANCS_EVENT_ID_NOTIFICATION_REMOVED
};
/**@brief Control point command IDs that the Notification Consumer can send
* to the Notification Provider.
*/
enum bt_ancs_cmd_id_val {
/** Requests attributes to be sent from the NP to the NC for a given
* notification.
*/
BT_ANCS_COMMAND_ID_GET_NOTIF_ATTRIBUTES,
/** Requests attributes to be sent from the NP to the NC for a given
* iOS app.
*/
BT_ANCS_COMMAND_ID_GET_APP_ATTRIBUTES,
/** Requests an action to be performed on a given notification.
* For example, dismiss an alarm.
*/
BT_ANCS_COMMAND_ID_PERFORM_NOTIF_ACTION
};
/**@brief IDs for actions that can be performed for iOS notifications. */
enum bt_ancs_action_id_values {
/** Positive action. */
BT_ANCS_ACTION_ID_POSITIVE = 0,
/** Negative action. */
BT_ANCS_ACTION_ID_NEGATIVE
};
/**@brief App attribute ID values.
* @details Currently, only one value is defined. However, the number of app
* attributes might increase. For this reason, they are stored in an enumeration.
*/
enum bt_ancs_app_attr_id_val {
/** Command used to get the display name for an app identifier. */
BT_ANCS_APP_ATTR_ID_DISPLAY_NAME = 0
};
/**@brief IDs for iOS notification attributes. */
enum bt_ancs_notif_attr_id_val {
/** Identifies that the attribute data is of an "App Identifier" type. */
BT_ANCS_NOTIF_ATTR_ID_APP_IDENTIFIER = 0,
/** Identifies that the attribute data is a "Title". */
BT_ANCS_NOTIF_ATTR_ID_TITLE,
/** Identifies that the attribute data is a "Subtitle". */
BT_ANCS_NOTIF_ATTR_ID_SUBTITLE,
/** Identifies that the attribute data is a "Message". */
BT_ANCS_NOTIF_ATTR_ID_MESSAGE,
/** Identifies that the attribute data is a "Message Size". */
BT_ANCS_NOTIF_ATTR_ID_MESSAGE_SIZE,
/** Identifies that the attribute data is a "Date". */
BT_ANCS_NOTIF_ATTR_ID_DATE,
/** The notification has a "Positive action" that can be executed associated with it. */
BT_ANCS_NOTIF_ATTR_ID_POSITIVE_ACTION_LABEL,
/** The notification has a "Negative action" that can be executed associated with it. */
BT_ANCS_NOTIF_ATTR_ID_NEGATIVE_ACTION_LABEL
};
/**@brief Flags for iOS notifications. */
struct bt_ancs_notif_flags {
/** If this flag is set, the notification has a low priority. */
uint8_t silent : 1;
/** If this flag is set, the notification has a high priority. */
uint8_t important : 1;
/** If this flag is set, the notification is pre-existing. */
uint8_t pre_existing : 1;
/** If this flag is set, the notification has a positive action that can be taken. */
uint8_t positive_action : 1;
/** If this flag is set, the notification has a negative action that can be taken. */
uint8_t negative_action : 1;
};
/**@brief Parsing states for received iOS notification and app attributes. */
enum bt_ancs_parse_state {
/** Parsing the command ID. */
BT_ANCS_PARSE_STATE_COMMAND_ID,
/** Parsing the notification UID. */
BT_ANCS_PARSE_STATE_NOTIF_UID,
/** Parsing app ID. */
BT_ANCS_PARSE_STATE_APP_ID,
/** Parsing attribute ID. */
BT_ANCS_PARSE_STATE_ATTR_ID,
/** Parsing the LSB of the attribute length. */
BT_ANCS_PARSE_STATE_ATTR_LEN1,
/** Parsing the MSB of the attribute length. */
BT_ANCS_PARSE_STATE_ATTR_LEN2,
/** Parsing the attribute data. */
BT_ANCS_PARSE_STATE_ATTR_DATA,
/** Parsing is skipped for the rest of an attribute (or entire attribute). */
BT_ANCS_PARSE_STATE_ATTR_SKIP,
/** Parsing for one attribute is done. */
BT_ANCS_PARSE_STATE_DONE
};
/**@brief iOS notification structure. */
struct bt_ancs_evt_notif {
/** Notification UID. */
uint32_t notif_uid;
/** Whether the notification was added, removed, or modified. */
enum bt_ancs_evt_id_values evt_id;
/** Bitmask to signal whether a special condition applies to the
* notification. For example, "Silent" or "Important".
*/
struct bt_ancs_notif_flags evt_flags;
/** Classification of the notification type. For example, email or
* location.
*/
enum bt_ancs_category_id_val category_id;
/** Current number of active notifications for this category ID. */
uint8_t category_count;
};
/**@brief iOS attribute structure. This type is used for both notification
* attributes and app attributes.
*/
struct bt_ancs_attr {
/** Length of the received attribute data. */
uint16_t attr_len;
/** Classification of the attribute type. For example, "Title" or "Date". */
uint32_t attr_id;
/** Pointer to where the memory is allocated for storing incoming attributes. */
uint8_t *attr_data;
};
/**@brief iOS notification attribute content requested by the application. */
struct bt_ancs_attr_list {
/** Boolean to determine whether this attribute will be requested from
* the Notification Provider.
*/
bool get;
/** Attribute ID: AppIdentifier(0), Title(1), Subtitle(2), Message(3),
* MessageSize(4), Date(5), PositiveActionLabel(6),
* NegativeActionLabel(7).
*/
uint32_t attr_id;
/** Length of the attribute. If more data is received from the
* Notification Provider, all the data beyond this length is discarded.
*/
uint16_t attr_len;
/** Pointer to where the memory is allocated for storing incoming attributes. */
uint8_t *attr_data;
};
struct bt_ancs_client;
/**@brief Attribute response structure. */
struct bt_ancs_attr_response {
/** Command ID. */
enum bt_ancs_cmd_id_val command_id;
/** iOS notification attribute or app attribute, depending on the
* Command ID.
*/
struct bt_ancs_attr attr;
/** Notification UID. */
uint32_t notif_uid;
/** App identifier. */
uint8_t app_id[BT_ANCS_ATTR_DATA_MAX];
};
/**@brief Notification Source notification callback function.
*
* @param[in] ancs_c ANCS client instance.
* @param[in] err 0 if the notification is valid.
* Otherwise, contains a (negative) error code.
* @param[in] notif iOS notification structure.
*/
typedef void (*bt_ancs_ns_notif_cb)(struct bt_ancs_client *ancs_c, int err,
const struct bt_ancs_evt_notif *notif);
/**@brief Data Source notification callback function.
*
* @param[in] ancs_c ANCS client instance.
* @param[in] response Attribute response structure.
*/
typedef void (*bt_ancs_ds_notif_cb)(struct bt_ancs_client *ancs_c,
const struct bt_ancs_attr_response *response);
/**@brief Write response callback function.
*
* @param[in] ancs_c ANCS client instance.
* @param[in] err ATT error code.
*/
typedef void (*bt_ancs_write_cb)(struct bt_ancs_client *ancs_c,
uint8_t err);
struct bt_ancs_parse_sm {
/** The current list of attributes that are being parsed. This will
* point to either ancs_notif_attr_list or ancs_app_attr_list in
* struct bt_ancs_client.
*/
struct bt_ancs_attr_list *attr_list;
/** Number of possible attributes. When parsing begins, it is set to
* either @ref BT_ANCS_NOTIF_ATTR_COUNT or @ref BT_ANCS_APP_ATTR_COUNT.
*/
uint32_t attr_count;
/** The number of attributes expected upon receiving attributes. Keeps
* track of when to stop reading incoming attributes.
*/
uint32_t expected_number_of_attrs;
/** ANCS notification attribute parsing state. */
enum bt_ancs_parse_state parse_state;
/** Variable to keep track of what command type is being parsed
* ( @ref BT_ANCS_COMMAND_ID_GET_NOTIF_ATTRIBUTES or
* @ref BT_ANCS_COMMAND_ID_GET_APP_ATTRIBUTES).
*/
enum bt_ancs_cmd_id_val command_id;
/** Attribute that the parsed data is copied into. */
uint8_t *data_dest;
/** Variable to keep track of the parsing progress, for the given attribute. */
uint16_t current_attr_index;
/** Variable to keep track of the parsing progress, for the given app identifier. */
uint32_t current_app_id_index;
};
/**@brief ANCS client instance, which contains various status information. */
struct bt_ancs_client {
/** Connection object. */
uint8_t conn;
/** Internal state. */
atomic_t state;
/** Handle of the Control Point Characteristic. */
uint16_t handle_cp;
/** Handle of the Notification Source Characteristic. */
uint16_t handle_ns;
/** Handle of the CCCD of the Notification Source Characteristic. */
uint16_t handle_ns_ccc;
/** Handle of the Data Source Characteristic. */
uint16_t handle_ds;
/** Handle of the CCCD of the Data Source Characteristic. */
uint16_t handle_ds_ccc;
/** GATT write parameters for Control Point Characteristic. */
attWriteReq_t cp_write_params;
/** Callback function for Control Point GATT write. */
bt_ancs_write_cb cp_write_cb;
/** Data buffer for Control Point GATT write. */
uint8_t cp_data[100];
/** GATT subscribe parameters for Notification Source Characteristic. */
attWriteReq_t ns_notif_params;
/** Callback function for Notification Source notification. */
bt_ancs_ns_notif_cb ns_notif_cb;
/** GATT subscribe parameters for Data Source Characteristic. */
attWriteReq_t ds_notif_params;
/** Callback function for Data Source notification. */
bt_ancs_ds_notif_cb ds_notif_cb;
/** For all attributes: contains information about whether the
* attributes are to be requested upon attribute request, and
* the length and buffer of where to store attribute data.
*/
struct bt_ancs_attr_list
ancs_notif_attr_list[BT_ANCS_NOTIF_ATTR_COUNT];
/** For all app attributes: contains information about whether the
* attributes are to be requested upon attribute request, and the
* length and buffer of where to store attribute data.
*/
struct bt_ancs_attr_list
ancs_app_attr_list[BT_ANCS_APP_ATTR_COUNT];
/** The number of attributes that are to be requested when an iOS
* notification attribute request is made.
*/
uint32_t number_of_requested_attr;
/** Structure containing different information used to parse incoming
* attributes correctly (from data_source characteristic).
*/
struct bt_ancs_parse_sm parse_info;
/** Allocate memory for the attribute response here.
*/
struct bt_ancs_attr_response attr_response;
};
/**@brief Function for initializing the ANCS client.
*
* @param[out] ancs_c ANCS client instance. This structure must be
* supplied by the application. It is initialized by
* this function and will later be used to identify
* this particular client instance.
*
* @retval 0 If the client was initialized successfully.
* Otherwise, a (negative) error code is returned.
*/
int bt_ancs_client_init(struct bt_ancs_client *ancs_c);
void ancs_reinit(struct bt_ancs_client *ancs_c);
/**@brief Function for writing to the CCCD to enable notifications from
* the Apple Notification Service.
*
* @param[in] ancs_c ANCS client instance.
* @param[in] func Callback function for handling notification.
*
* @retval 0 If writing to the CCCD was successful.
* Otherwise, a (negative) error code is returned.
*/
int bt_ancs_subscribe_notification_source(struct bt_ancs_client *ancs_c,
bt_ancs_ns_notif_cb func);
/**@brief Function for writing to the CCCD to enable data source notifications from the ANCS.
*
* @param[in] ancs_c ANCS client instance.
* @param[in] func Callback function for handling notification.
*
* @retval 0 If writing to the CCCD was successful.
* Otherwise, a (negative) error code is returned.
*/
int bt_ancs_subscribe_data_source(struct bt_ancs_client *ancs_c,
bt_ancs_ds_notif_cb func);
/**@brief Function for writing to the CCCD to disable notifications from the ANCS.
*
* @param[in] ancs_c ANCS client instance
*
* @retval 0 If writing to the CCCD was successful.
* Otherwise, a (negative) error code is returned.
*/
int bt_ancs_unsubscribe_notification_source(struct bt_ancs_client *ancs_c);
/**@brief Function for writing to the CCCD to disable data source notifications from the ANCS.
*
* @param[in] ancs_c ANCS client instance.
*
* @retval 0 If writing to the CCCD was successful.
* Otherwise, a (negative) error code is returned.
*/
int bt_ancs_unsubscribe_data_source(struct bt_ancs_client *ancs_c);
/**@brief Function for registering attributes that will be requested when
* @ref bt_ancs_request_attrs is called.
*
* @param[in] ancs_c ANCS client instance on which the attribute is registered.
* @param[in] id ID of the attribute that is added.
* @param[in] data Pointer to the buffer where the data of the attribute can be stored.
* @param[in] len Length of the buffer where the data of the attribute can be stored.
*
* @retval 0 If all operations are successful.
* Otherwise, a (negative) error code is returned.
*/
int bt_ancs_register_attr(struct bt_ancs_client *ancs_c,
const enum bt_ancs_notif_attr_id_val id,
uint8_t *data, const uint16_t len);
/**@brief Function for registering attributes that will be requested when
* @ref bt_ancs_request_app_attr is called.
*
* @param[in] ancs_c ANCS client instance on which the attribute is registered.
* @param[in] id ID of the attribute that is added.
* @param[in] data Pointer to the buffer where the data of the attribute can be stored.
* @param[in] len Length of the buffer where the data of the attribute can be stored.
*
* @retval 0 If all operations are successful.
* Otherwise, a (negative) error code is returned.
*/
int bt_ancs_register_app_attr(struct bt_ancs_client *ancs_c,
const enum bt_ancs_app_attr_id_val id,
uint8_t *data, const uint16_t len);
/**@brief Function for requesting attributes for a notification.
*
* @param[in] ancs_c ANCS client instance.
* @param[in] notif Pointer to the notification whose attributes will be requested from
* the Notification Provider.
* @param[in] func Callback function for handling NP write response.
*
* @retval 0 If all operations are successful.
* Otherwise, a (negative) error code is returned.
*/
int bt_ancs_request_attrs(struct bt_ancs_client *ancs_c,
const struct bt_ancs_evt_notif *notif,
bt_ancs_write_cb func);
/**@brief Function for requesting attributes for a given app.
*
* @param[in] ancs_c ANCS client instance.
* @param[in] app_id App identifier of the app for which the app attributes are requested.
* @param[in] len Length of the app identifier.
* @param[in] func Callback function for handling NP write response.
*
* @retval 0 If all operations are successful.
* Otherwise, a (negative) error code is returned.
*/
int bt_ancs_request_app_attr(struct bt_ancs_client *ancs_c,
const uint8_t *app_id, uint32_t len,
bt_ancs_write_cb func);
/**@brief Function for performing a notification action.
*
* @param[in] ancs_c ANCS client instance.
* @param[in] uuid The UUID of the notification for which to perform the action.
* @param[in] action_id Perform a positive or negative action.
* @param[in] func Callback function for handling NP write response.
*
* @retval 0 If the operation is successful.
* Otherwise, a (negative) error code is returned.
*/
int bt_ancs_notification_action(struct bt_ancs_client *ancs_c, uint32_t uuid,
enum bt_ancs_action_id_values action_id,
bt_ancs_write_cb func);
void Ancs_handleNotification(gattMsgEvent_t *pMsg);
#endif /* ANCS_CLIENT_H_ */
@@ -0,0 +1,72 @@
/*
* Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd.
*
* Attention: This software (modified or not) and binary are used for
* microcontroller manufactured by Nanjing Qinheng Microelectronics.
*/
#ifndef BT_ANCS_CLIENT_INTERNAL_H_
#define BT_ANCS_CLIENT_INTERNAL_H_
#include "ancs_client.h"
#ifdef __cplusplus
extern "C" {
#endif
enum {
ANCS_BT_CONNECTED,
ANCS_BT_DISCOVERY_COMPLETE,
ANCS_NS_NOTIF_ENABLED,
ANCS_DS_NOTIF_ENABLED,
ANCS_CP_WRITE_PENDING
};
/**@brief Call Notification Source notification callback function.
*
* @param[in] ancs_c ANCS client instance.
* @param[in] err 0 if the notification is valid.
* Otherwise, contains a (negative) error code.
* @param[in] notif iOS notification structure.
*/
static inline void bt_ancs_do_ns_notif_cb(struct bt_ancs_client *ancs_c, int err,
const struct bt_ancs_evt_notif *notif)
{
if (ancs_c->ns_notif_cb) {
ancs_c->ns_notif_cb(ancs_c, err, notif);
}
}
/**@brief Call Data Source notification callback function.
*
* @param[in] ancs_c ANCS client instance.
* @param[in] response Attribute response structure.
*/
static inline void bt_ancs_do_ds_notif_cb(struct bt_ancs_client *ancs_c,
const struct bt_ancs_attr_response *response)
{
if (ancs_c->ds_notif_cb) {
ancs_c->ds_notif_cb(ancs_c, response);
}
}
/**@brief Internal function for writing to ANCS Control Point.
*
* The caller is expected to set the ANCS_CP_WRITE_PENDING and to set up
* the Control Point data buffer before calling this function.
*
* @param[in] ancs_c ANCS client instance.
* @param[in] len Length of data in Control Point data buffer.
* @param[in] func Callback function for handling NP write response.
*
* @retval 0 If the operation is successful.
* Otherwise, a (negative) error code is returned.
*/
int bt_ancs_cp_write(struct bt_ancs_client *ancs_c, uint16_t len,
bt_ancs_write_cb func);
#ifdef __cplusplus
}
#endif
#endif /* BT_ANCS_CLIENT_INTERNAL_H_ */
@@ -0,0 +1,589 @@
/********************************** (C) COPYRIGHT *******************************
* File Name : devinfoservice.c
* Author : WCH
* Version : V1.0
* Date : 2018/12/10
* Description : Device information service
*********************************************************************************
* Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd.
* Attention: This software (modified or not) and binary are used for
* microcontroller manufactured by Nanjing Qinheng Microelectronics.
*******************************************************************************/
/*********************************************************************
* INCLUDES
*/
#include "CONFIG.h"
#include "devinfoservice.h"
/*********************************************************************
* MACROS
*/
/*********************************************************************
* CONSTANTS
*/
/*********************************************************************
* TYPEDEFS
*/
/*********************************************************************
* GLOBAL VARIABLES
*/
// Device information service
const uint8_t devInfoServUUID[ATT_BT_UUID_SIZE] = {
LO_UINT16(DEVINFO_SERV_UUID), HI_UINT16(DEVINFO_SERV_UUID)};
// System ID
const uint8_t devInfoSystemIdUUID[ATT_BT_UUID_SIZE] = {
LO_UINT16(SYSTEM_ID_UUID), HI_UINT16(SYSTEM_ID_UUID)};
// Model Number String
const uint8_t devInfoModelNumberUUID[ATT_BT_UUID_SIZE] = {
LO_UINT16(MODEL_NUMBER_UUID), HI_UINT16(MODEL_NUMBER_UUID)};
// Serial Number String
const uint8_t devInfoSerialNumberUUID[ATT_BT_UUID_SIZE] = {
LO_UINT16(SERIAL_NUMBER_UUID), HI_UINT16(SERIAL_NUMBER_UUID)};
// Firmware Revision String
const uint8_t devInfoFirmwareRevUUID[ATT_BT_UUID_SIZE] = {
LO_UINT16(FIRMWARE_REV_UUID), HI_UINT16(FIRMWARE_REV_UUID)};
// Hardware Revision String
const uint8_t devInfoHardwareRevUUID[ATT_BT_UUID_SIZE] = {
LO_UINT16(HARDWARE_REV_UUID), HI_UINT16(HARDWARE_REV_UUID)};
// Software Revision String
const uint8_t devInfoSoftwareRevUUID[ATT_BT_UUID_SIZE] = {
LO_UINT16(SOFTWARE_REV_UUID), HI_UINT16(SOFTWARE_REV_UUID)};
// Manufacturer Name String
const uint8_t devInfoMfrNameUUID[ATT_BT_UUID_SIZE] = {
LO_UINT16(MANUFACTURER_NAME_UUID), HI_UINT16(MANUFACTURER_NAME_UUID)};
// IEEE 11073-20601 Regulatory Certification Data List
const uint8_t devInfo11073CertUUID[ATT_BT_UUID_SIZE] = {
LO_UINT16(IEEE_11073_CERT_DATA_UUID), HI_UINT16(IEEE_11073_CERT_DATA_UUID)};
// PnP ID
const uint8_t devInfoPnpIdUUID[ATT_BT_UUID_SIZE] = {
LO_UINT16(PNP_ID_UUID), HI_UINT16(PNP_ID_UUID)};
/*********************************************************************
* EXTERNAL VARIABLES
*/
/*********************************************************************
* EXTERNAL FUNCTIONS
*/
/*********************************************************************
* LOCAL VARIABLES
*/
/*********************************************************************
* Profile Attributes - variables
*/
// Device Information Service attribute
static const gattAttrType_t devInfoService = {ATT_BT_UUID_SIZE, devInfoServUUID};
// System ID characteristic
static uint8_t devInfoSystemIdProps = GATT_PROP_READ;
static uint8_t devInfoSystemId[DEVINFO_SYSTEM_ID_LEN] = {0, 0, 0, 0, 0, 0, 0, 0};
// Model Number String characteristic
static uint8_t devInfoModelNumberProps = GATT_PROP_READ;
static const uint8_t devInfoModelNumber[] = "Model Number";
// Serial Number String characteristic
static uint8_t devInfoSerialNumberProps = GATT_PROP_READ;
static const uint8_t devInfoSerialNumber[] = "Serial Number";
// Firmware Revision String characteristic
static uint8_t devInfoFirmwareRevProps = GATT_PROP_READ;
static const uint8_t devInfoFirmwareRev[] = "Firmware Revision";
// Hardware Revision String characteristic
static uint8_t devInfoHardwareRevProps = GATT_PROP_READ;
static const uint8_t devInfoHardwareRev[] = "Hardware Revision";
// Software Revision String characteristic
static uint8_t devInfoSoftwareRevProps = GATT_PROP_READ;
static const uint8_t devInfoSoftwareRev[] = "Software Revision";
// Manufacturer Name String characteristic
static uint8_t devInfoMfrNameProps = GATT_PROP_READ;
static const uint8_t devInfoMfrName[] = "Manufacturer Name";
// IEEE 11073-20601 Regulatory Certification Data List characteristic
static uint8_t devInfo11073CertProps = GATT_PROP_READ;
static const uint8_t devInfo11073Cert[] = {
DEVINFO_11073_BODY_EXP, // authoritative body type
0x00, // authoritative body structure type
// authoritative body data follows below:
'e', 'x', 'p', 'e', 'r', 'i', 'm', 'e', 'n', 't', 'a', 'l'};
// System ID characteristic
static uint8_t devInfoPnpIdProps = GATT_PROP_READ;
static uint8_t devInfoPnpId[DEVINFO_PNP_ID_LEN] = {
1, // Vendor ID source (1=Bluetooth SIG)
LO_UINT16(0x07D7), HI_UINT16(0x07D7), // Vendor ID (WCH)
LO_UINT16(0x0000), HI_UINT16(0x0000), // Product ID (vendor-specific)
LO_UINT16(0x0110), HI_UINT16(0x0110) // Product version (JJ.M.N)
};
/*********************************************************************
* Profile Attributes - Table
*/
static gattAttribute_t devInfoAttrTbl[] = {
// Device Information Service
{
{ATT_BT_UUID_SIZE, primaryServiceUUID}, /* type */
GATT_PERMIT_AUTHEN_READ, /* permissions */
0, /* handle */
(uint8_t *)&devInfoService /* pValue */
},
// System ID Declaration
{
{ATT_BT_UUID_SIZE, characterUUID},
GATT_PERMIT_AUTHEN_READ,
0,
&devInfoSystemIdProps},
// System ID Value
{
{ATT_BT_UUID_SIZE, devInfoSystemIdUUID},
GATT_PERMIT_AUTHEN_READ,
0,
(uint8_t *)devInfoSystemId},
// Model Number String Declaration
{
{ATT_BT_UUID_SIZE, characterUUID},
GATT_PERMIT_READ,
0,
&devInfoModelNumberProps},
// Model Number Value
{
{ATT_BT_UUID_SIZE, devInfoModelNumberUUID},
GATT_PERMIT_READ,
0,
(uint8_t *)devInfoModelNumber},
// Serial Number String Declaration
{
{ATT_BT_UUID_SIZE, characterUUID},
GATT_PERMIT_READ,
0,
&devInfoSerialNumberProps},
// Serial Number Value
{
{ATT_BT_UUID_SIZE, devInfoSerialNumberUUID},
GATT_PERMIT_READ,
0,
(uint8_t *)devInfoSerialNumber},
// Firmware Revision String Declaration
{
{ATT_BT_UUID_SIZE, characterUUID},
GATT_PERMIT_READ,
0,
&devInfoFirmwareRevProps},
// Firmware Revision Value
{
{ATT_BT_UUID_SIZE, devInfoFirmwareRevUUID},
GATT_PERMIT_READ,
0,
(uint8_t *)devInfoFirmwareRev},
// Hardware Revision String Declaration
{
{ATT_BT_UUID_SIZE, characterUUID},
GATT_PERMIT_READ,
0,
&devInfoHardwareRevProps},
// Hardware Revision Value
{
{ATT_BT_UUID_SIZE, devInfoHardwareRevUUID},
GATT_PERMIT_READ,
0,
(uint8_t *)devInfoHardwareRev},
// Software Revision String Declaration
{
{ATT_BT_UUID_SIZE, characterUUID},
GATT_PERMIT_READ,
0,
&devInfoSoftwareRevProps},
// Software Revision Value
{
{ATT_BT_UUID_SIZE, devInfoSoftwareRevUUID},
GATT_PERMIT_READ,
0,
(uint8_t *)devInfoSoftwareRev},
// Manufacturer Name String Declaration
{
{ATT_BT_UUID_SIZE, characterUUID},
GATT_PERMIT_READ,
0,
&devInfoMfrNameProps},
// Manufacturer Name Value
{
{ATT_BT_UUID_SIZE, devInfoMfrNameUUID},
GATT_PERMIT_READ,
0,
(uint8_t *)devInfoMfrName},
// IEEE 11073-20601 Regulatory Certification Data List Declaration
{
{ATT_BT_UUID_SIZE, characterUUID},
GATT_PERMIT_READ,
0,
&devInfo11073CertProps},
// IEEE 11073-20601 Regulatory Certification Data List Value
{
{ATT_BT_UUID_SIZE, devInfo11073CertUUID},
GATT_PERMIT_READ,
0,
(uint8_t *)devInfo11073Cert},
// PnP ID Declaration
{
{ATT_BT_UUID_SIZE, characterUUID},
GATT_PERMIT_READ,
0,
&devInfoPnpIdProps},
// PnP ID Value
{
{ATT_BT_UUID_SIZE, devInfoPnpIdUUID},
GATT_PERMIT_READ,
0,
(uint8_t *)devInfoPnpId}
};
/*********************************************************************
* LOCAL FUNCTIONS
*/
static bStatus_t devInfo_ReadAttrCB(uint16_t connHandle, gattAttribute_t *pAttr,
uint8_t *pValue, uint16_t *pLen, uint16_t offset, uint16_t maxLen, uint8_t method);
/*********************************************************************
* PROFILE CALLBACKS
*/
// Device Info Service Callbacks
gattServiceCBs_t devInfoCBs = {
devInfo_ReadAttrCB, // Read callback function pointer
NULL, // Write callback function pointer
NULL // Authorization callback function pointer
};
/*********************************************************************
* NETWORK LAYER CALLBACKS
*/
/*********************************************************************
* PUBLIC FUNCTIONS
*/
/*********************************************************************
* @fn DevInfo_AddService
*
* @brief Initializes the Device Information service by registering
* GATT attributes with the GATT server.
*
* @return Success or Failure
*/
bStatus_t DevInfo_AddService(void)
{
// Register GATT attribute list and CBs with GATT Server App
return GATTServApp_RegisterService(devInfoAttrTbl,
GATT_NUM_ATTRS(devInfoAttrTbl),
GATT_MAX_ENCRYPT_KEY_SIZE,
&devInfoCBs);
}
/*********************************************************************
* @fn DevInfo_SetParameter
*
* @brief Set a Device Information parameter.
*
* @param param - Profile parameter ID
* @param len - length of data to write
* @param value - pointer to data to write. This is dependent on
* the parameter ID and WILL be cast to the appropriate
* data type (example: data type of uint16_t will be cast to
* uint16_t pointer).
*
* @return bStatus_t
*/
bStatus_t DevInfo_SetParameter(uint8_t param, uint8_t len, void *value)
{
bStatus_t ret = SUCCESS;
switch(param)
{
case DEVINFO_SYSTEM_ID:
tmos_memcpy(devInfoSystemId, value, len);
break;
default:
ret = INVALIDPARAMETER;
break;
}
return (ret);
}
/*********************************************************************
* @fn DevInfo_GetParameter
*
* @brief Get a Device Information parameter.
*
* @param param - Profile parameter ID
* @param value - pointer to data to get. This is dependent on
* the parameter ID and WILL be cast to the appropriate
* data type (example: data type of uint16_t will be cast to
* uint16_t pointer).
*
* @return bStatus_t
*/
bStatus_t DevInfo_GetParameter(uint8_t param, void *value)
{
bStatus_t ret = SUCCESS;
switch(param)
{
case DEVINFO_SYSTEM_ID:
tmos_memcpy(value, devInfoSystemId, sizeof(devInfoSystemId));
break;
case DEVINFO_MODEL_NUMBER:
tmos_memcpy(value, devInfoModelNumber, sizeof(devInfoModelNumber));
break;
case DEVINFO_SERIAL_NUMBER:
tmos_memcpy(value, devInfoSerialNumber, sizeof(devInfoSerialNumber));
break;
case DEVINFO_FIRMWARE_REV:
tmos_memcpy(value, devInfoFirmwareRev, sizeof(devInfoFirmwareRev));
break;
case DEVINFO_HARDWARE_REV:
tmos_memcpy(value, devInfoHardwareRev, sizeof(devInfoHardwareRev));
break;
case DEVINFO_SOFTWARE_REV:
tmos_memcpy(value, devInfoSoftwareRev, sizeof(devInfoSoftwareRev));
break;
case DEVINFO_MANUFACTURER_NAME:
tmos_memcpy(value, devInfoMfrName, sizeof(devInfoMfrName));
break;
case DEVINFO_11073_CERT_DATA:
tmos_memcpy(value, devInfo11073Cert, sizeof(devInfo11073Cert));
break;
case DEVINFO_PNP_ID:
tmos_memcpy(value, devInfoPnpId, sizeof(devInfoPnpId));
break;
default:
ret = INVALIDPARAMETER;
break;
}
return (ret);
}
/*********************************************************************
* @fn devInfo_ReadAttrCB
*
* @brief Read an attribute.
*
* @param connHandle - connection message was received on
* @param pAttr - pointer to attribute
* @param pValue - pointer to data to be read
* @param pLen - length of data to be read
* @param offset - offset of the first octet to be read
* @param maxLen - maximum length of data to be read
*
* @return Success or Failure
*/
static bStatus_t devInfo_ReadAttrCB(uint16_t connHandle, gattAttribute_t *pAttr,
uint8_t *pValue, uint16_t *pLen, uint16_t offset, uint16_t maxLen, uint8_t method)
{
bStatus_t status = SUCCESS;
uint16_t uuid = BUILD_UINT16(pAttr->type.uuid[0], pAttr->type.uuid[1]);
switch(uuid)
{
case SYSTEM_ID_UUID:
// verify offset
if(offset >= sizeof(devInfoSystemId))
{
status = ATT_ERR_INVALID_OFFSET;
}
else
{
// determine read length
*pLen = MIN(maxLen, (sizeof(devInfoSystemId) - offset));
// copy data
tmos_memcpy(pValue, &devInfoSystemId[offset], *pLen);
}
break;
case MODEL_NUMBER_UUID:
// verify offset
if(offset >= (sizeof(devInfoModelNumber) - 1))
{
status = ATT_ERR_INVALID_OFFSET;
}
else
{
// determine read length (exclude null terminating character)
*pLen = MIN(maxLen, ((sizeof(devInfoModelNumber) - 1) - offset));
// copy data
tmos_memcpy(pValue, &devInfoModelNumber[offset], *pLen);
}
break;
case SERIAL_NUMBER_UUID:
// verify offset
if(offset >= (sizeof(devInfoSerialNumber) - 1))
{
status = ATT_ERR_INVALID_OFFSET;
}
else
{
// determine read length (exclude null terminating character)
*pLen = MIN(maxLen, ((sizeof(devInfoSerialNumber) - 1) - offset));
// copy data
tmos_memcpy(pValue, &devInfoSerialNumber[offset], *pLen);
}
break;
case FIRMWARE_REV_UUID:
// verify offset
if(offset >= (sizeof(devInfoFirmwareRev) - 1))
{
status = ATT_ERR_INVALID_OFFSET;
}
else
{
// determine read length (exclude null terminating character)
*pLen = MIN(maxLen, ((sizeof(devInfoFirmwareRev) - 1) - offset));
// copy data
tmos_memcpy(pValue, &devInfoFirmwareRev[offset], *pLen);
}
break;
case HARDWARE_REV_UUID:
// verify offset
if(offset >= (sizeof(devInfoHardwareRev) - 1))
{
status = ATT_ERR_INVALID_OFFSET;
}
else
{
// determine read length (exclude null terminating character)
*pLen = MIN(maxLen, ((sizeof(devInfoHardwareRev) - 1) - offset));
// copy data
tmos_memcpy(pValue, &devInfoHardwareRev[offset], *pLen);
}
break;
case SOFTWARE_REV_UUID:
// verify offset
if(offset >= (sizeof(devInfoSoftwareRev) - 1))
{
status = ATT_ERR_INVALID_OFFSET;
}
else
{
// determine read length (exclude null terminating character)
*pLen = MIN(maxLen, ((sizeof(devInfoSoftwareRev) - 1) - offset));
// copy data
tmos_memcpy(pValue, &devInfoSoftwareRev[offset], *pLen);
}
break;
case MANUFACTURER_NAME_UUID:
// verify offset
if(offset >= (sizeof(devInfoMfrName) - 1))
{
status = ATT_ERR_INVALID_OFFSET;
}
else
{
// determine read length (exclude null terminating character)
*pLen = MIN(maxLen, ((sizeof(devInfoMfrName) - 1) - offset));
// copy data
tmos_memcpy(pValue, &devInfoMfrName[offset], *pLen);
}
break;
case IEEE_11073_CERT_DATA_UUID:
// verify offset
if(offset >= sizeof(devInfo11073Cert))
{
status = ATT_ERR_INVALID_OFFSET;
}
else
{
// determine read length
*pLen = MIN(maxLen, (sizeof(devInfo11073Cert) - offset));
// copy data
tmos_memcpy(pValue, &devInfo11073Cert[offset], *pLen);
}
break;
case PNP_ID_UUID:
// verify offset
if(offset >= sizeof(devInfoPnpId))
{
status = ATT_ERR_INVALID_OFFSET;
}
else
{
// determine read length
*pLen = MIN(maxLen, (sizeof(devInfoPnpId) - offset));
// copy data
tmos_memcpy(pValue, &devInfoPnpId[offset], *pLen);
}
break;
default:
*pLen = 0;
status = ATT_ERR_ATTR_NOT_FOUND;
break;
}
return (status);
}
/*********************************************************************
*********************************************************************/
@@ -0,0 +1,109 @@
/********************************** (C) COPYRIGHT *******************************
* File Name : devinfoservice.h
* Author : WCH
* Version : V1.0
* Date : 2018/12/11
* Description :
*********************************************************************************
* Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd.
* Attention: This software (modified or not) and binary are used for
* microcontroller manufactured by Nanjing Qinheng Microelectronics.
*******************************************************************************/
#ifndef DEVINFOSERVICE_H
#define DEVINFOSERVICE_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************************************************************
* INCLUDES
*/
/*********************************************************************
* CONSTANTS
*/
// Device Information Service Parameters
#define DEVINFO_SYSTEM_ID 0
#define DEVINFO_MODEL_NUMBER 1
#define DEVINFO_SERIAL_NUMBER 2
#define DEVINFO_FIRMWARE_REV 3
#define DEVINFO_HARDWARE_REV 4
#define DEVINFO_SOFTWARE_REV 5
#define DEVINFO_MANUFACTURER_NAME 6
#define DEVINFO_11073_CERT_DATA 7
#define DEVINFO_PNP_ID 8
// IEEE 11073 authoritative body values
#define DEVINFO_11073_BODY_EMPTY 0
#define DEVINFO_11073_BODY_IEEE 1
#define DEVINFO_11073_BODY_CONTINUA 2
#define DEVINFO_11073_BODY_EXP 254
// System ID length
#define DEVINFO_SYSTEM_ID_LEN 8
// PnP ID length
#define DEVINFO_PNP_ID_LEN 7
/*********************************************************************
* TYPEDEFS
*/
/*********************************************************************
* MACROS
*/
/*********************************************************************
* Profile Callbacks
*/
/*********************************************************************
* API FUNCTIONS
*/
/*
* DevInfo_AddService- Initializes the Device Information service by registering
* GATT attributes with the GATT server.
*
*/
extern bStatus_t DevInfo_AddService(void);
/*********************************************************************
* @fn DevInfo_SetParameter
*
* @brief Set a Device Information parameter.
*
* @param param - Profile parameter ID
* @param len - length of data to right
* @param value - pointer to data to write. This is dependent on
* the parameter ID and WILL be cast to the appropriate
* data type (example: data type of uint16_t will be cast to
* uint16_t pointer).
*
* @return bStatus_t
*/
bStatus_t DevInfo_SetParameter(uint8_t param, uint8_t len, void *value);
/*
* DevInfo_GetParameter - Get a Device Information parameter.
*
* param - Profile parameter ID
* value - pointer to data to write. This is dependent on
* the parameter ID and WILL be cast to the appropriate
* data type (example: data type of uint16_t will be cast to
* uint16_t pointer).
*/
extern bStatus_t DevInfo_GetParameter(uint8_t param, void *value);
/*********************************************************************
*********************************************************************/
#ifdef __cplusplus
}
#endif
#endif /* DEVINFOSERVICE_H */