first submission
This commit is contained in:
@@ -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=""${workspace_loc:/${ProjName}/app/include}""/>
|
||||
<listOptionValue builtIn="false" value=""${workspace_loc:/${ProjName}/includes}""/>
|
||||
<listOptionValue builtIn="false" value=""${workspace_loc:/${ProjName}/services}""/>
|
||||
<listOptionValue builtIn="false" value=""${workspace_loc:/${ProjName}/SRC/Core}""/>
|
||||
<listOptionValue builtIn="false" value=""${workspace_loc:/${ProjName}/SRC/Startup}""/>
|
||||
<listOptionValue builtIn="false" value=""${workspace_loc:/${ProjName}/SRC/Debug}""/>
|
||||
<listOptionValue builtIn="false" value=""${workspace_loc:/${ProjName}/SRC/Peripheral/inc}""/>
|
||||
<listOptionValue builtIn="false" value=""${workspace_loc:/${ProjName}/APP/include}""/>
|
||||
<listOptionValue builtIn="false" value=""${workspace_loc:/${ProjName}/Profile/include}""/>
|
||||
<listOptionValue builtIn="false" value=""${workspace_loc:/${ProjName}/HAL/include}""/>
|
||||
<listOptionValue builtIn="false" value=""${workspace_loc:/${ProjName}/app/include}""/>
|
||||
<listOptionValue builtIn="false" value=""${workspace_loc:/${ProjName}/LIB}""/>
|
||||
</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=""${workspace_loc:/${ProjName}/includes}""/>
|
||||
</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=""../""/>
|
||||
<listOptionValue builtIn="false" value=""${workspace_loc:/${ProjName}/LIB}""/>
|
||||
</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=""${workspace_loc:/${ProjName}/HAL/Link.ld}""/>
|
||||
</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=""../LD""/>
|
||||
</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(¬ification_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(¬if_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, ¬ification_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_ */
|
||||
Binary file not shown.
@@ -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, ¬if);
|
||||
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(¬if);
|
||||
|
||||
bt_ancs_do_ns_notif_cb(ancs_c, err, ¬if);
|
||||
}
|
||||
|
||||
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 */
|
||||
Reference in New Issue
Block a user