summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore5
m---------afb-utilities0
-rw-r--r--conf.d/app-templates/LICENSE201
-rw-r--r--conf.d/app-templates/README.md382
-rw-r--r--conf.d/app-templates/book.json94
-rw-r--r--conf.d/app-templates/cmake/cmake.d/01-build_options.cmake113
-rw-r--r--conf.d/app-templates/cmake/cmake.d/02-variables.cmake174
-rw-r--r--conf.d/app-templates/cmake/cmake.d/03-macros.cmake501
-rw-r--r--conf.d/app-templates/cmake/cmake.d/04-extra_targets.cmake162
-rw-r--r--conf.d/app-templates/cmake/common.cmake63
-rw-r--r--conf.d/app-templates/cmake/configure_file.cmake2
-rw-r--r--conf.d/app-templates/cmake/export.map1
-rw-r--r--conf.d/app-templates/docs/0-Doc-Revisions.md6
-rw-r--r--conf.d/app-templates/docs/README.md22
-rw-r--r--conf.d/app-templates/docs/SUMMARY.md10
-rw-r--r--conf.d/app-templates/docs/_layouts/ebook/page.html36
-rw-r--r--conf.d/app-templates/docs/_layouts/ebook/pdf_footer.html13
-rw-r--r--conf.d/app-templates/docs/_layouts/ebook/pdf_header.html13
-rw-r--r--conf.d/app-templates/docs/_layouts/ebook/summary.html58
-rw-r--r--conf.d/app-templates/docs/_layouts/layout.html28
-rw-r--r--conf.d/app-templates/docs/cover.jpgbin0 -> 237964 bytes
-rw-r--r--conf.d/app-templates/docs/cover_small.jpgbin0 -> 12433 bytes
-rw-r--r--conf.d/app-templates/docs/dev_guide/0_Abstract.md21
-rw-r--r--conf.d/app-templates/docs/dev_guide/1_Quickstart.md81
-rw-r--r--conf.d/app-templates/docs/dev_guide/2_project_architecture.md98
-rw-r--r--conf.d/app-templates/docs/dev_guide/3_advanced_usage.md102
-rw-r--r--conf.d/app-templates/docs/dev_guide/4_advanced_customization.md42
-rw-r--r--conf.d/app-templates/docs/dev_guide/5_autobuild.md41
-rw-r--r--conf.d/app-templates/docs/dev_guide/pictures/iotbzh_logo_small.pngbin0 -> 6989 bytes
-rw-r--r--conf.d/app-templates/docs/resources/cover.svg210
-rw-r--r--conf.d/app-templates/docs/resources/ebook.css386
-rwxr-xr-xconf.d/app-templates/docs/resources/make_cover.sh27
-rwxr-xr-xconf.d/app-templates/gendocs.sh79
-rw-r--r--conf.d/app-templates/samples.d/CMakeLists.txt.sample21
-rw-r--r--conf.d/app-templates/samples.d/config.cmake.sample203
-rw-r--r--conf.d/app-templates/samples.d/config.xml.in.sample142
-rw-r--r--conf.d/app-templates/samples.d/xds-config.env.sample10
-rwxr-xr-xconf.d/app-templates/template.d/autobuild/agl/autobuild.in63
-rwxr-xr-xconf.d/app-templates/template.d/autobuild/linux/autobuild.in65
-rw-r--r--conf.d/app-templates/template.d/config.xml.in9
-rw-r--r--conf.d/app-templates/template.d/deb-config.dsc.in15
-rw-r--r--conf.d/app-templates/template.d/deb-config.install.in2
-rw-r--r--conf.d/app-templates/template.d/debian.changelog.in5
-rw-r--r--conf.d/app-templates/template.d/debian.compat.in1
-rw-r--r--conf.d/app-templates/template.d/debian.control.in16
-rw-r--r--conf.d/app-templates/template.d/debian.rules.in87
-rw-r--r--conf.d/app-templates/template.d/gdb-native-target.ini.in35
-rwxr-xr-xconf.d/app-templates/template.d/install-wgt-on-target.sh.in19
-rw-r--r--conf.d/app-templates/template.d/rpm-config.spec.in62
-rwxr-xr-xconf.d/app-templates/template.d/start-on-target.sh.in25
-rw-r--r--conf.d/app-templates/wgt/icon-default.pngbin0 -> 10651 bytes
-rw-r--r--conf.d/app-templates/wgt/icon-html5.pngbin0 -> 6848 bytes
-rw-r--r--conf.d/app-templates/wgt/icon-native.pngbin0 -> 5276 bytes
-rw-r--r--conf.d/app-templates/wgt/icon-qml.pngbin0 -> 4971 bytes
-rw-r--r--conf.d/app-templates/wgt/icon-service.pngbin0 -> 13273 bytes
-rw-r--r--conf.d/project/json.d/ahl-binding.json127
-rw-r--r--conf.d/project/json.d/onload-audio-control.json137
-rw-r--r--htdocs/CMakeLists.txt14
-rw-r--r--htdocs/alsa-core.html52
-rw-r--r--htdocs/audiohl-demo.html47
-rw-r--r--htdocs/audiohl.html135
-rw-r--r--htdocs/index.html3
-rw-r--r--src/CMakeLists.txt21
-rw-r--r--src/ahl-apidef.h336
-rw-r--r--src/ahl-apidef.json156
-rw-r--r--src/ahl-binding.c530
-rw-r--r--src/ahl-binding.h113
-rw-r--r--src/ahl-config.c148
-rw-r--r--src/ahl-deviceenum.c412
-rw-r--r--src/ahl-interface.h74
-rw-r--r--src/ahl-policy.c90
71 files changed, 5562 insertions, 584 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..b092854
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,5 @@
+build/
+CMakeCache.txt
+Makefile
+*.so
+nbproject/private
diff --git a/afb-utilities b/afb-utilities
new file mode 160000
+Subproject 1860c197a402f76f960eb2e373daf1259592cb6
diff --git a/conf.d/app-templates/LICENSE b/conf.d/app-templates/LICENSE
new file mode 100644
index 0000000..93b07d3
--- /dev/null
+++ b/conf.d/app-templates/LICENSE
@@ -0,0 +1,201 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "{}"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright 2017 claneys
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/conf.d/app-templates/README.md b/conf.d/app-templates/README.md
new file mode 100644
index 0000000..96dfb3c
--- /dev/null
+++ b/conf.d/app-templates/README.md
@@ -0,0 +1,382 @@
+# AGL CMake template
+
+Files used to build an application, or binding, project with the
+AGL Application Framework.
+
+To build your AGL project using these templates, you have to install
+them within your project and adjust compilation option in `config.cmake`.
+For technical reasons, you also have to specify **cmake** target in
+sub CMakeLists.txt installed. Make a globbing search to find source files
+isn't recommended now to handle project build especially in a multiuser
+project because CMake will not be aware of new or removed source files.
+
+You'll find usage samples here:
+
+- [helloworld-service](https://github.com/iotbzh/helloworld-service)
+- [low-level-can-service](https://gerrit.automotivelinux.org/gerrit/apps/low-level-can-service)
+- [high-level-viwi-service](https://github.com/iotbzh/high-level-viwi-service)
+- [audio-binding](https://github.com/iotbzh/audio-binding)
+- [unicens2-binding](https://github.com/iotbzh/unicens2-binding)
+
+## Quickstart
+
+### Initialization
+
+To use these templates files on your project just install the reference files using
+**git submodule** then use `config.cmake` file to configure your project specificities :
+
+```bash
+git submodule add https://gerrit.automotivelinux.org/gerrit/p/apps/app-templates.git conf.d/app-templates
+mkdir conf.d/cmake
+cp conf.d/app-templates/cmake/config.cmake.sample conf.d/cmake/config.cmake
+```
+
+Edit the copied config.cmake file to fit your needs.
+
+Now, create your top CMakeLists.txt file which include `config.cmake` file.
+
+An example is available in **app-templates** submodule that you can copy and
+use:
+
+```bash
+cp conf.d/app-templates/cmake/CMakeLists.txt.sample CMakeLists.txt
+```
+
+### Create your CMake targets
+
+For each target part of your project, you need to use ***PROJECT_TARGET_ADD***
+to include this target to your project.
+
+Using it, make available the cmake variable ***TARGET_NAME*** until the next
+***PROJECT_TARGET_ADD*** is invoked with a new target name.
+
+So, typical usage defining a target is:
+
+```cmake
+PROJECT_TARGET_ADD(SuperExampleName) --> Adding target to your project
+
+add_executable/add_library(${TARGET_NAME}.... --> defining your target sources
+
+SET_TARGET_PROPERTIES(${TARGET_NAME} PROPERTIES.... --> fit target properties
+for macros usage
+
+INSTALL(TARGETS ${TARGET_NAME}....
+```
+
+### Targets PROPERTIES
+
+You should set properties on your targets that will be used to package your
+apps in a widget file that could be installed on an AGL system.
+
+Specify what is the type of your targets that you want to be included in the
+widget package with the property **LABELS**:
+
+Choose between:
+
+- **BINDING**: Shared library that be loaded by the AGL Application Framework
+- **BINDINGV2**: Shared library that be loaded by the AGL Application Framework.
+ This has to be accompagnied with a JSON file named like the *${OUTPUT_NAME}-apidef* of
+ the target that describe the API with OpenAPI syntax (e.g: *mybinding-apidef*).
+ Or you can choose the name by setting the *CACHE* cmake variable *OPENAPI_DEF*
+ (***CAUTION***: setting a CACHE variable is needed, or set a normal variable
+ with the *PARENT_SCOPE* option to make it visible for the parent scope
+ where the target is defined) JSON file will be used to generate header file
+ using `afb-genskel` tool.
+- **HTDOCS**: Root directory of a web app. This target has to build its
+ directory and puts its files in the ${CMAKE_CURRENT_BINARY_DIR}/${TARGET_NAME}
+- **DATA**: Resources used by your application. This target has to build its
+ directory and puts its files in the ${CMAKE_CURRENT_BINARY_DIR}/${TARGET_NAME}
+- **EXECUTABLE**: Entry point of your application executed by the AGL
+ Application Framework
+
+```cmake
+SET_TARGET_PROPERTIES(${TARGET_NAME}
+ PREFIX "afb-"
+ LABELS "BINDING"
+ OUTPUT_NAME "file_output_name")
+```
+
+> **TIP** you should use the prefix _afb-_ with your **BINDING* targets which
+> stand for **Application Framework Binding**.
+
+## More details: Typical project architecture
+
+A typical project architecture would be :
+
+```tree
+<project-root-path>
+│
+├── conf.d/
+│ ├── autobuild/
+│ │ ├── agl
+│ │ │ └── autobuild
+│ │ ├── linux
+│ │ │ └── autobuild
+│ │ └── windows
+│ │ └── autobuild
+│ ├── app-templates/
+│ │ ├── README.md
+│ │ ├── autobuild/
+│ │ │ ├── agl
+│ │ │ │ └── autobuild.in
+│ │ │ ├── linux
+│ │ │ │ └── autobuild.in
+│ │ │ └── windows
+│ │ │ └── autobuild.in
+│ │ ├── cmake/
+│ │ │ ├── config.cmake.sample
+│ │ │ ├── export.map
+│ │ │ └── macros.cmake
+│ │ ├── deb/
+│ │ │ └── config.deb.in
+│ │ ├── rpm/
+│ │ │ └── config.spec.in
+│ │ └── wgt/
+│ │ ├── config.xml.in
+│ │ ├── config.xml.in.sample
+│ │ ├── icon-default.png
+│ │ ├── icon-html5.png
+│ │ ├── icon-native.png
+│ │ ├── icon-qml.png
+│ │ └── icon-service.png
+│ ├── packaging/
+│ │ ├── config.spec
+│ │ └── config.deb
+│ ├── cmake
+│ │ └── config.cmake
+│ └── wgt
+│ └── config.xml.in
+├── <libs>
+├── <target>
+│ └── <files>
+├── <target>
+│ └── <file>
+└── <target>
+ └── <files>
+```
+
+| # | Parent | Description |
+| - | -------| ----------- |
+| \<root-path\> | - | Path to your project. Hold master CMakeLists.txt and general files of your projects. |
+| conf.d | \<root-path\> | Holds needed files to build, install, debug, package an AGL app project |
+| app-templates | conf.d | Git submodule to app-templates AGL repository which provides CMake helpers macros library, and build scripts. config.cmake is a copy of config.cmake.sample configured for the projects. SHOULD NOT BE MODIFIED MANUALLY !|
+| autobuild | conf.d | Scripts generated from app-templates to build packages the same way for differents platforms.|
+| cmake | conf.d | Contains at least config.cmake file modified from the sample provided in app-templates submodule. |
+| wgt | conf.d | Contains at least config.xml.in template file modified from the sample provided in app-templates submodule for the needs of project (See config.xml.in.sample file for more details). |
+| packaging | conf.d | Contains output files used to build packages. |
+| \<libs\> | \<root-path\> | External dependencies libraries. This isn't to be used to include header file but build and link statically specifics libraries. | Library sources files. Can be a decompressed library archive file or project fork. |
+| \<target\> | \<root-path\> | A target to build, typically library, executable, etc. |
+
+### Update app-templates submodule
+
+You may have some news bug fixes or features available from app-templates
+repository that you want. To update your submodule proceed like the following:
+
+```bash
+git submodule update --remote
+git commit -s conf.d/app-templates
+```
+
+This will update the submodule to the HEAD of master branch repository.
+
+You could just want to update at a specified repository tag or branch or commit
+, here are the method to do so:
+
+```bash
+cd conf.d/app-templates
+# Choose one of the following depending what you want
+git checkout <tag_name>
+git checkout --detach <branch_name>
+git checkout --detach <commit_id>
+# Then commit
+cd ../..
+git commit -s conf.d/app-templates
+```
+
+### Build a widget
+
+#### config.xml.in file
+
+To build a widget you need a _config.xml_ file describing what is your apps and
+how Application Framework would launch it. This repo provide a simple default
+file _config.xml.in_ that should work for simple application without
+interactions with others bindings.
+
+It is recommanded that you use the sample one which is more complete. You can
+find it at the same location under the name _config.xml.in.sample_ (stunning
+isn't it). Just copy the sample file to your _conf.d/wgt_ directory and name it
+_config.xml.in_, then edit it to fit your needs.
+
+> ***CAUTION*** : The default file is only meant to be use for a
+> simple widget app, more complicated ones which needed to export
+> their api, or ship several app in one widget need to use the provided
+> _config.xml.in.sample_ which had all new Application Framework
+> features explained and examples.
+
+#### Using cmake template macros
+
+To leverage all cmake templates features, you have to specify ***properties***
+on your targets. Some macros will not works without specifying which is the
+target type.
+
+As the type is not always specified for some custom targets, like an ***HTML5***
+application, macros make the difference using ***LABELS*** property.
+
+Choose between:
+
+- **BINDING**: Shared library that be loaded by the AGL Application Framework
+- **BINDINGV2**: Shared library that be loaded by the AGL Application Framework.
+ This has to be accompagnied with a JSON file named like the *${OUTPUT_NAME}-apidef* of
+ the target that describe the API with OpenAPI syntax (e.g: *mybinding-apidef*).
+ Or you can choose the name by setting the *CACHE* cmake variable *OPENAPI_DEF*
+ (***CAUTION***: setting a CACHE variable is needed, or set a normal variable
+ with the *PARENT_SCOPE* option to make it visible for the parent scope
+ where the target is defined) JSON file will be used to generate header file
+ using `afb-genskel` tool.
+- **HTDOCS**: Root directory of a web app. This target has to build its
+ directory and puts its files in the ${CMAKE_CURRENT_BINARY_DIR}/${TARGET_NAME}
+- **DATA**: Resources used by your application. This target has to build its
+ directory and puts its files in the ${CMAKE_CURRENT_BINARY_DIR}/${TARGET_NAME}
+- **EXECUTABLE**: Entry point of your application executed by the AGL
+ Application Framework
+
+Example:
+
+```cmake
+SET_TARGET_PROPERTIES(${TARGET_NAME} PROPERTIES
+ LABELS "HTDOCS"
+ OUTPUT_NAME dist.prod
+ )
+```
+
+If your target output is not named as the ***TARGET_NAME***, you need to specify
+***OUTPUT_NAME*** property that will be used by the ***populate_widget*** macro.
+
+Use the ***populate_widget*** macro as latest statement of your target
+definition. Then at the end of your project definition you should use the macro
+***build_widget*** that make an archive from the populated widget tree using the
+`wgtpkg-pack` Application Framework tools.
+
+## Macro reference
+
+### PROJECT_TARGET_ADD
+
+Typical usage would be to add the target to your project using macro
+`PROJECT_TARGET_ADD` with the name of your target as parameter.
+
+Example:
+
+```cmake
+PROJECT_TARGET_ADD(low-can-demo)
+```
+
+> ***NOTE***: This will make available the variable `${TARGET_NAME}`
+> set with the specificied name. This variable will change at the next call
+> to this macros.
+
+### project_subdirs_add
+
+This macro will search in all subfolder any `CMakeLists.txt` file. If found then
+it will be added to your project. This could be use in an hybrid application by
+example where the binding lay in a sub directory.
+
+Usage :
+
+```cmake
+project_subdirs_add()
+```
+
+You also can specify a globbing pattern as argument to filter which folders
+will be looked for.
+
+To filter all directories that begin with a number followed by a dash the
+anything:
+
+```cmake
+project_subdirs_add("[0-9]-*")
+```
+
+## Advanced customization
+
+### Including additionnals cmake files
+
+Advanced tuning is possible using addionnals cmake files that are included
+automatically from some specifics locations. They are included in that order:
+
+- Project CMake files normaly located in _<project-root-path>/conf.d/app-templates/cmake/cmake.d_
+- Home CMake files located in _$HOME/.config/app-templates/cmake.d_
+- System CMake files located in _/etc/app-templates/cmake.d_
+
+CMake files has to be named using the following convention: `XX-common-*.cmake`
+or `XX-${PROJECT_NAME}-*.cmake`, where `XX` are numbers, `*` file name
+(ie. `99-common-my_customs.cmake`).
+
+> **NOTE** You need to specify after numbers that indicate include order, to
+which project that file applies, if it applies to all project then use keyword
+`common`.
+
+So, saying that you should be aware that every normal cmake variables used at
+project level could be overwrited by home or system located cmake files if
+variables got the same name. Exceptions are cached variables set using
+**CACHE** keyword:
+
+Example:
+
+```cmake
+set(VARIABLE_NAME 'value string random' CACHE STRING 'docstring')
+```
+
+### Include customs templated scripts
+
+As well as for additionnals cmake files you can include your own templated
+scripts that will be passed to cmake command `configure_file`.
+
+Just create your own script to the following directories:
+
+- Home location in _$HOME/.config/app-templates/scripts_
+- System location in _/etc/app-templates/scripts_
+
+Scripts only needs to use the extension `.in` to be parsed and configured by
+CMake command.
+
+## Autobuild script usage
+
+### Generation
+
+To be integrated in the Yocto build workflow you have to generate `autobuild`
+scripts using _autobuild_ target.
+
+To generate those scripts proceeds:
+
+```bash
+mkdir -p build
+cd build
+cmake .. && make autobuild
+```
+
+You should see _conf.d/autobuild/agl/autobuild_ file now.
+
+### Available targets
+
+Here are the available targets available from _autobuild_ scripts:
+
+- **clean** : clean build directory from object file and targets results.
+- **distclean** : delete build directory
+- **configure** : generate project Makefile from CMakeLists.txt files.
+- **build** : compile all project targets.
+- **package** : build and output a wgt package.
+
+You can specify variables that modify the behavior of compilation using
+the following variables:
+
+- **CONFIGURE_ARGS** : Variable used at **configure** time.
+- **BUILD_ARGS** : Variable used at **build** time.
+- **DEST** : Directory where to output ***wgt*** file.
+
+Variable as to be in CMake format. (ie: BUILD_ARGS="-DC_FLAGS='-g -O2'")
+
+Usage example:
+
+```bash
+./conf.d/autobuild/wgt/autobuild package DEST=/tmp
+```
diff --git a/conf.d/app-templates/book.json b/conf.d/app-templates/book.json
new file mode 100644
index 0000000..dafc6ae
--- /dev/null
+++ b/conf.d/app-templates/book.json
@@ -0,0 +1,94 @@
+{
+ "title": "CMake Application templates",
+ "subtitle": "Developer Documentation",
+ "description": "CMake templates used to build an AGL apps and widget",
+ "keywords": "AGL, Development, Iotbzh",
+ "author": "IoT.Bzh Team",
+ "website": "http://iot.bzh",
+ "published": "July 2017",
+ "version": "1.0",
+
+ "gitbook": "3.2.2",
+ "root": "docs",
+ "pdf": {
+ "fontFamily": "Verdana",
+ "fontSize": 12,
+ "paperSize": "a4",
+ "pageBreaksBefore": "//h:div[@class=\"page-break\"]"
+ },
+ "styles": {
+ "website": "resources/ebook.css",
+ "ebook": "resources/ebook.css",
+ "pdf": "resources/ebook.css"
+ },
+
+ "hidepageheaders": [2, 3],
+ "hidepagefooters": [2, 3],
+
+ "plugins": [
+ "regexplace"
+ ],
+ "pluginsConfig": {
+ "regexplace": {
+ "removeFirstPartsInSectionNumber": true,
+ "substitutes": [{
+ "pattern": "<!-- pagebreak -->",
+ "flags": "g",
+ "substitute": "<div class=\"page-break\"></div>"
+ },
+ {
+ "pattern": "<!-- clear -->",
+ "flags": "g",
+ "substitute": "<div class=\"clear\"></div>"
+ },
+ {
+ "pattern": "<!-- nopagebreak -->",
+ "flags": "g",
+ "substitute": "<div class=\"nopb\">"
+ },
+ {
+ "pattern": "<!-- endnopagebreak -->",
+ "flags": "g",
+ "substitute": "</div>"
+ },
+ {
+ "pattern": "<!-- note -->",
+ "flags": "g",
+ "substitute": "<div class=\"note\">"
+ },
+ {
+ "pattern": "<!-- endnote -->",
+ "flags": "g",
+ "substitute": "</div>"
+ },
+ {
+ "pattern": "!\\[(.*?)\\]\\((.*?)(?:\\s+\"(.*)\")?\\){0,}{caption=1([^\\}]*)}",
+ "flags": "gmi",
+ "substitute": "<figure id=\"fig_PAGE_LEVEL_._INDEX_\"><img $3 alt=\"$1\" title=\"$1\" href=\"$2\"><figcaption></figcaption></figure>",
+ "decode": true
+ },
+ {
+ "pattern": "<img ([^>]*) {0,}\/{0,}> {0,}{caption=1([^\\}]*)}",
+ "flags": "g",
+ "substitute": "<figure id=\"fig_PAGE_LEVEL_._INDEX_\"><img $2 $1><figcaption></figcaption></figure>",
+ "decode": true
+ },
+ {
+ "pattern": "<img (.*)alt=\"([^\"]*)\"(.*) {0,1}\/{0,1}><figcaption></figcaption>",
+ "flags": "g",
+ "substitute": "<img$1alt=\"$2\"$3><figcaption><span>Picture _PAGE_LEVEL_._INDEX_</span>: $2</figcaption>",
+ "store": {
+ "substitute": "<a href=\"_PAGE_PATH_#fig_PAGE_LEVEL_._INDEX_\">Pic. _PAGE_LEVEL_._INDEX_</a> <span>$2</span>",
+ "variable_name": "pictures"
+ }
+ },
+ {
+ "pattern": "<img ([^>]*)> {0,}{style {1,}([^}]*)}",
+ "flags": "g",
+ "substitute": "<img $1 style=\"$2\">",
+ "decode": true
+ }
+ ]
+ }
+ }
+}
diff --git a/conf.d/app-templates/cmake/cmake.d/01-build_options.cmake b/conf.d/app-templates/cmake/cmake.d/01-build_options.cmake
new file mode 100644
index 0000000..4fb10ef
--- /dev/null
+++ b/conf.d/app-templates/cmake/cmake.d/01-build_options.cmake
@@ -0,0 +1,113 @@
+###########################################################################
+# Copyright 2015, 2016, 2017 IoT.bzh
+#
+# author: Fulup Ar Foll <fulup@iot.bzh>
+# contrib: Romain Forlot <romain.forlot@iot.bzh>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+###########################################################################
+
+
+#--------------------------------------------------------------------------
+# WARNING:
+# Do not change this cmake template
+# Customise your preferences in "./conf.d/cmake/config.cmake"
+#--------------------------------------------------------------------------
+
+# (BUG!!!) as PKG_CONFIG_PATH does not work [should be en env variable]
+set(PKG_CONFIG_USE_CMAKE_PREFIX_PATH ON CACHE BOOLEAN "Flag for using prefix path")
+
+INCLUDE(FindPkgConfig)
+INCLUDE(CheckIncludeFiles)
+INCLUDE(CheckLibraryExists)
+INCLUDE(GNUInstallDirs)
+
+set(CMAKE_BUILD_TYPE Debug CACHE STRING "the type of build")
+set(CMAKE_POSITION_INDEPENDENT_CODE ON)
+set(CMP0048 1)
+
+# Default compilation options
+############################################################################
+link_libraries(-Wl,--as-needed -Wl,--gc-sections)
+set(COMPILE_OPTIONS -Wall -Wextra -Wconversion -Wno-unused-parameter -Wno-sign-compare -Wno-sign-conversion -Werror=maybe-uninitialized -Werror=implicit-function-declaration -ffunction-sections -fdata-sections -fPIC CACHE STRING "Compilation flags")
+foreach(option ${COMPILE_OPTIONS})
+ add_compile_options($<$<CONFIG:PROFILING>:${option}>)
+endforeach()
+
+# Compilation OPTIONS depending on language
+#########################################
+foreach(option ${COMPILE_OPTIONS})
+ add_compile_options(${option})
+endforeach()
+foreach(option ${C_COMPILE_OPTIONS})
+ add_compile_options($<$<COMPILE_LANGUAGE:C>:${option}>)
+endforeach()
+foreach(option ${CXX_COMPILE_OPTIONS})
+ add_compile_options($<$<COMPILE_LANGUAGE:CXX>:${option}>)
+endforeach()
+
+# Compilation option depending on CMAKE_BUILD_TYPE
+##################################################
+set(PROFILING_COMPILE_OPTIONS -g -O0 -pg -Wp,-U_FORTIFY_SOURCE CACHE STRING "Compilation flags for PROFILING build type.")
+set(DEBUG_COMPILE_OPTIONS -g -ggdb -Wp,-U_FORTIFY_SOURCE CACHE STRING "Compilation flags for DEBUG build type.")
+set(CCOV_COMPILE_OPTIONS -g -O2 --coverage CACHE STRING "Compilation flags for CCOV build type.")
+set(RELEASE_COMPILE_OPTIONS -g -O2 CACHE STRING "Compilation flags for RELEASE build type.")
+foreach(option ${PROFILING_COMPILE_OPTIONS})
+ add_compile_options($<$<CONFIG:PROFILING>:${option}>)
+endforeach()
+foreach(option ${DEBUG_COMPILE_OPTIONS})
+ add_compile_options($<$<CONFIG:PROFILING>:${option}>)
+endforeach()
+foreach(option ${CCOV_COMPILE_OPTIONS})
+ add_compile_options($<$<CONFIG:PROFILING>:${option}>)
+endforeach()
+foreach(option ${RELEASE_COMPILE_OPTIONS})
+ add_compile_options($<$<CONFIG:PROFILING>:${option}>)
+endforeach()
+
+# Env variable overload default
+if(DEFINED ENV{INSTALL_PREFIX})
+ set(INSTALL_PREFIX $ENV{INSTALL_PREFIX} CACHE PATH "The path where to install")
+else()
+ set(INSTALL_PREFIX "${CMAKE_SOURCE_DIR}/Install" CACHE PATH "The path where to install")
+endif()
+set(CMAKE_INSTALL_PREFIX ${INSTALL_PREFIX} CACHE STRING "Installation Prefix")
+
+# Loop on required package and add options
+foreach (PKG_CONFIG ${PKG_REQUIRED_LIST})
+ string(REGEX REPLACE "[<>]?=.*$" "" XPREFIX ${PKG_CONFIG})
+ PKG_CHECK_MODULES(${XPREFIX} REQUIRED ${PKG_CONFIG})
+
+ INCLUDE_DIRECTORIES(${${XPREFIX}_INCLUDE_DIRS})
+ list(APPEND link_libraries ${${XPREFIX}_LDFLAGS})
+ add_compile_options (${${XPREFIX}_CFLAGS})
+endforeach(PKG_CONFIG)
+
+# Optional LibEfence Malloc debug library
+IF(CMAKE_BUILD_TYPE MATCHES DEBUG)
+CHECK_LIBRARY_EXISTS(efence malloc "" HAVE_LIBEFENCE)
+IF(HAVE_LIBEFENCE)
+ MESSAGE(STATUS "Linking with ElectricFence for debugging purposes...")
+ SET(libefence_LIBRARIES "-lefence")
+ list (APPEND link_libraries ${libefence_LIBRARIES})
+ENDIF(HAVE_LIBEFENCE)
+ENDIF(CMAKE_BUILD_TYPE MATCHES DEBUG)
+
+# set default include directories
+INCLUDE_DIRECTORIES(${EXTRA_INCLUDE_DIRS})
+
+# Default Linkflag
+set (PKG_TEMPLATE_PREFIX ${CMAKE_SOURCE_DIR}/${PROJECT_APP_TEMPLATES_DIR} CACHE PATH "Default Package Templates Directory")
+if(NOT BINDINGS_LINK_FLAG)
+ set(BINDINGS_LINK_FLAG "-Wl,--version-script=${PKG_TEMPLATE_PREFIX}/cmake/export.map")
+endif()
diff --git a/conf.d/app-templates/cmake/cmake.d/02-variables.cmake b/conf.d/app-templates/cmake/cmake.d/02-variables.cmake
new file mode 100644
index 0000000..2491473
--- /dev/null
+++ b/conf.d/app-templates/cmake/cmake.d/02-variables.cmake
@@ -0,0 +1,174 @@
+###########################################################################
+# Copyright 2015, 2016, 2017 IoT.bzh
+#
+# author: Fulup Ar Foll <fulup@iot.bzh>
+# contrib: Romain Forlot <romain.forlot@iot.bzh>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+###########################################################################
+
+
+#--------------------------------------------------------------------------
+# WARNING:
+# Do not change this cmake template
+# Customise your preferences in "./etc/config.cmake"
+#--------------------------------------------------------------------------
+
+# Get colorized message output non Windows OS. You know bash ? :)
+if(NOT WIN32)
+ string(ASCII 27 Esc)
+ set(ColourReset "${Esc}[m")
+ set(ColourBold "${Esc}[1m")
+ set(Red "${Esc}[31m")
+ set(Green "${Esc}[32m")
+ set(Yellow "${Esc}[33m")
+ set(Blue "${Esc}[34m")
+ set(Magenta "${Esc}[35m")
+ set(Cyan "${Esc}[36m")
+ set(White "${Esc}[37m")
+ set(BoldRed "${Esc}[1;31m")
+ set(BoldGreen "${Esc}[1;32m")
+ set(BoldYellow "${Esc}[1;33m")
+ set(BoldBlue "${Esc}[1;34m")
+ set(BoldMagenta "${Esc}[1;35m")
+ set(BoldCyan "${Esc}[1;36m")
+ set(BoldWhite "${Esc}[1;37m")
+endif()
+
+# Native packaging name
+set(NPKG_PROJECT_NAME agl-${PROJECT_NAME})
+
+if(DEFINED ENV{SDKTARGETSYSROOT})
+file(STRINGS $ENV{SDKTARGETSYSROOT}/usr/include/linux/version.h LINUX_VERSION_CODE_LINE REGEX "LINUX_VERSION_CODE")
+set(BUILD_ENV_SYSROOT $ENV{SDKTARGETSYSROOT})
+elseif(DEFINED ENV{PKG_CONFIG_SYSROOT_DIR})
+file(STRINGS $ENV{PKG_CONFIG_SYSROOT_DIR}/usr/include/linux/version.h LINUX_VERSION_CODE_LINE REGEX "LINUX_VERSION_CODE")
+set(BUILD_ENV_SYSROOT $ENV{PKG_CONFIG_SYSROOT_DIR})
+else()
+file(STRINGS /usr/include/linux/version.h LINUX_VERSION_CODE_LINE REGEX "LINUX_VERSION_CODE")
+set(BUILD_ENV_SYSROOT "")
+endif()
+
+string(REGEX MATCH "[0-9]+" LINUX_VERSION_CODE ${LINUX_VERSION_CODE_LINE})
+math(EXPR a "${LINUX_VERSION_CODE} >> 16")
+math(EXPR b "(${LINUX_VERSION_CODE} >> 8) & 255")
+math(EXPR c "(${LINUX_VERSION_CODE} & 255)")
+
+set(KERNEL_VERSION "${a}.${b}.${c}")
+
+# Get the os type
+# Used to package .deb
+set(OS_RELEASE_PATH "${BUILD_ENV_SYSROOT}/etc/os-release")
+if(EXISTS ${OS_RELEASE_PATH})
+ execute_process(COMMAND bash "-c" "grep -E '^ID(_LIKE)?=' ${OS_RELEASE_PATH} | tail -n 1"
+ OUTPUT_VARIABLE TMP_OSRELEASE
+ )
+
+ if (NOT TMP_OSRELEASE STREQUAL "")
+ string(REGEX REPLACE ".*=\"?([0-9a-z\._-]*)\"?\n" "\\1" OSRELEASE ${TMP_OSRELEASE})
+ else()
+ set(OSRELEASE "NOT COMPATIBLE !")
+ endif()
+
+else()
+ set(OSRELEASE "NOT COMPATIBLE ! Missing ${OS_RELEASE_PATH} file.")
+endif()
+message(STATUS "Distribution used ${OSRELEASE}")
+
+# Include project configuration
+# ------------------------------
+project(${PROJECT_NAME} VERSION ${PROJECT_VERSION} LANGUAGES ${PROJECT_LANGUAGES})
+set(PROJECT_LIBDIR "${CMAKE_SOURCE_DIR}/libs" CACHE PATH "Subpath to libraries")
+set(PROJECT_RESOURCES "${CMAKE_SOURCE_DIR}/data" CACHE PATH "Subpath to data")
+
+set(AFB_TOKEN "" CACHE PATH "Default AFB_TOKEN")
+set(AFB_REMPORT "1234" CACHE PATH "Default AFB_TOKEN")
+
+# Check GCC minimal version
+if (gcc_minimal_version)
+message (STATUS "${Cyan}-- Check gcc_minimal_version (found gcc version ${CMAKE_C_COMPILER_VERSION}) \
+(found g++ version ${CMAKE_CXX_COMPILER_VERSION})${ColourReset}")
+if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS ${gcc_minimal_version} OR CMAKE_C_COMPILER_VERSION VERSION_LESS ${gcc_minimal_version})
+message(FATAL_ERROR "${Red}**** FATAL: Require at least gcc-${gcc_minimal_version} please set CMAKE_C[XX]_COMPILER")
+endif()
+endif(gcc_minimal_version)
+
+# Check Kernel mandatory version, will fail the configuration if required version not matched.
+if (kernel_mandatory_version)
+message (STATUS "${Cyan}-- Check kernel_mandatory_version (found kernel version ${KERNEL_VERSION})${ColourReset}")
+if (KERNEL_VERSION VERSION_LESS ${kernel_mandatory_version})
+message(FATAL_ERROR "${Red}**** FATAL: Require at least ${kernel_mandatory_version} please use a recent kernel or source your SDK environment then clean and reconfigure your CMake project.")
+endif (KERNEL_VERSION VERSION_LESS ${kernel_mandatory_version})
+endif(kernel_mandatory_version)
+
+# Check Kernel minimal version just print a Warning about missing features
+# and set a definition to be used as preprocessor condition in code to disable
+# incompatibles features.
+if (kernel_minimal_version)
+message (STATUS "${Cyan}-- Check kernel_minimal_version (found kernel version ${KERNEL_VERSION})${ColourReset}")
+if (KERNEL_VERSION VERSION_LESS ${kernel_minimal_version})
+message(WARNING "${Yellow}**** Warning: Some feature(s) require at least ${kernel_minimal_version}. Please use a recent kernel or source your SDK environment then clean and reconfigure your CMake project.${ColourReset}")
+else (KERNEL_VERSION VERSION_LESS ${kernel_minimal_version})
+add_definitions(-DKERNEL_MINIMAL_VERSION_OK)
+endif (KERNEL_VERSION VERSION_LESS ${kernel_minimal_version})
+endif(kernel_minimal_version)
+
+# Project path variables
+# ----------------------
+set(PKGOUT_DIR package CACHE PATH "Output directory for packages")
+
+# Define a default package directory
+if(PKG_PREFIX)
+ set(PROJECT_PKG_BUILD_DIR ${PKG_PREFIX}/${PKGOUT_DIR} CACHE PATH "Application contents to be packaged")
+else()
+ set(PROJECT_PKG_BUILD_DIR ${CMAKE_CURRENT_BINARY_DIR}/${PKGOUT_DIR} CACHE PATH "Application contents to be packaged")
+endif()
+
+# Paths to templates files
+set(TEMPLATE_DIR "${PKG_TEMPLATE_PREFIX}/template.d" CACHE PATH "Subpath to a directory where are stored needed files to launch on remote target to debuging purposes")
+
+string(REGEX REPLACE "^(.*)/.*$" "\\1" ENTRY_POINT "${PKG_TEMPLATE_PREFIX}")
+set(PROJECT_PKG_ENTRY_POINT ${ENTRY_POINT}/packaging CACHE PATH "Where package build files, like rpm.spec file or config.xml, are write.")
+
+set(WIDGET_ICON "${ENTRY_POINT}/wgt/${PROJECT_ICON}" CACHE PATH "Path to the widget icon")
+set(WIDGET_CONFIG_TEMPLATE ${TEMPLATE_DIR}/config.xml.in CACHE PATH "Path to widget config file template (config.xml.in)")
+
+# Path to autobuild template
+set(PROJECT_TEMPLATE_AGL_AUTOBUILD_DIR ${CMAKE_SOURCE_DIR}/conf.d/autobuild/agl CACHE PATH "Subpath to a directory where are stored autobuild script")
+set(PROJECT_TEMPLATE_LINUX_AUTOBUILD_DIR ${CMAKE_SOURCE_DIR}/conf.d/autobuild/linux CACHE PATH "Subpath to a directory where are stored autobuild script")
+
+# Archive target variables
+set(ARCHIVE_OUTPUT_ARCHIVE ${PROJECT_PKG_ENTRY_POINT}/${NPKG_PROJECT_NAME}_${PROJECT_VERSION}.orig.tar)
+set(ARCHIVE_OUTPUT ${ARCHIVE_OUTPUT_ARCHIVE}.gz)
+set(TMP_ARCHIVE_SUBMODULE ${PROJECT_PKG_ENTRY_POINT}/${NPKG_PROJECT_NAME}-sub)
+set(CMD_ARCHIVE_SUBMODULE \'git archive --verbose --prefix=${NPKG_PROJECT_NAME}-${PROJECT_VERSION}/$$path/ --format tar HEAD --output ${TMP_ARCHIVE_SUBMODULE}-$$sha1.tar\' )
+
+if(OSRELEASE MATCHES "debian" AND NOT DEFINED ENV{SDKTARGETSYSROOT} AND NOT DEFINED CMAKE_TOOLCHAIN_FILE)
+ # build deb spec file from template
+ set(PACKAGING_DEB_OUTPUT_DSC ${PROJECT_PKG_ENTRY_POINT}/${NPKG_PROJECT_NAME}.dsc)
+ set(PACKAGING_DEB_OUTPUT_INSTALL ${PROJECT_PKG_ENTRY_POINT}/debian.${NPKG_PROJECT_NAME}.install)
+ set(PACKAGING_DEB_OUTPUT_CHANGELOG ${PROJECT_PKG_ENTRY_POINT}/debian.changelog)
+ set(PACKAGING_DEB_OUTPUT_COMPAT ${PROJECT_PKG_ENTRY_POINT}/debian.compat)
+ set(PACKAGING_DEB_OUTPUT_CONTROL ${PROJECT_PKG_ENTRY_POINT}/debian.control)
+ set(PACKAGING_DEB_OUTPUT_RULES ${PROJECT_PKG_ENTRY_POINT}/debian.rules)
+endif()
+
+# Break After Binding are loaded but before they get initialised
+set(GDB_INITIAL_BREAK "personality" CACHE STRING "Initial Break Point for GDB remote")
+
+# Define some checker binaries to verify input DATA files
+# to be included in package. Schema aren't checked for now.
+# Dummy checker about JSON.
+set(LUA_CHECKER "luac" "-p" CACHE STRING "LUA compiler")
+set(XML_CHECKER "xmllint" CACHE STRING "XML linter")
+set(JSON_CHECKER "echo" CACHE STRING "JSON linter")
diff --git a/conf.d/app-templates/cmake/cmake.d/03-macros.cmake b/conf.d/app-templates/cmake/cmake.d/03-macros.cmake
new file mode 100644
index 0000000..45d1ef5
--- /dev/null
+++ b/conf.d/app-templates/cmake/cmake.d/03-macros.cmake
@@ -0,0 +1,501 @@
+###########################################################################
+# Copyright 2015, 2016, 2017 IoT.bzh
+#
+# author: Fulup Ar Foll <fulup@iot.bzh>
+# contrib: Romain Forlot <romain.forlot@iot.bzh>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+###########################################################################
+
+#--------------------------------------------------------------------------
+# WARNING:
+# Do not change this cmake template
+# Customise your preferences in "./conf.d/cmake/config.cmake"
+#--------------------------------------------------------------------------
+# CMake 3.6 imported macros to simulate list(FILTER ...) subcommand
+# -----------------------
+MACRO(PARSE_ARGUMENTS prefix arg_names option_names)
+SET(DEFAULT_ARGS)
+FOREACH(arg_name ${arg_names})
+ SET(${prefix}_${arg_name})
+ENDFOREACH(arg_name)
+FOREACH(option ${option_names})
+ SET(${prefix}_${option} FALSE)
+ENDFOREACH(option)
+
+SET(current_arg_name DEFAULT_ARGS)
+SET(current_arg_list)
+FOREACH(arg ${ARGN})
+ LIST_CONTAINS(is_arg_name ${arg} ${arg_names})
+ IF (is_arg_name)
+ SET(${prefix}_${current_arg_name} ${current_arg_list})
+ SET(current_arg_name ${arg})
+ SET(current_arg_list)
+ ELSE (is_arg_name)
+ LIST_CONTAINS(is_option ${arg} ${option_names})
+ IF (is_option)
+ SET(${prefix}_${arg} TRUE)
+ ELSE (is_option)
+ SET(current_arg_list ${current_arg_list} ${arg})
+ ENDIF (is_option)
+ ENDIF (is_arg_name)
+ENDFOREACH(arg)
+SET(${prefix}_${current_arg_name} ${current_arg_list})
+ENDMACRO(PARSE_ARGUMENTS)
+
+MACRO(LIST_CONTAINS var value)
+SET(${var})
+FOREACH (value2 ${ARGN})
+ IF (${value} STREQUAL ${value2})
+ SET(${var} TRUE)
+ ENDIF (${value} STREQUAL ${value2})
+ENDFOREACH (value2)
+ENDMACRO(LIST_CONTAINS)
+
+MACRO(LIST_FILTER)
+PARSE_ARGUMENTS(LIST_FILTER "OUTPUT_VARIABLE" "" ${ARGV})
+# Check arguments.
+LIST(LENGTH LIST_FILTER_DEFAULT_ARGS LIST_FILTER_default_length)
+IF(${LIST_FILTER_default_length} EQUAL 0)
+ MESSAGE(FATAL_ERROR "LIST_FILTER: missing list variable.")
+ENDIF(${LIST_FILTER_default_length} EQUAL 0)
+IF(${LIST_FILTER_default_length} EQUAL 1)
+ MESSAGE(FATAL_ERROR "LIST_FILTER: missing regular expression variable.")
+ENDIF(${LIST_FILTER_default_length} EQUAL 1)
+# Reset output variable
+IF(NOT LIST_FILTER_OUTPUT_VARIABLE)
+ SET(LIST_FILTER_OUTPUT_VARIABLE "LIST_FILTER_internal_output")
+ENDIF(NOT LIST_FILTER_OUTPUT_VARIABLE)
+SET(${LIST_FILTER_OUTPUT_VARIABLE})
+# Extract input list from arguments
+LIST(GET LIST_FILTER_DEFAULT_ARGS 0 LIST_FILTER_input_list)
+LIST(REMOVE_AT LIST_FILTER_DEFAULT_ARGS 0)
+FOREACH(LIST_FILTER_item ${${LIST_FILTER_input_list}})
+ FOREACH(LIST_FILTER_regexp_var ${LIST_FILTER_DEFAULT_ARGS})
+ FOREACH(LIST_FILTER_regexp ${${LIST_FILTER_regexp_var}})
+ IF(${LIST_FILTER_item} MATCHES ${LIST_FILTER_regexp})
+ LIST(APPEND ${LIST_FILTER_OUTPUT_VARIABLE} ${LIST_FILTER_item})
+ ENDIF(${LIST_FILTER_item} MATCHES ${LIST_FILTER_regexp})
+ ENDFOREACH(LIST_FILTER_regexp ${${LIST_FILTER_regexp_var}})
+ ENDFOREACH(LIST_FILTER_regexp_var)
+ENDFOREACH(LIST_FILTER_item)
+# If OUTPUT_VARIABLE is not specified, overwrite the input list.
+IF(${LIST_FILTER_OUTPUT_VARIABLE} STREQUAL "LIST_FILTER_internal_output")
+ SET(${LIST_FILTER_input_list} ${${LIST_FILTER_OUTPUT_VARIABLE}})
+ENDIF(${LIST_FILTER_OUTPUT_VARIABLE} STREQUAL "LIST_FILTER_internal_output")
+ENDMACRO(LIST_FILTER)
+
+# Generic useful macro
+# -----------------------
+macro(PROJECT_TARGET_ADD TARGET_NAME)
+ set_property(GLOBAL APPEND PROPERTY PROJECT_TARGETS ${TARGET_NAME})
+ set(TARGET_NAME ${TARGET_NAME})
+endmacro(PROJECT_TARGET_ADD)
+
+macro(PROJECT_PKGDEP_ADD PKG_NAME)
+ set_property(GLOBAL APPEND PROPERTY PROJECT_PKG_DEPS ${PKG_NAME})
+endmacro(PROJECT_PKGDEP_ADD)
+
+macro(defstr name value)
+ add_definitions(-D${name}=${value})
+endmacro(defstr)
+
+macro(configure_files_in_dir dir)
+ file(GLOB filelist "${dir}/*in")
+ foreach(file ${filelist})
+ get_filename_component(filename ${file} NAME)
+ string(REGEX REPLACE "target" "${RSYNC_TARGET}" destinationfile ${filename})
+ string(REGEX REPLACE ".in$" "" destinationfile ${destinationfile})
+ configure_file(${file} ${CMAKE_CURRENT_BINARY_DIR}/target/${destinationfile})
+ set_property(DIRECTORY APPEND PROPERTY ADDITIONAL_MAKE_CLEAN_FILES "${CMAKE_CURRENT_BINARY_DIR}/target/${destinationfile}")
+ endforeach()
+endmacro(configure_files_in_dir)
+
+macro(add_required_module PKG_CONFIG)
+ string(REGEX REPLACE "[<>]?=.*$" "" XPREFIX ${PKG_CONFIG})
+ PKG_CHECK_MODULES(${XPREFIX} REQUIRED ${PKG_CONFIG})
+
+ INCLUDE_DIRECTORIES(${${XPREFIX}_INCLUDE_DIRS})
+ list(APPEND link_libraries ${${XPREFIX}_LDFLAGS})
+ add_compile_options (${${XPREFIX}_CFLAGS})
+endmacro(add_required_module)
+
+# Create custom target dedicated for HTML5 and DATA AGL target type
+macro(add_input_files INPUT_FILES)
+ if(NOT DEFINED XML_FILES)
+ set(ext_reg "xml$")
+ set(XML_LIST ${INPUT_FILES})
+ list_filter(XML_LIST ext_reg)
+ endif()
+ if(NOT DEFINED LUA_LIST)
+ set(ext_reg "lua$")
+ set(LUA_LIST ${INPUT_FILES})
+ list_filter(LUA_LIST ext_reg)
+ endif()
+ if(NOT DEFINED JSON_FILES)
+ set(ext_reg "json$")
+ set(JSON_LIST ${INPUT_FILES})
+ list_filter(JSON_LIST ext_reg)
+ endif()
+
+ # These are v3.6 subcommand. Not used as default for now as
+ # many dev use Ubuntu 16.04 which have only 3.5 version
+ #list(FILTER XML_LIST INCLUDE REGEX "xml$")
+ #list(FILTER LUA_LIST INCLUDE REGEX "lua$")
+ #list(FILTER JSON_LIST INCLUDE REGEX "json$")
+
+ add_custom_target(${TARGET_NAME}
+ DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${TARGET_NAME}
+ )
+
+ foreach(file ${XML_LIST})
+ add_custom_command(TARGET ${TARGET_NAME}
+ PRE_BUILD
+ WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
+ COMMAND ${XML_CHECKER} ${file}
+ )
+ endforeach()
+ foreach(file ${LUA_LIST})
+ add_custom_command(TARGET ${TARGET_NAME}
+ PRE_BUILD
+ WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
+ COMMAND ${LUA_CHECKER} ${file}
+ )
+ endforeach()
+ foreach(file ${JSON_LIST})
+ add_custom_command(TARGET ${TARGET_NAME}
+ PRE_BUILD
+ WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
+ COMMAND cat ${file} | ${JSON_CHECKER}
+ )
+ endforeach()
+
+ add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${TARGET_NAME}
+ DEPENDS ${INPUT_FILES}
+ COMMAND mkdir -p ${CMAKE_CURRENT_BINARY_DIR}/${TARGET_NAME}
+ COMMAND touch ${CMAKE_CURRENT_BINARY_DIR}/${TARGET_NAME}
+ COMMAND cp -r ${INPUT_FILES} ${CMAKE_CURRENT_BINARY_DIR}/${TARGET_NAME}
+ )
+endmacro()
+
+# Set the name of the OPENAPI definition JSON file for binding v2
+macro(set_openapi_filename openapi_filename)
+ set(OPENAPI_DEF ${openapi_filename} CACHE STRING "OpenAPI JSON file name used to generate binding header file before building a binding v2 target.")
+endmacro()
+
+# Pre-packaging
+macro(project_targets_populate)
+ # Default Widget default directory
+ set(BINDIR bin)
+ set(ETCDIR etc)
+ set(LIBDIR lib)
+ set(HTTPDIR htdocs)
+ set(DATADIR data)
+ set(PACKAGE_BINDIR ${PROJECT_PKG_BUILD_DIR}/${BINDIR})
+ set(PACKAGE_ETCDIR ${PROJECT_PKG_BUILD_DIR}/${ETCDIR})
+ set(PACKAGE_LIBDIR ${PROJECT_PKG_BUILD_DIR}/${LIBDIR})
+ set(PACKAGE_HTTPDIR ${PROJECT_PKG_BUILD_DIR}/${HTTPDIR})
+ set(PACKAGE_DATADIR ${PROJECT_PKG_BUILD_DIR}/${DATADIR})
+
+ add_custom_command(OUTPUT ${PACKAGE_BINDIR} ${PACKAGE_ETCDIR} ${PACKAGE_LIBDIR} ${PACKAGE_HTTPDIR} ${PACKAGE_DATADIR}
+ COMMAND mkdir -p ${PACKAGE_BINDIR} ${PACKAGE_ETCDIR} ${PACKAGE_LIBDIR} ${PACKAGE_HTTPDIR} ${PACKAGE_DATADIR})
+ add_custom_target(populate DEPENDS ${PACKAGE_BINDIR} ${PACKAGE_ETCDIR} ${PACKAGE_LIBDIR} ${PACKAGE_HTTPDIR} ${PACKAGE_DATADIR})
+
+ INSTALL(CODE "execute_process(COMMAND make populate)")
+ INSTALL(DIRECTORY ${PROJECT_PKG_BUILD_DIR}/
+ DESTINATION ${CMAKE_INSTALL_PREFIX}/${PROJECT_NAME}
+ )
+
+ get_property(PROJECT_TARGETS GLOBAL PROPERTY PROJECT_TARGETS)
+ foreach(TARGET ${PROJECT_TARGETS})
+ get_target_property(T ${TARGET} LABELS)
+ if(T)
+ # Declaration of a custom command that will populate widget tree with the target
+ set(POPULE_PACKAGE_TARGET "project_populate_${TARGET}")
+
+ get_target_property(P ${TARGET} PREFIX)
+ get_target_property(BD ${TARGET} BINARY_DIR)
+ get_target_property(SD ${TARGET} SOURCE_DIR)
+ get_target_property(OUT ${TARGET} OUTPUT_NAME)
+
+ if(P MATCHES "NOTFOUND$")
+ if (${T} STREQUAL "BINDING")
+ set(P "lib")
+ else()
+ set(P "")
+ endif()
+ endif()
+
+ if(${T} STREQUAL "BINDING")
+ list(APPEND BINDINGS_LIST "${P}${OUT}")
+ add_custom_command(OUTPUT ${PACKAGE_LIBDIR}/${P}${OUT}.so
+ DEPENDS ${BD}/${P}${OUT}.so
+ COMMAND mkdir -p ${PACKAGE_LIBDIR}
+ COMMAND cp ${BD}/${P}${OUT}.so ${PACKAGE_LIBDIR}
+ )
+ add_custom_target(${POPULE_PACKAGE_TARGET} DEPENDS ${PACKAGE_LIBDIR}/${P}${OUT}.so)
+ add_dependencies(populate ${POPULE_PACKAGE_TARGET})
+ add_dependencies(${POPULE_PACKAGE_TARGET} ${TARGET})
+ elseif(${T} STREQUAL "BINDINGV2")
+ if (OPENAPI_DEF)
+ add_custom_command(OUTPUT ${SD}/${OPENAPI_DEF}.h
+ DEPENDS ${SD}/${OPENAPI_DEF}.json
+ COMMAND afb-genskel ${SD}/${OPENAPI_DEF}.json > ${SD}/${OPENAPI_DEF}.h
+ )
+ add_custom_target("${TARGET}_GENSKEL" DEPENDS ${SD}/${OPENAPI_DEF}.h
+ COMMENT "Generating OpenAPI header file ${OPENAPI_DEF}.h")
+ add_dependencies(${TARGET} "${TARGET}_GENSKEL")
+ else()
+ add_custom_command(OUTPUT ${SD}/${OUT}-apidef.h
+ DEPENDS ${SD}/${OUT}-apidef.json
+ COMMAND afb-genskel ${SD}/${OUT}-apidef.json > ${SD}/${OUT}-apidef.h
+ )
+ add_custom_target("${TARGET}_GENSKEL" DEPENDS ${SD}/${OUT}-apidef.h
+ COMMENT "Generating OpenAPI header file ${OUT}-apidef.h")
+ add_dependencies(${TARGET} "${TARGET}_GENSKEL")
+ endif()
+
+ add_custom_command(OUTPUT ${PACKAGE_LIBDIR}/${P}${OUT}.so
+ DEPENDS ${BD}/${P}${OUT}.so
+ COMMAND mkdir -p ${PACKAGE_LIBDIR}
+ COMMAND cp ${BD}/${P}${OUT}.so ${PACKAGE_LIBDIR}
+ )
+ add_custom_target(${POPULE_PACKAGE_TARGET} DEPENDS ${PACKAGE_LIBDIR}/${P}${OUT}.so)
+ add_dependencies(populate ${POPULE_PACKAGE_TARGET})
+ add_dependencies(${POPULE_PACKAGE_TARGET} ${TARGET})
+ elseif(${T} STREQUAL "EXECUTABLE")
+ add_custom_command(OUTPUT ${PACKAGE_BINDIR}/${P}${OUT}
+ DEPENDS ${BD}/${P}${OUT}
+ COMMAND mkdir -p ${PACKAGE_BINDIR}
+ COMMAND cp ${BD}/${P}${OUT} ${PACKAGE_BINDIR}
+ )
+ add_custom_target(${POPULE_PACKAGE_TARGET} DEPENDS ${PACKAGE_BINDIR}/${P}${OUT})
+ add_dependencies(populate ${POPULE_PACKAGE_TARGET})
+ add_dependencies(${POPULE_PACKAGE_TARGET} ${TARGET})
+ elseif(${T} STREQUAL "HTDOCS")
+ add_custom_command(OUTPUT ${PACKAGE_HTTPDIR}-xx
+ DEPENDS ${BD}/${P}${OUT}
+ COMMAND mkdir -p ${PACKAGE_HTTPDIR}
+ COMMAND touch ${PACKAGE_HTTPDIR}
+ COMMAND cp -r ${BD}/${P}${OUT}/* ${PACKAGE_HTTPDIR}
+ )
+ add_custom_target(${POPULE_PACKAGE_TARGET} DEPENDS ${PACKAGE_HTTPDIR}-xx)
+ add_dependencies(populate ${POPULE_PACKAGE_TARGET})
+ add_dependencies(${POPULE_PACKAGE_TARGET} ${TARGET})
+ elseif(${T} STREQUAL "DATA")
+ # Generate list of output files instead of just one output directory
+ get_target_property(SF ${TARGET} SOURCES)
+ foreach(file ${SF})
+ get_filename_component(JUST_FILENAME ${file} NAME)
+ list(APPEND OUTPUT_FILES ${PACKAGE_DATADIR}/${JUST_FILENAME})
+ endforeach()
+ add_custom_target(${POPULE_PACKAGE_TARGET})
+ add_custom_command(TARGET ${POPULE_PACKAGE_TARGET}
+ POST_BUILD
+ COMMAND mkdir -p ${PACKAGE_DATADIR}
+ COMMAND touch ${PACKAGE_DATADIR}
+ COMMAND cp -r ${BD}/${TARGET} ${PACKAGE_DATADIR}
+ )
+ add_dependencies(populate ${POPULE_PACKAGE_TARGET})
+ add_dependencies(${POPULE_PACKAGE_TARGET} ${TARGET})
+ endif(${T} STREQUAL "BINDING")
+ elseif(${CMAKE_BUILD_TYPE} MATCHES "[Dd][Ee][Bb][Uu][Gg]")
+ MESSAGE("${Yellow}.. Warning: ${TARGET} ignored when packaging.${ColourReset}")
+ endif()
+ endforeach()
+endmacro(project_targets_populate)
+
+macro(remote_targets_populate)
+ if (DEFINED ENV{RSYNC_TARGET})
+ set (RSYNC_TARGET $ENV{RSYNC_TARGET})
+ endif()
+ if (DEFINED ENV{RSYNC_PREFIX})
+ set (RSYNC_PREFIX $ENV{RSYNC_PREFIX})
+ endif()
+
+ set(
+ REMOTE_LAUNCH "Test on target with: ${CMAKE_CURRENT_BINARY_DIR}/target/start-on-${RSYNC_TARGET}.sh"
+ CACHE STRING "Command to start ${PROJECT_NAME} on remote target ${RSYNC_TARGET}"
+ )
+
+ if(NOT RSYNC_TARGET OR NOT RSYNC_PREFIX)
+ message ("${Yellow}.. Warning: RSYNC_TARGET RSYNC_PREFIX not defined 'make remote-target-populate' not instanciated${ColourReset}")
+ add_custom_target(remote-target-populate
+ COMMENT "${Red}*** Fatal: RSYNC_TARGET RSYNC_PREFIX environment variables required with 'make remote-target-populate'${ColourReset}"
+ COMMAND exit -1
+ )
+ else()
+ set(BINDINGS_REGEX "not_set")
+ if(DEFINED BINDINGS_LIST)
+ list(LENGTH BINDINGS_LIST BINDINGS_LIST_LENGTH)
+ if(BINDINGS_LIST_LENGTH EQUAL 1)
+ list(GET BINDINGS_LIST 0 BINDINGS_REGEX)
+ string(APPEND BINDINGS_REGEX ".so")
+ elseif(BINDINGS_LIST_LENGTH GREATER 1)
+ foreach(B IN LISTS BINDINGS_LIST)
+ STRING(APPEND BINDINGS_STR "${B}|")
+ endforeach()
+ STRING(REGEX REPLACE "^(.*)\\|$" "(\\1).so" BINDINGS_REGEX ${BINDINGS_STR})
+ endif()
+ endif()
+
+ configure_files_in_dir(${TEMPLATE_DIR})
+ configure_files_in_dir(${TEMPLATE_DIR})
+
+ add_custom_target(remote-target-populate
+ COMMAND chmod +x ${CMAKE_CURRENT_BINARY_DIR}/target/*.sh
+ COMMAND rsync -e "ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null" --archive --delete ${PROJECT_PKG_BUILD_DIR}/ ${RSYNC_TARGET}:${RSYNC_PREFIX}/${PROJECT_NAME}
+ COMMENT "${REMOTE_LAUNCH}"
+ )
+ add_dependencies(remote-target-populate populate)
+ endif()
+endmacro(remote_targets_populate)
+
+macro(wgt_package_build)
+ if(NOT EXISTS ${WIDGET_CONFIG_TEMPLATE})
+ MESSAGE(FATAL_ERROR "${Red}WARNING ! Missing mandatory files to build widget file.\nYou need a config.xml template: please specify WIDGET_CONFIG_TEMPLATE correctly.${ColourReset}")
+ endif()
+ if(NOT WIDGET_TYPE)
+ MESSAGE(FATAL_ERROR "WIDGET_TYPE must be set in your config.cmake.\neg.: set(WIDGET_TYPE application/vnd.agl.service)")
+ endif()
+ if(NOT DEFINED PROJECT_ICON)
+ if( ${WIDGET_TYPE} MATCHES "agl.native")
+ set(ICON_PATH ${PKG_APP_TEMPLATE_DIR}/wgt/icon-native.png)
+ elseif( ${WIDGET_TYPE} MATCHES "agl.service")
+ set(ICON_PATH ${PKG_APP_TEMPLATE_DIR}/wgt/icon-service.png)
+ elseif( ${WIDGET_TYPE} MATCHES "x-executable")
+ set(ICON_PATH ${PKG_APP_TEMPLATE_DIR}/wgt/icon-qml.png)
+ elseif( ${WIDGET_TYPE} MATCHES "text/html")
+ set(ICON_PATH ${PKG_APP_TEMPLATE_DIR}/wgt/icon-html5.png)
+ endif()
+ elseif(EXISTS "${CMAKE_SOURCE_DIR}/${WIDGET_ICON}")
+ set(ICON_PATH "${CMAKE_SOURCE_DIR}/${WIDGET_ICON}")
+ elseif(EXISTS "${WIDGET_ICON}")
+ set(ICON_PATH "${WIDGET_ICON}")
+ else()
+ set(ICON_PATH ${CMAKE_SOURCE_DIR}/${PROJECT_APP_TEMPLATES_DIR}/wgt/icon-default.png)
+ endif()
+
+ if(NOT WIDGET_ENTRY_POINT)
+ set(WIDGET_ENTRY_POINT lib)
+ endif()
+
+ add_custom_command(OUTPUT ${PROJECT_PKG_BUILD_DIR}/config.xml
+ COMMAND ${CMAKE_COMMAND} -DINFILE=${WIDGET_CONFIG_TEMPLATE} -DOUTFILE=${PROJECT_PKG_BUILD_DIR}/config.xml -DPROJECT_BINARY_DIR=${CMAKE_CURRENT_BINARY_DIR} -P ${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_APP_TEMPLATES_DIR}/cmake/configure_file.cmake
+ COMMAND cp ${ICON_PATH} ${PROJECT_PKG_BUILD_DIR}/${PROJECT_ICON}
+
+ )
+ add_custom_target(packaging_wgt DEPENDS ${PROJECT_PKG_BUILD_DIR}/config.xml)
+
+ # Fulup ??? copy any extra file in wgt/etc into populate package before building the widget
+ file(GLOB PROJECT_CONF_FILES "${TEMPLATE_DIR}/etc/*")
+ if(${PROJECT_CONF_FILES})
+ file(COPY "${TEMPLATE_DIR}/etc/*" DESTINATION ${PROJECT_PKG_BUILD_DIR}/etc/)
+ endif(${PROJECT_CONF_FILES})
+
+ add_custom_command(OUTPUT ${PROJECT_NAME}.wgt
+ DEPENDS ${PROJECT_TARGETS}
+ COMMAND wgtpkg-pack -f -o ${PROJECT_NAME}.wgt ${PROJECT_PKG_BUILD_DIR}
+ )
+
+ add_custom_target(widget DEPENDS ${PROJECT_NAME}.wgt)
+ add_dependencies(widget populate packaging_wgt)
+ set_property(DIRECTORY APPEND PROPERTY ADDITIONAL_MAKE_CLEAN_FILES "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}.wgt")
+
+ if(NOT RSYNC_TARGET)
+ message ("${Yellow}.. Warning: RSYNC_TARGET not defined 'make widget-target-install' not instanciated${ColourReset}")
+ add_custom_target(widget-target-install
+ COMMENT "${Red}*** Fatal: RSYNC_TARGET RSYNC_PREFIX environment variables required with 'make widget-target-install'${ColourReset}"
+ COMMAND exit -1
+ )
+ else()
+ configure_files_in_dir(${TEMPLATE_DIR})
+ add_custom_target(widget-target-install
+ DEPENDS widget
+ COMMAND chmod +x ${CMAKE_CURRENT_BINARY_DIR}/target/install-wgt-on-${RSYNC_TARGET}.sh
+ COMMAND ${CMAKE_CURRENT_BINARY_DIR}/target/install-wgt-on-${RSYNC_TARGET}.sh
+ )
+endif()
+
+ if(PACKAGE_MESSAGE)
+ add_custom_command(TARGET widget
+ POST_BUILD
+ COMMAND ${CMAKE_COMMAND} -E cmake_echo_color --cyan "++ ${PACKAGE_MESSAGE}")
+ endif()
+endmacro(wgt_package_build)
+
+macro(rpm_package_build)
+ add_custom_command(OUTPUT ${NPKG_PROJECT_NAME}.spec
+ DEPENDS ${PROJECT_TARGETS}
+ archive
+ packaging
+ COMMAND rpmbuild --define=\"%_sourcedir ${PROJECT_PKG_ENTRY_POINT}\" -ba ${PROJECT_PKG_ENTRY_POINT}/${NPKG_PROJECT_NAME}.spec
+ )
+
+ add_custom_target(rpm DEPENDS ${NPKG_PROJECT_NAME}.spec)
+ add_dependencies(rpm populate packaging)
+
+ if(PACKAGE_MESSAGE)
+ add_custom_command(TARGET rpm
+ POST_BUILD
+ COMMAND ${CMAKE_COMMAND} -E cmake_echo_color --cyan "++ ${PACKAGE_MESSAGE}")
+ endif()
+endmacro(rpm_package_build)
+
+macro(deb_package_build)
+#TODO
+endmacro(deb_package_build)
+
+macro(project_package_build)
+ if(EXISTS ${TEMPLATE_DIR})
+ rpm_package_build()
+ endif()
+
+ if(EXISTS ${TEMPLATE_DIR})
+ wgt_package_build()
+ endif()
+
+ if(EXISTS ${TEMPLATE_DIR})
+ deb_package_build()
+ endif()
+endmacro(project_package_build)
+
+macro(project_subdirs_add)
+ set (ARGSLIST ${ARGN})
+ list(LENGTH ARGSLIST ARGSNUM)
+ if(${ARGSNUM} GREATER 0)
+ file(GLOB filelist "${ARGV0}")
+ else()
+ file(GLOB filelist "*")
+ endif()
+
+ foreach(filename ${filelist})
+ if(EXISTS "${filename}/CMakeLists.txt")
+ add_subdirectory(${filename})
+ endif(EXISTS "${filename}/CMakeLists.txt")
+ endforeach()
+endmacro(project_subdirs_add)
+
+# Print developer helper message when build is done
+# -------------------------------------------------------
+macro(project_closing_msg)
+ get_property(PROJECT_TARGETS_SET GLOBAL PROPERTY PROJECT_TARGETS SET)
+ get_property(PROJECT_TARGETS GLOBAL PROPERTY PROJECT_TARGETS)
+ if(CLOSING_MESSAGE AND ${PROJECT_TARGETS_SET})
+ add_custom_target(${PROJECT_NAME}_build_done ALL
+ COMMAND ${CMAKE_COMMAND} -E cmake_echo_color --cyan "++ ${CLOSING_MESSAGE}"
+ )
+ add_dependencies(${PROJECT_NAME}_build_done
+ ${DEPENDENCIES_TARGET} ${PROJECT_TARGETS})
+ endif()
+endmacro()
diff --git a/conf.d/app-templates/cmake/cmake.d/04-extra_targets.cmake b/conf.d/app-templates/cmake/cmake.d/04-extra_targets.cmake
new file mode 100644
index 0000000..d54e128
--- /dev/null
+++ b/conf.d/app-templates/cmake/cmake.d/04-extra_targets.cmake
@@ -0,0 +1,162 @@
+###########################################################################
+# Copyright 2015, 2016, 2017 IoT.bzh
+#
+# author: Fulup Ar Foll <fulup@iot.bzh>
+# contrib: Romain Forlot <romain.forlot@iot.bzh>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+###########################################################################
+
+
+#--------------------------------------------------------------------------
+# WARNING:
+# Do not change this cmake template
+# Customise your preferences in "./conf.d/cmake/config.cmake"
+#--------------------------------------------------------------------------
+
+# Add a dummy target to enable global dependency order
+# -----------------------------------------------------
+if(EXTRA_DEPENDENCIES_ORDER)
+ set(DEPENDENCIES_TARGET ${PROJECT_NAME}_extra_dependencies)
+ add_custom_target(${DEPENDENCIES_TARGET} ALL
+ DEPENDS ${EXTRA_DEPENDENCY_ORDER}
+ )
+endif()
+
+# ----------------------------------------------------------------------------
+# Archive target
+# ----------------------------------------------------------------------------
+add_custom_command(OUTPUT ${ARCHIVE_OUTPUT}
+ DEPENDS ${PROJECT_TARGETS}
+ #Create git archive of the main project
+ COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR}\; git --git-dir=${CMAKE_CURRENT_SOURCE_DIR}/.git archive --format=tar --prefix=${NPKG_PROJECT_NAME}-${PROJECT_VERSION}/ HEAD -o ${ARCHIVE_OUTPUT_ARCHIVE}
+ #Create tmp git archive for each submodule
+ COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR}\; git --git-dir=${CMAKE_CURRENT_SOURCE_DIR}/.git submodule foreach --recursive ${CMD_ARCHIVE_SUBMODULE}
+ #Concatenate main archive and tmp submodule archive
+ COMMAND for SUBTAR in ${TMP_ARCHIVE_SUBMODULE}-*.tar\; do tar --concatenate --file=${ARCHIVE_OUTPUT_ARCHIVE} $$SUBTAR\;done
+ #Remove tmp submodule archive
+ COMMAND rm -rf ${TMP_ARCHIVE_SUBMODULE}-*.tar
+ #Compress main archive
+ COMMAND gzip --force --verbose ${ARCHIVE_OUTPUT_ARCHIVE}
+)
+add_custom_target(archive DEPENDS ${ARCHIVE_OUTPUT})
+
+# ----------------------------------------------------------------------------
+# Packaging target
+# ----------------------------------------------------------------------------
+#Format Build require package
+foreach (PKG_CONFIG ${PKG_REQUIRED_LIST})
+ #Unset TMP variable
+ unset(XPREFIX)
+ unset(XRULE)
+ unset(RPM_EXTRA_DEP)
+ unset(DEB_EXTRA_DEP)
+ #For deb package,add EOL format only for a new line
+ if(DEB_PKG_DEPS)
+ set(DEB_PKG_DEPS "${DEB_PKG_DEPS},\n")
+ endif()
+ #Get pkg-config rule on version
+ string(REGEX REPLACE "[<>]?=.*$" "" XPREFIX ${PKG_CONFIG})
+ string(REGEX MATCH "[<>]?=" XRULE ${PKG_CONFIG})
+ #Only if pkg-config has rule on version
+ if(XRULE)
+ string(REGEX REPLACE ".*[<>]?=" "" XVERS ${PKG_CONFIG})
+ set(RPM_EXTRA_DEP " ${XRULE} ${XVERS}")
+ set(DEB_EXTRA_DEP " (${XRULE} ${XVERS})")
+ endif()
+ # Format for rpm package
+ set(RPM_PKG_DEPS "${RPM_PKG_DEPS}BuildRequires: pkgconfig(${XPREFIX})${RPM_EXTRA_DEP}\n")
+
+ # Format for deb package
+ # Because the tool "dpkg" is used on the packages db to find the
+ # package providing the pkg-cong file ${XPREFIX}.pc, we need
+ # to test the OS release package type
+ # Only doable within a native environment not under SDK
+ if( OSRELEASE MATCHES "debian" AND NOT DEFINED ENV{SDKTARGETSYSROOT} AND NOT DEFINED CMAKE_TOOLCHAIN_FILE)
+ execute_process(
+ COMMAND pkg-config --print-provides ${XPREFIX}
+ OUTPUT_VARIABLE TMP_PKG_BIN
+ )
+ if(TMP_PKG_BIN)
+ string(REGEX REPLACE " *=.*$" "" PKG_BIN ${TMP_PKG_BIN})
+ set(DEB_PKG_DEPS "${DEB_PKG_DEPS} ${PKG_BIN} ${DEB_EXTRA_DEP}")
+ else()
+ message(FATAL_ERROR "-- ${Red}${XPREFIX} development files not installed. Abort.${ColourReset}")
+ endif()
+ endif()
+endforeach()
+
+if(NOT EXISTS ${TEMPLATE_DIR}/rpm-config.spec.in)
+ MESSAGE(FATAL_ERROR "${Red}Missing mandatory files: you need rpm-config.spec.in in ${TEMPLATE_DIR} folder.${ColourReset}")
+endif()
+
+# Because the tool "dpkg" is used on the packages db to find the
+# package providing the pkg-cong file ${XPREFIX}.pc, we need
+# to test the OS release package type
+# Only doable within a native environment not under SDK
+if(OSRELEASE MATCHES "debian" AND NOT DEFINED ENV{SDKTARGETSYSROOT} AND NOT DEFINED CMAKE_TOOLCHAIN_FILE)
+ add_custom_target(packaging_deb DEPENDS ${TEMPLATE_DIR}/debian.compat.in
+ ${TEMPLATE_DIR}/debian.changelog.in
+ ${TEMPLATE_DIR}/deb-config.dsc.in
+ ${TEMPLATE_DIR}/deb-config.install.in
+ ${TEMPLATE_DIR}/debian.control.in
+ ${TEMPLATE_DIR}/debian.rules.in
+ COMMAND ${CMAKE_COMMAND} -DINFILE=${TEMPLATE_DIR}/debian.compat.in -DOUTFILE=${PACKAGING_DEB_OUTPUT_COMPAT} -DPROJECT_BINARY_DIR=${CMAKE_CURRENT_BINARY_DIR} -P ${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_APP_TEMPLATES_DIR}/cmake/configure_file.cmake
+ COMMAND ${CMAKE_COMMAND} -DINFILE=${TEMPLATE_DIR}/debian.changelog.in -DOUTFILE=${PACKAGING_DEB_OUTPUT_CHANGELOG} -DPROJECT_BINARY_DIR=${CMAKE_CURRENT_BINARY_DIR} -P ${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_APP_TEMPLATES_DIR}/cmake/configure_file.cmake
+ COMMAND ${CMAKE_COMMAND} -DINFILE=${TEMPLATE_DIR}/deb-config.dsc.in -DOUTFILE=${PACKAGING_DEB_OUTPUT_DSC} -DPROJECT_BINARY_DIR=${CMAKE_CURRENT_BINARY_DIR} -P ${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_APP_TEMPLATES_DIR}/cmake/configure_file.cmake
+ COMMAND ${CMAKE_COMMAND} -DINFILE=${TEMPLATE_DIR}/deb-config.install.in -DOUTFILE=${PACKAGING_DEB_OUTPUT_INSTALL} -DPROJECT_BINARY_DIR=${CMAKE_CURRENT_BINARY_DIR} -P ${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_APP_TEMPLATES_DIR}/cmake/configure_file.cmake
+ COMMAND ${CMAKE_COMMAND} -DINFILE=${TEMPLATE_DIR}/debian.control.in -DOUTFILE=${PACKAGING_DEB_OUTPUT_CONTROL} -DPROJECT_BINARY_DIR=${CMAKE_CURRENT_BINARY_DIR} -P ${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_APP_TEMPLATES_DIR}/cmake/configure_file.cmake
+ COMMAND ${CMAKE_COMMAND} -DINFILE=${TEMPLATE_DIR}/debian.rules.in -DOUTFILE=${PACKAGING_DEB_OUTPUT_RULES} -DPROJECT_BINARY_DIR=${CMAKE_CURRENT_BINARY_DIR} -P ${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_APP_TEMPLATES_DIR}/cmake/configure_file.cmake
+ )
+endif()
+
+add_custom_target(packaging)
+set(PACKAGING_SPEC_OUTPUT ${PROJECT_PKG_ENTRY_POINT}/${NPKG_PROJECT_NAME}.spec)
+add_custom_target(packaging_rpm DEPENDS ${TEMPLATE_DIR}/rpm-config.spec.in
+ COMMAND ${CMAKE_COMMAND} -DINFILE=${TEMPLATE_DIR}/rpm-config.spec.in -DOUTFILE=${PACKAGING_SPEC_OUTPUT} -DPROJECT_BINARY_DIR=${CMAKE_CURRENT_BINARY_DIR} -P ${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_APP_TEMPLATES_DIR}/cmake/configure_file.cmake
+)
+add_dependencies(packaging packaging_rpm)
+if(TARGET packaging_wgt)
+ add_dependencies(packaging packaging_wgt)
+endif()
+if(OSRELEASE MATCHES "debian" AND NOT DEFINED ENV{SDKTARGETSYSROOT} AND NOT DEFINED CMAKE_TOOLCHAIN_FILE)
+ # Target to add dependencies indirectly to "packaging" target.
+ add_dependencies(packaging packaging_deb)
+endif()
+
+#Generate a cmake cache file usable by cmake script.
+set(CacheForScript ${CMAKE_BINARY_DIR}/CMakeCacheForScript.cmake)
+#Create a tmp cmake file.
+file(WRITE ${CacheForScript} "")
+
+get_cmake_property(Vars VARIABLES)
+foreach(Var ${Vars})
+ if(${Var})
+ #Replace unwanted char.
+ string(REPLACE "\\" "\\\\" VALUE ${${Var}})
+ string(REPLACE "\n" "\\n" VALUE ${VALUE})
+ string(REPLACE "\r" "\\n" VALUE ${VALUE})
+ string(REPLACE "\"" "\\\"" VALUE ${VALUE})
+ endif()
+ file(APPEND ${CacheForScript} "set(${Var} \"${VALUE}\")\n")
+endforeach()
+
+# ----------------------------------------------------------------------------
+# Autobuild target
+# ----------------------------------------------------------------------------
+
+add_custom_target(autobuild ALL DEPENDS ${CMAKE_SOURCE_DIR}/${PROJECT_APP_TEMPLATES_DIR}/template.d/autobuild/agl/autobuild.in
+ ${CMAKE_SOURCE_DIR}/${PROJECT_APP_TEMPLATES_DIR}/template.d/autobuild/linux/autobuild.in
+ COMMAND ${CMAKE_COMMAND} -DINFILE=${CMAKE_SOURCE_DIR}/${PROJECT_APP_TEMPLATES_DIR}/template.d/autobuild/agl/autobuild.in -DOUTFILE=${PROJECT_TEMPLATE_AGL_AUTOBUILD_DIR}/autobuild -DPROJECT_BINARY_DIR=${CMAKE_CURRENT_BINARY_DIR} -P ${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_APP_TEMPLATES_DIR}/cmake/configure_file.cmake
+ COMMAND ${CMAKE_COMMAND} -DINFILE=${CMAKE_SOURCE_DIR}/${PROJECT_APP_TEMPLATES_DIR}/template.d/autobuild/agl/autobuild.in -DOUTFILE=${PROJECT_TEMPLATE_LINUX_AUTOBUILD_DIR}/autobuild -DPROJECT_BINARY_DIR=${CMAKE_CURRENT_BINARY_DIR} -P ${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_APP_TEMPLATES_DIR}/cmake/configure_file.cmake
+)
diff --git a/conf.d/app-templates/cmake/common.cmake b/conf.d/app-templates/cmake/common.cmake
new file mode 100644
index 0000000..d0ec12e
--- /dev/null
+++ b/conf.d/app-templates/cmake/common.cmake
@@ -0,0 +1,63 @@
+###########################################################################
+# Copyright 2015, 2016, 2017 IoT.bzh
+#
+# author: Fulup Ar Foll <fulup@iot.bzh>
+# contrib: Romain Forlot <romain.forlot@iot.bzh>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+###########################################################################
+
+
+#--------------------------------------------------------------------------
+# WARNING:
+# Do not change this cmake template
+# Customise your preferences in "./conf.d/cmake/config.cmake"
+#--------------------------------------------------------------------------
+
+file(GLOB project_cmakefiles ${PROJECT_APP_TEMPLATES_DIR}/cmake/cmake.d/[0-9][0-9]-*.cmake)
+list(SORT project_cmakefiles)
+file(GLOB home_cmakefiles $ENV{HOME}/.config/app-templates/cmake.d/[0-9][0-9]-common*.cmake $ENV{HOME}/.config/app-templates/cmake.d/[0-9][0-9]-${PROJECT_NAME}*.cmake)
+list(SORT home_cmakefiles)
+file(GLOB system_cmakefiles /etc/app-templates/cmake.d/[0-9][0-9]-common*.cmake /etc/app-templates/cmake.d/[0-9][0-9]-${PROJECT_NAME}*.cmake)
+list(SORT system_cmakefiles)
+
+foreach(file ${system_cmakefiles} ${home_cmakefiles} ${project_cmakefiles})
+ message(STATUS "Include: ${file}")
+ include(${file})
+endforeach()
+
+file(GLOB project_cmakefiles ${ENTRY_POINT}/cmake/[0-9][0-9]-${OSRELEASE}*.cmake )
+list(SORT project_cmakefiles)
+if(NOT project_cmakefiles)
+ file(GLOB project_cmakefiles ${ENTRY_POINT}/cmake/[0-9][0-9]-default*.cmake)
+endif()
+
+foreach(file ${project_cmakefiles})
+ message(STATUS "Include: ${file}")
+ include(${file})
+endforeach()
+
+if(DEFINED PROJECT_SRC_DIR_PATTERN)
+ project_subdirs_add(${PROJECT_SRC_DIR_PATTERN})
+else()
+ project_subdirs_add()
+endif(DEFINED PROJECT_SRC_DIR_PATTERN)
+
+configure_files_in_dir(${PROJECT_APP_TEMPLATES_DIR}/${ENTRY_POINT}/template.d)
+configure_files_in_dir($ENV{HOME}/.config/app-templates/scripts)
+configure_files_in_dir(/etc/app-templates/scripts)
+
+project_targets_populate()
+remote_targets_populate()
+project_package_build()
+project_closing_msg()
diff --git a/conf.d/app-templates/cmake/configure_file.cmake b/conf.d/app-templates/cmake/configure_file.cmake
new file mode 100644
index 0000000..2028388
--- /dev/null
+++ b/conf.d/app-templates/cmake/configure_file.cmake
@@ -0,0 +1,2 @@
+include(${CMAKE_BINARY_DIR}/CMakeCacheForScript.cmake)
+configure_file(${INFILE} ${OUTFILE} @ONLY)
diff --git a/conf.d/app-templates/cmake/export.map b/conf.d/app-templates/cmake/export.map
new file mode 100644
index 0000000..ee2f413
--- /dev/null
+++ b/conf.d/app-templates/cmake/export.map
@@ -0,0 +1 @@
+{ global: afbBindingV*; local: *; };
diff --git a/conf.d/app-templates/docs/0-Doc-Revisions.md b/conf.d/app-templates/docs/0-Doc-Revisions.md
new file mode 100644
index 0000000..38b6e74
--- /dev/null
+++ b/conf.d/app-templates/docs/0-Doc-Revisions.md
@@ -0,0 +1,6 @@
+Document revisions
+==================
+
+| Date | Version | Designation  | Author |
+|-------------|---------|--------------------------------------|-------------------------|
+| 4 Jul 2017 | 1.0 | Initial release | R. Forlot [ Iot.bzh ] |
diff --git a/conf.d/app-templates/docs/README.md b/conf.d/app-templates/docs/README.md
new file mode 100644
index 0000000..39f9209
--- /dev/null
+++ b/conf.d/app-templates/docs/README.md
@@ -0,0 +1,22 @@
+# Introduction
+
+This document explain how to use the CMake templates files and associated
+files to ease developement of AGL application.
+
+<br>
+<br>
+<br>
+<br>
+<br>
+
+| *Meta* | *Data* |
+| -- | -- |
+| **Title** | {{ config.title }} |
+| **Author** | {{ config.author }} |
+| **Description** | {{ config.description }} |
+| **Keywords** | {{ config.keywords }} |
+| **Language** | English |
+| **Published** | Published {{ config.published }} as an electronic book |
+| **Updated** | {{ gitbook.time }} |
+| **Collection** | Open-source |
+| **Website** | [{{ config.website }}]({{ config.website }}) |
diff --git a/conf.d/app-templates/docs/SUMMARY.md b/conf.d/app-templates/docs/SUMMARY.md
new file mode 100644
index 0000000..f475678
--- /dev/null
+++ b/conf.d/app-templates/docs/SUMMARY.md
@@ -0,0 +1,10 @@
+# Summary
+
+* [Document revisions](0-Doc-Revisions.md)
+
+* [Developper guide](dev_guide/0_Abstract.md)
+ * [Quickstart](dev_guide/1_Quickstart.md)
+ * [Project architecture](dev_guide/2_project_architecture.md)
+ * [Advanced usage](dev_guide/3_advanced_usage.md)
+ * [Customization](dev_guide/4_advanced_customization.md)
+ * [Autobuild](dev_guide/5_autobuild.md)
diff --git a/conf.d/app-templates/docs/_layouts/ebook/page.html b/conf.d/app-templates/docs/_layouts/ebook/page.html
new file mode 100644
index 0000000..bf325e9
--- /dev/null
+++ b/conf.d/app-templates/docs/_layouts/ebook/page.html
@@ -0,0 +1,36 @@
+{% extends "layout.html" %}
+
+{% block title %}{{ page.title }}{% endblock %}
+{% block description %}{{ page.description }}{% endblock %}
+
+{% block style %}
+ {### Include theme css before plugins css ###}
+ {% if not fileExists(config.styles.print) %}
+ {% if options.format %}
+ <link rel="stylesheet" href="{{ (options.format + ".css")|resolveAsset }}">
+ {% else %}
+ <link rel="stylesheet" href="{{ "ebook.css"|resolveAsset }}">
+ {% endif %}
+ {% endif %}
+
+ {{ super() }}
+
+ {### Custom stylesheets for the book ###}
+
+ {% for type, style in config.styles %}
+ {% if fileExists(style) and (type == "ebook" or type == "print" or type == options.format) %}
+ <link rel="stylesheet" href="{{ style|resolveFile }}">
+ {% endif %}
+ {% endfor %}
+{% endblock %}
+
+{% block body %}
+<div class="page">
+ {% block page %}
+ <h1 class="book-chapter book-chapter-{{ page.depth }}">{{ page.title }}</h1>
+ <div class="section">
+ {{ page.content|safe }}
+ </div>
+ {% endblock %}
+</div>
+{% endblock %}
diff --git a/conf.d/app-templates/docs/_layouts/ebook/pdf_footer.html b/conf.d/app-templates/docs/_layouts/ebook/pdf_footer.html
new file mode 100644
index 0000000..679e562
--- /dev/null
+++ b/conf.d/app-templates/docs/_layouts/ebook/pdf_footer.html
@@ -0,0 +1,13 @@
+{% extends "./page.html" %}
+{% block body %}
+<div id="pdf-footer" class="pdf-footer">
+ <span class="footer-left">Version {{ config.version }}</span>
+ <span class="footer-right">{{ page.num }}</span>
+ <span class="footer-center">{{ config.published }}</span>
+</div>
+
+<!-- Allow to hide footer for some pages using hidepagefooters config in book.json -->
+<script>
+ if (({% for num in config.hidepagefooters %}{{ page.num }} == {{ num }} || {% endfor %}false)) document.getElementById('pdf-footer').style.display = 'none'
+</script>
+{% endblock %}
diff --git a/conf.d/app-templates/docs/_layouts/ebook/pdf_header.html b/conf.d/app-templates/docs/_layouts/ebook/pdf_header.html
new file mode 100644
index 0000000..ef49641
--- /dev/null
+++ b/conf.d/app-templates/docs/_layouts/ebook/pdf_header.html
@@ -0,0 +1,13 @@
+{% extends "./page.html" %}
+{% block body %}
+<div id="pdf-header" class="pdf-header">
+ <span class="header-left">IoT.Bzh</span>
+ <span class="header-right">{{ config.title }}</span>
+</div>
+
+<!-- Allow to hide header for some pages using hidepageheaders config in book.json -->
+<script>
+ if (({% for num in config.hidepageheaders %}{{ page.num }} == {{ num }} || {% endfor %}false)) document.getElementById('pdf-header').style.display = 'none'
+</script>
+
+{% endblock %} \ No newline at end of file
diff --git a/conf.d/app-templates/docs/_layouts/ebook/summary.html b/conf.d/app-templates/docs/_layouts/ebook/summary.html
new file mode 100644
index 0000000..be328a4
--- /dev/null
+++ b/conf.d/app-templates/docs/_layouts/ebook/summary.html
@@ -0,0 +1,58 @@
+{% extends "./page.html" %}
+
+{% block title %}{{ "SUMMARY"|t }}{% endblock %}
+
+{% macro articles(_articles) %}
+ {% for article in _articles %}
+ <li>
+ <span class="inner">
+ {% if article.path or article.url %}
+ {% if article.path %}
+ <a href="{{ article.path|contentURL }}{{ article.anchor }}">{{ article.title }}</a>
+ {% else %}
+ <a target="_blank" href="{{ article.url }}">{{ article.title }}</a>
+ {% endif %}
+ {% else %}
+ <span>{{ article.title }}</span>
+ {% endif %}
+ {% if 1 %}
+ <span class="page">{{ article.level }}</span>
+ {% endif %}
+ </span>
+ {% if article.articles.length > 0 %}
+ <ol>
+ {{ articles(article.articles) }}
+ </ol>
+ {% endif %}
+ </li>
+ {% endfor %}
+{% endmacro %}
+
+{% block page %}
+<div class="section toc">
+ <h1>{{ "SUMMARY"|t }}</h1>
+ <ol>
+ {% for part in summary.parts %}
+ {% if part.title %}
+ <li class="part-title">
+ <h2>{{ part.title }}</h2>
+ </li>
+ {% endif %}
+ {{ articles(part.articles) }}
+
+ {% if not loop.last %}
+ <li class="divider"></li>
+ {% endif %}
+ {% endfor %}
+
+ {% if glossary.path %}
+ <li>
+ <span class="inner">
+ <a href="{{ ('/' + glossary.path)|contentURL }}">{{ "GLOSSARY"|t }}</a>
+ </span>
+ </li>
+ {% endif %}
+ </ol>
+</div>
+{% endblock %}
+
diff --git a/conf.d/app-templates/docs/_layouts/layout.html b/conf.d/app-templates/docs/_layouts/layout.html
new file mode 100644
index 0000000..3d5aca6
--- /dev/null
+++ b/conf.d/app-templates/docs/_layouts/layout.html
@@ -0,0 +1,28 @@
+<!DOCTYPE HTML>
+<html lang="{{ config.language }}" {% if page.dir == "rtl" %}dir="rtl"{% endif %}>
+ <head>
+ <meta charset="UTF-8">
+ <meta content="text/html; charset=utf-8" http-equiv="Content-Type">
+ <title>{% block title %}{{ config.title|d("GitBook", true) }}{% endblock %}</title>
+ <meta http-equiv="X-UA-Compatible" content="IE=edge" />
+ <meta name="description" content="{% block description %}{% endblock %}">
+ <meta name="generator" content="GitBook {{ gitbook.version }}">
+ {% if config.author %}<meta name="author" content="{{ config.author }}">{% endif %}
+ {% if config.isbn %}<meta name="identifier" content="{{ config.isbn }}" scheme="ISBN">{% endif %}
+ {% block style %}
+ {% for resource in plugins.resources.css %}
+ {% if resource.url %}
+ <link rel="stylesheet" href="{{ resource.url }}">
+ {% else %}
+ <link rel="stylesheet" href="{{ resource.path|resolveAsset }}">
+ {% endif %}
+ {% endfor %}
+ {% endblock %}
+
+ {% block head %}{% endblock %}
+ </head>
+ <body>
+ {% block body %}{% endblock %}
+ {% block javascript %}{% endblock %}
+ </body>
+</html>
diff --git a/conf.d/app-templates/docs/cover.jpg b/conf.d/app-templates/docs/cover.jpg
new file mode 100644
index 0000000..4e04b0c
--- /dev/null
+++ b/conf.d/app-templates/docs/cover.jpg
Binary files differ
diff --git a/conf.d/app-templates/docs/cover_small.jpg b/conf.d/app-templates/docs/cover_small.jpg
new file mode 100644
index 0000000..315816b
--- /dev/null
+++ b/conf.d/app-templates/docs/cover_small.jpg
Binary files differ
diff --git a/conf.d/app-templates/docs/dev_guide/0_Abstract.md b/conf.d/app-templates/docs/dev_guide/0_Abstract.md
new file mode 100644
index 0000000..a04cc87
--- /dev/null
+++ b/conf.d/app-templates/docs/dev_guide/0_Abstract.md
@@ -0,0 +1,21 @@
+# Developper Guide: use AGL CMake Templates
+
+## Abstract
+
+Files used to build an application, or binding, project with the
+AGL Application Framework.
+
+To build your AGL project using these templates, you have to install
+them within your project and adjust compilation option in `config.cmake`.
+For technical reasons, you also have to specify **cmake** target in
+sub CMakeLists.txt installed. Make a globbing search to find source files
+isn't recommended now to handle project build especially in a multiuser
+project because CMake will not be aware of new or removed source files.
+
+You'll find usage samples here:
+
+- [helloworld-service](https://github.com/iotbzh/helloworld-service)
+- [low-level-can-service](https://gerrit.automotivelinux.org/gerrit/apps/low-level-can-service)
+- [high-level-viwi-service](https://github.com/iotbzh/high-level-viwi-service)
+- [audio-binding](https://github.com/iotbzh/audio-binding)
+- [unicens2-binding](https://github.com/iotbzh/unicens2-binding)
diff --git a/conf.d/app-templates/docs/dev_guide/1_Quickstart.md b/conf.d/app-templates/docs/dev_guide/1_Quickstart.md
new file mode 100644
index 0000000..9aad1ef
--- /dev/null
+++ b/conf.d/app-templates/docs/dev_guide/1_Quickstart.md
@@ -0,0 +1,81 @@
+
+# Quickstart
+
+## Initialization
+
+To use these templates files on your project just install the reference files using
+**git submodule** then use `config.cmake` file to configure your project specificities :
+
+```bash
+git submodule add https://gerrit.automotivelinux.org/gerrit/apps/app-templatesconf.d/app-templates conf.d/app-templates
+mkdir conf.d/cmake
+cp conf.d/app-templates/cmake/config.cmake.sample conf.d/cmake/config.cmake
+```
+
+Edit the copied config.cmake file to fit your needs.
+
+Now, create your top CMakeLists.txt file which include `config.cmake` file.
+
+An example is available in **app-templates** submodule that you can copy and
+use:
+
+```bash
+cp conf.d/app-templates/cmake/CMakeLists.txt CMakeLists.txt
+```
+
+## Create your CMake targets
+
+For each target part of your project, you need to use ***PROJECT_TARGET_ADD***
+to include this target to your project.
+
+Using it, make available the cmake variable ***TARGET_NAME*** until the next
+***PROJECT_TARGET_ADD*** is invoked with a new target name.
+
+So, typical usage defining a target is:
+
+```cmake
+PROJECT_TARGET_ADD(SuperExampleName) --> Adding target to your project
+
+add_executable/add_library(${TARGET_NAME}.... --> defining your target sources
+
+SET_TARGET_PROPERTIES(${TARGET_NAME} PROPERTIES.... --> fit target properties
+for macros usage
+
+INSTALL(TARGETS ${TARGET_NAME}....
+```
+
+## Targets PROPERTIES
+
+You should set properties on your targets that will be used to package your
+apps in a widget file that could be installed on an AGL system.
+
+Specify what is the type of your targets that you want to be included in the
+widget package with the property **LABELS**:
+
+Choose between:
+
+- **BINDING**: Shared library that be loaded by the AGL Application Framework
+- **BINDINGV2**: Shared library that be loaded by the AGL Application Framework.
+ This has to be accompagnied with a JSON file named like the *${OUTPUT_NAME}-apidef* of
+ the target that describe the API with OpenAPI syntax (e.g: *mybinding-apidef*).
+ Or you can choose the name by setting the *CACHE* cmake variable *OPENAPI_DEF*
+ (***CAUTION***: setting a CACHE variable is needed, or set a normal variable
+ with the *PARENT_SCOPE* option to make it visible for the parent scope
+ where the target is defined) JSON file will be used to generate header file
+ using `afb-genskel` tool.
+- **HTDOCS**: Root directory of a web app. This target has to build its
+ directory and puts its files in the ${CMAKE_CURRENT_BINARY_DIR}/${TARGET_NAME}
+- **DATA**: Resources used by your application. This target has to build its
+ directory and puts its files in the ${CMAKE_CURRENT_BINARY_DIR}/${TARGET_NAME}
+- **EXECUTABLE**: Entry point of your application executed by the AGL
+ Application Framework
+
+```cmake
+SET_TARGET_PROPERTIES(${TARGET_NAME}
+ PREFIX "afb-"
+ LABELS "BINDING"
+ OUTPUT_NAME "file_output_name")
+```
+
+> **TIP** you should use the prefix _afb-_ with your **BINDING* targets which
+> stand for **Application Framework Binding**.
diff --git a/conf.d/app-templates/docs/dev_guide/2_project_architecture.md b/conf.d/app-templates/docs/dev_guide/2_project_architecture.md
new file mode 100644
index 0000000..0cae607
--- /dev/null
+++ b/conf.d/app-templates/docs/dev_guide/2_project_architecture.md
@@ -0,0 +1,98 @@
+# Project architecture
+
+A typical project architecture would be :
+
+```tree
+<project-root-path>
+│
+├── conf.d/
+│ ├── autobuild/
+│ │ ├── agl
+│ │ │ └── autobuild
+│ │ ├── linux
+│ │ │ └── autobuild
+│ │ └── windows
+│ │ └── autobuild
+│ ├── app-templates/
+│ │ ├── README.md
+│ │ ├── autobuild/
+│ │ │ ├── agl
+│ │ │ │ └── autobuild.in
+│ │ │ ├── linux
+│ │ │ │ └── autobuild.in
+│ │ │ └── windows
+│ │ │ └── autobuild.in
+│ │ ├── cmake/
+│ │ │ ├── config.cmake.sample
+│ │ │ ├── export.map
+│ │ │ └── macros.cmake
+│ │ ├── deb/
+│ │ │ └── config.deb.in
+│ │ ├── rpm/
+│ │ │ └── config.spec.in
+│ │ └── wgt/
+│ │ ├── config.xml.in
+│ │ ├── config.xml.in.sample
+│ │ ├── icon-default.png
+│ │ ├── icon-html5.png
+│ │ ├── icon-native.png
+│ │ ├── icon-qml.png
+│ │ └── icon-service.png
+│ ├── packaging/
+│ │ ├── config.spec
+│ │ └── config.deb
+│ ├── cmake
+│ │ └── config.cmake
+│ └── wgt
+│ └── config.xml.in
+├── <libs>
+├── <target>
+│ └── <files>
+├── <target>
+│ └── <file>
+└── <target>
+ └── <files>
+```
+
+| # | Parent | Description |
+| - | -------| ----------- |
+| \<root-path\> | - | Path to your project. Hold master CMakeLists.txt and general files of your projects. |
+| conf.d | \<root-path\> | Holds needed files to build, install, debug, package an AGL app project |
+| app-templates | conf.d | Git submodule to app-templates AGL repository which provides CMake helpers macros library, and build scripts. config.cmake is a copy of config.cmake.sample configured for the projects. SHOULD NOT BE MODIFIED MANUALLY !|
+| autobuild | conf.d | Scripts generated from app-templates to build packages the same way for differents platforms.|
+| cmake | conf.d | Contains at least config.cmake file modified from the sample provided in app-templates submodule. |
+| wgt | conf.d | Contains at least config.xml.in template file modified from the sample provided in app-templates submodule for the needs of project (See config.xml.in.sample file for more details). |
+| packaging | conf.d | Contains output files used to build packages. |
+| \<libs\> | \<root-path\> | External dependencies libraries. This isn't to be used to include header file but build and link statically specifics libraries. | Library sources files. Can be a decompressed library archive file or project fork. |
+| \<target\> | \<root-path\> | A target to build, typically library, executable, etc. |
+
+## Manage app-templates submodule
+
+### Update
+
+You may have some news bug fixes or features available from app-templates
+repository that you want. To update your submodule proceed like the following:
+
+```bash
+git submodule update --remote
+git commit -s conf.d/app-templates
+```
+
+This will update the submodule to the HEAD of master branch repository. Save
+the modification by commiting it in your master git project.
+
+### Checkout submodule to a git tag
+
+You could just want to update at a specified repository tag or branch or commit
+, here are the method to do so:
+
+```bash
+cd conf.d/app-templates
+# Choose one of the following depending what you want
+git checkout <tag_name>
+git checkout --detach <branch_name>
+git checkout --detach <commit_id>
+# Then commit
+cd ../..
+git commit -s conf.d/app-templates
+```
diff --git a/conf.d/app-templates/docs/dev_guide/3_advanced_usage.md b/conf.d/app-templates/docs/dev_guide/3_advanced_usage.md
new file mode 100644
index 0000000..cf85986
--- /dev/null
+++ b/conf.d/app-templates/docs/dev_guide/3_advanced_usage.md
@@ -0,0 +1,102 @@
+# Build a widget
+
+## config.xml.in file
+
+To build a widget you need a _config.xml_ file describing what is your apps and
+how Application Framework would launch it. This repo provide a simple default
+file _config.xml.in_ that should work for simple application without
+interactions with others bindings.
+
+It is recommanded that you use the sample one which is more complete. You can
+find it at the same location under the name _config.xml.in.sample_ (stunning
+isn't it). Just copy the sample file to your _conf.d/wgt_ directory and name it
+_config.xml.in_, then edit it to fit your needs.
+
+> ***CAUTION*** : The default file is only meant to be use for a
+> simple widget app, more complicated ones which needed to export
+> their api, or ship several app in one widget need to use the provided
+> _config.xml.in.sample_ which had all new Application Framework
+> features explained and examples.
+
+## Using cmake template macros
+
+To leverage all cmake templates features, you have to specify ***properties***
+on your targets. Some macros will not works without specifying which is the
+target type.
+
+As the type is not always specified for some custom targets, like an ***HTML5***
+application, macros make the difference using ***LABELS*** property.
+
+Choose between:
+
+- **BINDING**: Shared library that be loaded by the AGL Application Framework
+- **BINDINGV2**: Shared library that be loaded by the AGL Application Framework.
+ This has to be accompagnied with a JSON file named like the *${OUTPUT_NAME}-apidef* of
+ the target that describe the API with OpenAPI syntax (e.g: *mybinding-apidef*).
+ Or you can choose the name by setting the *CACHE* cmake variable *OPENAPI_DEF*
+ (***CAUTION***: setting a CACHE variable is needed, or set a normal variable
+ with the *PARENT_SCOPE* option to make it visible for the parent scope
+ where the target is defined) JSON file will be used to generate header file
+ using `afb-genskel` tool.
+- **HTDOCS**: Root directory of a web app. This target has to build its
+ directory and puts its files in the ${CMAKE_CURRENT_BINARY_DIR}/${TARGET_NAME}
+- **DATA**: Resources used by your application. This target has to build its
+ directory and puts its files in the ${CMAKE_CURRENT_BINARY_DIR}/${TARGET_NAME}
+- **EXECUTABLE**: Entry point of your application executed by the AGL
+ Application Framework
+
+Example:
+
+```cmake
+SET_TARGET_PROPERTIES(${TARGET_NAME} PROPERTIES
+ LABELS "HTDOCS"
+ OUTPUT_NAME dist.prod
+ )
+```
+
+If your target output is not named as the ***TARGET_NAME***, you need to specify
+***OUTPUT_NAME*** property that will be used by the ***populate_widget*** macro.
+
+Use the ***populate_widget*** macro as latest statement of your target
+definition. Then at the end of your project definition you should use the macro
+***build_widget*** that make an archive from the populated widget tree using the
+`wgtpkg-pack` Application Framework tools.
+
+## Macro reference
+
+### PROJECT_TARGET_ADD
+
+Typical usage would be to add the target to your project using macro
+`PROJECT_TARGET_ADD` with the name of your target as parameter.
+
+Example:
+
+```cmake
+PROJECT_TARGET_ADD(low-can-demo)
+```
+
+> ***NOTE***: This will make available the variable `${TARGET_NAME}`
+> set with the specificied name. This variable will change at the next call
+> to this macros.
+
+### project_subdirs_add
+
+This macro will search in all subfolder any `CMakeLists.txt` file. If found then
+it will be added to your project. This could be use in an hybrid application by
+example where the binding lay in a sub directory.
+
+Usage :
+
+```cmake
+project_subdirs_add()
+```
+
+You also can specify a globbing pattern as argument to filter which folders
+will be looked for.
+
+To filter all directories that begin with a number followed by a dash the
+anything:
+
+```cmake
+project_subdirs_add("[0-9]-*")
+```
diff --git a/conf.d/app-templates/docs/dev_guide/4_advanced_customization.md b/conf.d/app-templates/docs/dev_guide/4_advanced_customization.md
new file mode 100644
index 0000000..0d8957e
--- /dev/null
+++ b/conf.d/app-templates/docs/dev_guide/4_advanced_customization.md
@@ -0,0 +1,42 @@
+# Advanced customization
+
+## Including additionnals cmake files
+
+Advanced tuning is possible using addionnals cmake files that are included
+automatically from some specifics locations. They are included in that order:
+
+- Project CMake files normaly located in _<project-root-path>/conf.d/app-templates/cmake/cmake.d_
+- Home CMake files located in _$HOME/.config/app-templates/cmake.d_
+- System CMake files located in _/etc/app-templates/cmake.d_
+
+CMake files has to be named using the following convention: `XX-common-*.cmake`
+or `XX-${PROJECT_NAME}-*.cmake`, where `XX` are numbers, `*` file name
+(ie. `99-common-my_customs.cmake`).
+
+> **NOTE** You need to specify after numbers that indicate include order, to
+which project that file applies, if it applies to all project then use keyword
+`common`.
+
+So, saying that you should be aware that every normal cmake variables used at
+project level could be overwrited by home or system located cmake files if
+variables got the same name. Exceptions are cached variables set using
+**CACHE** keyword:
+
+Example:
+
+```cmake
+set(VARIABLE_NAME 'value string random' CACHE STRING 'docstring')
+```
+
+## Include customs templated scripts
+
+As well as for additionnals cmake files you can include your own templated
+scripts that will be passed to cmake command `configure_file`.
+
+Just create your own script to the following directories:
+
+- Home location in _$HOME/.config/app-templates/scripts_
+- System location in _/etc/app-templates/scripts_
+
+Scripts only needs to use the extension `.in` to be parsed and configured by
+CMake command.
diff --git a/conf.d/app-templates/docs/dev_guide/5_autobuild.md b/conf.d/app-templates/docs/dev_guide/5_autobuild.md
new file mode 100644
index 0000000..fe1c63d
--- /dev/null
+++ b/conf.d/app-templates/docs/dev_guide/5_autobuild.md
@@ -0,0 +1,41 @@
+# Autobuild script usage
+
+## Generation
+
+To be integrated in the Yocto build workflow you have to generate `autobuild`
+scripts using _autobuild_ target.
+
+To generate those scripts proceeds:
+
+```bash
+mkdir -p build
+cd build
+cmake .. && make autobuild
+```
+
+You should see _conf.d/autobuild/agl/autobuild_ file now.
+
+## Available targets
+
+Here are the available targets available from _autobuild_ scripts:
+
+- **clean** : clean build directory from object file and targets results.
+- **distclean** : delete build directory
+- **configure** : generate project Makefile from CMakeLists.txt files.
+- **build** : compile all project targets.
+- **package** : build and output a wgt package.
+
+You can specify variables that modify the behavior of compilation using
+the following variables:
+
+- **CONFIGURE_ARGS** : Variable used at **configure** time.
+- **BUILD_ARGS** : Variable used at **build** time.
+- **DEST** : Directory where to output ***wgt*** file.
+
+Variable as to be in CMake format. (ie: BUILD_ARGS="-DC_FLAGS='-g -O2'")
+
+Usage example:
+
+```bash
+./conf.d/autobuild/wgt/autobuild package DEST=/tmp
+```
diff --git a/conf.d/app-templates/docs/dev_guide/pictures/iotbzh_logo_small.png b/conf.d/app-templates/docs/dev_guide/pictures/iotbzh_logo_small.png
new file mode 100644
index 0000000..6a98c60
--- /dev/null
+++ b/conf.d/app-templates/docs/dev_guide/pictures/iotbzh_logo_small.png
Binary files differ
diff --git a/conf.d/app-templates/docs/resources/cover.svg b/conf.d/app-templates/docs/resources/cover.svg
new file mode 100644
index 0000000..6726de7
--- /dev/null
+++ b/conf.d/app-templates/docs/resources/cover.svg
@@ -0,0 +1,210 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="1800"
+ height="2360"
+ viewBox="0 0 1800 2360"
+ id="svg2"
+ version="1.1"
+ inkscape:version="0.91 r13725"
+ sodipodi:docname="cover.svg">
+ <defs
+ id="defs4175">
+ <filter
+ style="color-interpolation-filters:sRGB"
+ inkscape:label="Drop Shadow"
+ id="filter4000">
+ <feFlood
+ result="flood"
+ flood-color="rgb(0,0,0)"
+ flood-opacity="0.475"
+ id="feFlood4002" />
+ <feComposite
+ result="composite1"
+ operator="in"
+ in="flood"
+ in2="SourceGraphic"
+ id="feComposite4004" />
+ <feGaussianBlur
+ result="blur"
+ stdDeviation="5"
+ id="feGaussianBlur4006" />
+ <feOffset
+ result="offset"
+ dy="8"
+ dx="8"
+ id="feOffset4008" />
+ <feComposite
+ result="composite2"
+ operator="over"
+ in="SourceGraphic"
+ in2="offset"
+ id="feComposite4010" />
+ </filter>
+ <filter
+ style="color-interpolation-filters:sRGB"
+ id="filter4000-6"
+ inkscape:label="Drop Shadow">
+ <feFlood
+ id="feFlood4002-4"
+ flood-opacity="0.475"
+ flood-color="rgb(0,0,0)"
+ result="flood" />
+ <feComposite
+ id="feComposite4004-6"
+ in2="SourceGraphic"
+ in="flood"
+ operator="in"
+ result="composite1" />
+ <feGaussianBlur
+ id="feGaussianBlur4006-2"
+ stdDeviation="5"
+ result="blur" />
+ <feOffset
+ id="feOffset4008-8"
+ dx="8"
+ dy="8"
+ result="offset" />
+ <feComposite
+ id="feComposite4010-9"
+ in2="offset"
+ in="SourceGraphic"
+ operator="over"
+ result="composite2" />
+ </filter>
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="0.24748737"
+ inkscape:cx="928.3577"
+ inkscape:cy="404.58117"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ inkscape:snap-text-baseline="false"
+ units="px"
+ inkscape:window-width="1920"
+ inkscape:window-height="1171"
+ inkscape:window-x="1920"
+ inkscape:window-y="0"
+ inkscape:window-maximized="1"
+ showguides="false" />
+ <metadata
+ id="metadata7">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(0,1307.6379)">
+ <g
+ id="iotbzh-logo"
+ transform="matrix(2.3917866,0,0,2.3917866,216.6324,-1946.4393)"
+ inkscape:export-filename="/home/sdx/Pictures/Logo/logo_iot_bzh_100dpi.png"
+ inkscape:export-xdpi="100.22011"
+ inkscape:export-ydpi="100.22011"
+ style="display:inline;filter:url(#filter4000-6)">
+ <text
+ sodipodi:linespacing="125%"
+ id="text3557-5-3-7-0-7-3"
+ y="519.50671"
+ x="27.886671"
+ style="font-style:normal;font-weight:normal;font-size:97.09867096px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;display:inline;fill:#000000;fill-opacity:1;stroke:none"
+ xml:space="preserve"><tspan
+ style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:97.09867096px;line-height:125%;font-family:FreeEuro;-inkscape-font-specification:'FreeEuro Bold';text-align:start;writing-mode:lr-tb;text-anchor:start"
+ y="519.50671"
+ x="27.886671"
+ id="tspan3559-5-4-1-5-0-6"
+ sodipodi:role="line">IOT</tspan></text>
+ <path
+ inkscape:connector-curvature="0"
+ d="m 286.73007,473.23356 c 28.21686,16.29102 28.75566,58.73779 0.99693,78.53831 -7.67688,5.47598 -8.77935,4.91028 -1.99529,-1.0238 17.47377,-15.28453 17.98492,-42.17775 1.08522,-57.09786 l -3.91266,-3.45435 0.72312,-3.71053 c 0.39771,-2.04076 0.5997,-5.73115 0.44885,-8.20083 -0.33876,-5.54623 0.15803,-6.49185 2.65383,-5.05094 z m -64.76568,11.40332 c 7.06047,-7.74198 18.64659,-14.16089 29.04027,-16.08874 l 6.87489,-1.27521 0.87404,2.89709 c 0.4807,1.59343 0.67439,5.2245 0.43037,8.06906 l -0.44364,5.17195 -6.13887,1.6918 c -10.91241,3.00731 -20.4022,10.85909 -25.4533,21.05979 l -2.41633,4.87984 -2.74281,-0.41238 c -5.14252,-0.77316 -12.72985,-3.97645 -12.79123,-5.40033 -0.092,-2.13451 8.34659,-15.74625 12.76661,-20.59287 z m 33.20546,36.39493 c -28.21687,16.29101 -65.24624,-4.46574 -68.51461,-38.40577 -0.9039,-9.38637 0.13723,-10.0583 1.88428,-1.21608 4.49989,22.77499 27.53453,36.66428 48.90556,29.48876 l 4.94788,-1.66128 2.85184,2.48149 c 1.56852,1.36481 4.66349,3.38493 6.87772,4.48914 4.97257,2.47973 5.54308,3.38282 3.04733,4.82374 z m 22.50729,-61.79039 c 3.17451,9.98553 2.94038,23.22889 -0.58688,33.19399 l -2.33309,6.59143 -2.94597,-0.69161 c -1.6203,-0.38041 -4.86173,-2.02821 -7.2032,-3.6618 l -4.25721,-2.97018 1.60429,-6.16234 c 2.85178,-10.95404 0.79685,-23.09833 -5.51167,-32.57307 l -3.01788,-4.53253 1.72854,-2.16916 c 3.24083,-4.06698 9.80863,-9.03614 11.07242,-8.37738 1.89457,0.98756 9.46336,15.1015 11.45065,21.35265 z m -48.80223,10.31437 c 0,-32.58201 36.49058,-54.27201 67.51771,-40.1325 8.58077,3.9104 8.6421,5.148 0.11108,2.23988 -21.97368,-7.49048 -45.51946,5.51348 -49.99082,27.6091 l -1.03521,5.11561 -3.57498,1.22902 c -1.96621,0.67596 -5.26316,2.34622 -7.32655,3.71171 -4.63379,3.06649 -5.70115,3.10904 -5.70115,0.22718 z m 42.25842,50.3871 c -10.23499,-2.24356 -21.58699,-9.06801 -28.45341,-17.10525 l -4.5418,-5.31622 2.07194,-2.20549 c 1.13957,-1.21302 4.18733,-3.19628 6.77282,-4.40726 l 4.70085,-2.20176 4.53458,4.47053 c 8.06061,7.94674 19.60535,12.23927 30.96496,11.51329 l 5.43422,-0.34731 1.01427,2.58154 c 1.90169,4.84014 2.92124,13.01261 1.71883,13.77769 -1.80254,1.14695 -17.80995,0.64475 -24.21726,-0.75976 z"
+ style="display:inline;fill:#5a2ca0"
+ id="path3415-4-2-2-5-0-3-7-4-4-1-5" />
+ <text
+ sodipodi:linespacing="125%"
+ id="text3557-5-3-7-46-7-3-7"
+ y="519.50671"
+ x="317.95816"
+ style="font-style:normal;font-weight:normal;font-size:97.09867096px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;display:inline;fill:#000000;fill-opacity:1;stroke:none"
+ xml:space="preserve"><tspan
+ style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:97.09867096px;line-height:125%;font-family:FreeEuro;-inkscape-font-specification:'FreeEuro Bold';text-align:start;writing-mode:lr-tb;text-anchor:start"
+ y="519.50671"
+ x="317.95816"
+ id="tspan3559-5-4-1-90-0-2-9"
+ sodipodi:role="line">BZH</tspan></text>
+ </g>
+ <flowRoot
+ xml:space="preserve"
+ id="root-title"
+ transform="matrix(2.3469382,0,0,2.3469382,464.13874,-1200)"><flowRegion
+ id="flowRegion4303"><rect
+ id="rect4305"
+ width="679.99994"
+ height="141.42853"
+ x="-154.28572"
+ y="400"/>
+ </flowRegion>
+ <flowPara
+ id="title"
+ style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:87.5px;line-height:125%;font-family:Verdana;-inkscape-font-specification:'Verdana, Bold';text-align:center;writing-mode:lr-tb;text-anchor:middle;fill:#000000;fill-opacity:1">{title}</flowPara></flowRoot> <flowRoot
+ xml:space="preserve"
+ id="root-subtitle"
+ transform="matrix(1.8523279,0,0,1.8523279,553.97647,-380)"><flowRegion
+ id="flowRegion4303-6"><rect
+ id="rect4305-0"
+ width="852.32806"
+ height="154.51677"
+ x="-239.3591"
+ y="290"/>
+ </flowRegion>
+ <flowPara
+ id="subtitle"
+ style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:62.5px;line-height:125%;font-family:Verdana;-inkscape-font-specification:'Verdana, Bold';text-align:center;writing-mode:lr-tb;text-anchor:middle;fill:#000000;fill-opacity:1">{subtitle}</flowPara>
+ </flowRoot>
+ <flowRoot
+ id="root-version"
+ xml:space="preserve"
+ transform="translate(-2.0185547,164)"><flowRegion
+ id="flowRegion4169"><rect
+ y="501.68909"
+ x="343.32947"
+ height="99.591171"
+ width="1117.3768"
+ id="rect4171" /></flowRegion><flowPara
+ style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:50px;line-height:125%;font-family:Verdana;-inkscape-font-specification:'Verdana, Bold';text-align:center;writing-mode:lr-tb;text-anchor:middle;fill:#000000;fill-opacity:1"
+ id="version">{version}</flowPara></flowRoot> <flowRoot
+ transform="translate(-2.0185547,238)"
+ xml:space="preserve"
+ id="root-date"><flowRegion
+ id="flowRegion4170"><rect
+ id="rect4172"
+ width="1117.3768"
+ height="99.591171"
+ x="343.32947"
+ y="501.68909" /></flowRegion><flowPara
+ id="date"
+ style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:50px;line-height:125%;font-family:Verdana;-inkscape-font-specification:'Verdana, Bold';text-align:center;writing-mode:lr-tb;text-anchor:middle;fill:#000000;fill-opacity:1">{date}</flowPara></flowRoot> </g>
+</svg>
diff --git a/conf.d/app-templates/docs/resources/ebook.css b/conf.d/app-templates/docs/resources/ebook.css
new file mode 100644
index 0000000..39f126c
--- /dev/null
+++ b/conf.d/app-templates/docs/resources/ebook.css
@@ -0,0 +1,386 @@
+/* IoT.Bzh theaming */
+
+h1 {
+ color: #330066;
+ border-bottom: 2px solid #330066;
+}
+
+h2 {
+ color: #330066;
+}
+
+h3 {
+ color: #330066;
+}
+
+h4 {
+ color: #330066;
+}
+
+
+/* GENERAL ELEMENTS */
+
+/* clear both */
+
+.clear {
+ clear: both;
+}
+
+.section> :last-child {
+ margin-bottom: 0 !important;
+}
+
+.section> :first-child {
+ margin-top: 0 !important;
+}
+
+
+/* SPECIAL ELEMENTS */
+
+
+/* page break always after element on pdf/print definition */
+
+div.pagebreak {
+ page-break-after: always;
+}
+
+
+/* no page break inside element on pdf/print definition */
+
+div.nopb {
+ page-break-inside: avoid !important;
+ margin: 4px 0 4px 0;
+}
+
+
+/* note blocks */
+
+div.note {
+ background: #FCF8E3 none repeat scroll 0% 0%;
+ color: #8A6D3B;
+ padding: 15px;
+ margin-bottom: 10px;
+ border-bottom: 5px solid #DDD;
+ border-color: #FAEBCC;
+ page-break-inside: avoid;
+}
+
+div.note p {
+ padding-bottom: 0;
+ margin-bottom: 0;
+}
+
+
+/* images, figures and captions */
+
+p img {
+ /* center all images */
+ display: block;
+ margin: 0 auto;
+ padding: 10px 0;
+}
+
+figure {
+ margin: 1.0em 0px;
+ padding: 10px 0;
+ text-align: center;
+ page-break-inside: avoid;
+ display: block;
+}
+
+figure img {
+ display: block;
+ margin: 0 auto;
+}
+
+figcaption {
+ clear: left;
+ margin: 1.0em 0 0 0;
+ text-align: center;
+ font-style: italic;
+ line-height: 1.5em;
+ font-size: 80%;
+ color: #666;
+ display: block;
+}
+
+.page .section p img {
+ margin-top: 10px;
+}
+
+
+/* ul, ol list margin fix */
+
+.page .section ol,
+.page .section ul {
+ margin-bottom: 10px;
+}
+
+
+/* blockquotes */
+
+.page .section blockquote {
+ margin: 0 0 0 5%;
+ font-style: italic;
+}
+
+
+/* PAGE SPECIFIC */
+
+
+/* set summary page to right side of the paper */
+
+.page .toc h1 {
+ page-break-before: right;
+}
+
+.page .section.toc {
+ page-break-inside: always;
+}
+
+/* table headers */
+
+div#README\.md table {
+ margin-top: 30px;
+ font-size: 95%;
+}
+
+div#README\.md table thead {
+ display: none;
+}
+
+
+
+/* CITATION AND IMAGES */
+
+
+/* math image styles */
+
+.page .section p img.svg,
+.page .section p img.png {
+ margin-top: 0px;
+ margin-bottom: -2px;
+}
+
+.page .section p img.math {
+ vertical-align: middle;
+ height: auto;
+ width: auto;
+ margin-top: -4px;
+ max-height: 15px;
+}
+
+.page .section p img.math.line1 {
+ margin-top: -7px;
+ max-height: 19px;
+}
+
+.page .section p img.math.line2 {
+ margin-top: -1px;
+ max-height: 30px;
+}
+
+
+/* credits page */
+
+.page .section ul.pictures {
+ margin-left: -30px;
+}
+
+.page .section ul.pictures li {
+ list-style: outside none none;
+}
+
+.page .section ul.pictures li a {
+ float: left;
+}
+
+.page .section ul.pictures li span {
+ display: block;
+ margin-left: 100px;
+}
+
+
+
+/* sub and super script */
+
+.page .section sub {
+ font-size: 80%;
+ margin-left: 1px;
+}
+
+
+/* citations and references */
+
+.page .section sup {
+ margin-left: -1px;
+ margin-right: 2px;
+ font-size: 80%;
+}
+
+.page .section sup:before {
+ content: " ";
+}
+
+.page .section ul.citations,
+.page .section ul.references {
+ margin-left: -30px;
+}
+
+
+.page .section ul.citations li:nth-child(1) {
+ margin-top: 20px;
+ padding-top: 20px;
+ border-top: 1px solid #BBB;
+}
+
+.page .section ul.citations li,
+.page .section ul.references li {
+ list-style: outside none none;
+}
+
+.page .section ul.citations li {
+ font-size: 80%;
+}
+
+.page .section ul.citations li>span:nth-child(1),
+.page .section ul.references li>span:nth-child(1) {
+ display: block;
+ float: left;
+ text-align: left;
+ width: 70px
+}
+
+.page .section ul.citations li>span:nth-child(1) {
+ width: 50px
+}
+
+.page .section ul.references li div {
+ margin-left: 70px;
+}
+
+.page .section ul.citations li div {
+ margin-left: 50px;
+}
+
+.page .section a[href="#"],
+.page .section a[href="#"]:link,
+.page .section a[href="#"]:visited,
+.page .section a[href="#"]:hover,
+.page .section a[href="#"]:focus {
+ text-decoration: none;
+ color: inherit;
+ cursor: text;
+ font-style: italic;
+}
+
+
+/* self referential footnotes */
+
+.page .section div[type="selfref"] a[href="#"],
+.page .section div[type="selfref"] a[href="#"]:link,
+.page .section div[type="selfref"] a[href="#"]:visited,
+.page .section div[type="selfref"] a[href="#"]:hover,
+.page .section div[type="selfref"] a[href="#"]:focus {
+ font-style: normal;
+}
+
+.page .section div[type="selfref"] span:nth-child(1) {
+ display: none;
+}
+
+
+/* page break always after element on pdf/print definition */
+
+div.page-break {
+ page-break-inside: always;
+}
+
+div.page-break:before {
+ content: ' ';
+}
+
+
+/* no page break inside element on pdf/print definition */
+
+div.nopb {
+ page-break-inside: avoid;
+}
+
+/* justify text */
+p {
+ text-align: justify;
+}
+
+/* page header and footer */
+
+.pdf-footer,
+.pdf-header {
+ margin-top: 20px;
+ color: #aaa;
+}
+
+.pdf-header .header-left {
+ float: left;
+ margin-left: 2em;
+ margin-right: auto;
+}
+
+.pdf-header .header-right {
+ display: table;
+ margin-left: auto;
+ margin-right: 2em;
+}
+
+.pdf-footer .sub {
+ padding-top: 8px;
+ font-size: 70%;
+}
+
+.pdf-header .sub {
+ padding-top: 2px;
+ font-size: 70%;
+}
+
+.pdf-footer {
+ padding-top: 10px;
+ border-top: 1px solid #eee;
+}
+
+.pdf-footer .footer-left {
+ float: left;
+ margin-left: 2em;
+ margin-right: auto;
+}
+
+.pdf-footer .footer-center {
+ display: table;
+ margin-left: auto;
+ margin-right: auto;
+}
+
+.pdf-footer .footer-right {
+ float: right;
+ margin-left: auto;
+ margin-right: 2em;
+}
+
+.pdf-header {
+ padding-bottom: 10px;
+ border-bottom: 1px solid #eee;
+}
+
+.pdf-header .header-pages-count {
+ float: right;
+ text-align: right;
+}
+
+.pdf-header .header-pages-count a,
+.pdf-header .header-pages-count a:visited,
+.pdf-header .header-pages-count a:active,
+.pdf-header .header-pages-count a:focus,
+.pdf-header .header-pages-count a:link {
+ text-decoration: none;
+ color: #aaa;
+ cursor: text;
+}
diff --git a/conf.d/app-templates/docs/resources/make_cover.sh b/conf.d/app-templates/docs/resources/make_cover.sh
new file mode 100755
index 0000000..1026ecb
--- /dev/null
+++ b/conf.d/app-templates/docs/resources/make_cover.sh
@@ -0,0 +1,27 @@
+#!/bin/bash
+DOCS_DIR=$(cd $(dirname $0)/.. && pwd)
+BOOKFILE=$DOCS_DIR/../book.json
+
+TITLE=$(grep '"title":' $BOOKFILE | cut -d'"' -f 4)
+SUBTITLE=$(grep '"subtitle":' $BOOKFILE | cut -d'"' -f 4)
+VERSION="Version $(grep '"version":' $BOOKFILE | cut -d'"' -f 4)"
+DATE=$(grep '"published":' $BOOKFILE | cut -d'"' -f 4)
+
+[ -z "$TITLE" ] && { echo "Error TITLE not set!" ; exit 1; }
+[ -z "$VERSION" ] && { echo "Error VERSION not set!" ; exit 1; }
+[ -z "$DATE" ] && { echo "Error DATE not set!" ; exit 1; }
+
+
+cat $(dirname $0)/cover.svg | sed -e "s/{title}/$TITLE/g" \
+ -e "s/font-size:87.5px/font-size:54px/g" \
+ -e "s/{subtitle}/$SUBTITLE/g" \
+ -e "s/font-size:62.5px/font-size:40px/g" \
+ -e "s/{version}/$VERSION/g" \
+ -e "s/{date}/$DATE/g" \
+ > /tmp/cover.svg
+
+# use imagemagick convert tool (cover size must be 1800x2360)
+convert -resize "1600x2160!" -border 100 -bordercolor white -background white \
+ -flatten -quality 100 /tmp/cover.svg $DOCS_DIR/cover.jpg
+
+convert -resize "200x262!" $DOCS_DIR/cover.jpg $DOCS_DIR/cover_small.jpg
diff --git a/conf.d/app-templates/gendocs.sh b/conf.d/app-templates/gendocs.sh
new file mode 100755
index 0000000..ea9400a
--- /dev/null
+++ b/conf.d/app-templates/gendocs.sh
@@ -0,0 +1,79 @@
+#!/bin/bash
+
+OUTFILENAME="Gitbook-Iotbzh-template"
+
+SCRIPT=$(basename $BASH_SOURCE)
+
+function usage() {
+ cat <<EOF >&2
+Usage: $SCRIPT [options] [pdf|serve|doxygen]
+
+Options:
+ --debug
+ enable debug when generating pdf or html documentation
+ -d|--dry
+ dry run
+ -h|--help
+ get this help
+
+Example:
+ $SCRIPT pdf
+
+EOF
+ exit 1
+}
+
+function info() {
+ echo "$@" >&2
+}
+
+#default values
+DEBUG_FLAG=""
+DRY=""
+DO_ACTION=""
+OUT_DIR=./build
+
+[[ $? != 0 ]] && usage
+while [ $# -gt 0 ]; do
+ case "$1" in
+ --debug) DEBUG_FLAG="--log=debug --debug";;
+ -d|--dry) DRY=echo;;
+ -h|--help) usage;;
+ pdf | serve | doxygen) DO_ACTION=$1;;
+ --) break;;
+ esac
+ shift
+done
+
+cd $(dirname $0)
+ROOTDIR=`pwd -P`
+
+# Create out dir if needed
+[ -d $OUT_DIR ] || mkdir -p $OUT_DIR
+
+if [ "$DO_ACTION" = "pdf" -o "$DO_ACTION" = "serve" ]; then
+ GITBOOK=`which gitbook`
+ [ "$?" = "1" ] && { echo "You must install gitbook first, using: sudo npm install -g gitbook-cli"; exit 1; }
+
+ EBCONV=`which ebook-convert`
+ [ "$?" = "1" ] && { echo "You must install calibre first, using: 'sudo apt install calibre' or refer to https://calibre-ebook.com/download"; exit 1; }
+
+ if [ "$DO_ACTION" = "pdf" ]; then
+
+ # Update cover when book.json has been changed
+ [[ $ROOTDIR/book.json -nt $ROOTDIR/docs/cover.jpg ]] && { echo "Update cover files"; $ROOTDIR/docs/resources/make_cover.sh || exit 1; }
+
+ OUTFILE=$OUT_DIR/$OUTFILENAME.pdf
+ $DRY $GITBOOK pdf $ROOTDIR $OUTFILE $DEBUG_FLAG
+ [ "$?" = "0" ] && echo "PDF has been successfully generated in $OUTFILE"
+ else
+ $DRY $GITBOOK serve $DEBUG_FLAG
+ fi
+
+elif [ "$DO_ACTION" = "doxygen" ]; then
+ $DRY cd $OUT_DIR && cmake .. && make doxygen $ROOTDIR/Doxyfile
+
+else
+ echo "Unknown action !"
+ usage
+fi
diff --git a/conf.d/app-templates/samples.d/CMakeLists.txt.sample b/conf.d/app-templates/samples.d/CMakeLists.txt.sample
new file mode 100644
index 0000000..b485097
--- /dev/null
+++ b/conf.d/app-templates/samples.d/CMakeLists.txt.sample
@@ -0,0 +1,21 @@
+###########################################################################
+# Copyright 2015, 2016, 2017 IoT.bzh
+#
+# author: Romain Forlot <romain.forlot@iot.bzh>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+###########################################################################
+
+CMAKE_MINIMUM_REQUIRED(VERSION 3.3)
+
+include(${CMAKE_CURRENT_SOURCE_DIR}/conf.d/cmake/config.cmake)
diff --git a/conf.d/app-templates/samples.d/config.cmake.sample b/conf.d/app-templates/samples.d/config.cmake.sample
new file mode 100644
index 0000000..393c893
--- /dev/null
+++ b/conf.d/app-templates/samples.d/config.cmake.sample
@@ -0,0 +1,203 @@
+###########################################################################
+# Copyright 2015, 2016, 2017 IoT.bzh
+#
+# author: Fulup Ar Foll <fulup@iot.bzh>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+###########################################################################
+
+# Project Info
+# ------------------
+set(PROJECT_NAME example)
+set(PROJECT_VERSION "0.0")
+set(PROJECT_PRETTY_NAME "Example")
+set(PROJECT_DESCRIPTION "AGL application example")
+set(PROJECT_URL "https://gerrit.automotivelinux.org/gerrit/apps/app-templates")
+set(PROJECT_ICON "icon.png")
+set(PROJECT_AUTHOR "Last Name, First Name")
+set(PROJECT_AUTHOR_MAIL "example.man@bigouden.bzh")
+set(PROJECT_LICENSE "APL2.0")
+set(PROJECT_LANGUAGES,"C")
+
+# Where are stored default templates files from submodule or subtree app-templates in your project tree
+# relative to the root project directory
+set(PROJECT_APP_TEMPLATES_DIR "conf.d/app-templates")
+
+# Where are stored your external libraries for your project. This is 3rd party library that you don't maintain
+# but used and must be built and linked.
+# set(PROJECT_LIBDIR "libs")
+
+# Where are stored data for your application. Pictures, static resources must be placed in that folder.
+# set(PROJECT_RESOURCES "data")
+
+# Which directories inspect to find CMakeLists.txt target files
+# set(PROJECT_SRC_DIR_PATTERN "*")
+
+# Compilation Mode (DEBUG, RELEASE)
+# ----------------------------------
+set(CMAKE_BUILD_TYPE "DEBUG")
+
+# Kernel selection if needed. You can choose between a
+# mandatory version to impose a minimal version.
+# Or check Kernel minimal version and just print a Warning
+# about missing features and define a preprocessor variable
+# to be used as preprocessor condition in code to disable
+# incompatibles features. Preprocessor define is named
+# KERNEL_MINIMAL_VERSION_OK.
+#
+# NOTE*** FOR NOW IT CHECKS KERNEL Yocto environment and
+# Yocto SDK Kernel version.
+# -----------------------------------------------
+#set (kernel_mandatory_version 4.8)
+#set (kernel_minimal_version 4.8)
+
+# Compiler selection if needed. Impose a minimal version.
+# -----------------------------------------------
+set (gcc_minimal_version 4.9)
+
+# PKG_CONFIG required packages
+# -----------------------------
+set (PKG_REQUIRED_LIST
+ json-c
+ libsystemd>=222
+ afb-daemon
+ libmicrohttpd>=0.9.55
+)
+
+# Customize link option
+# -----------------------------
+#list(APPEND link_libraries -an-option)
+
+# Compilation options definition
+# Use CMake generator expressions to specify only for a specific language
+# Values are prefilled with default options that is currently used.
+# Either separate options with ";", or each options must be quoted separately
+# DO NOT PUT ALL OPTION QUOTED AT ONCE , COMPILATION COULD FAILED !
+# ----------------------------------------------------------------------------
+#set(COMPILE_OPTIONS
+# -Wall
+# -Wextra
+# -Wconversion
+# -Wno-unused-parameter
+# -Wno-sign-compare
+# -Wno-sign-conversion
+# -Werror=maybe-uninitialized
+# -Werror=implicit-function-declaration
+# -ffunction-sections
+# -fdata-sections
+# -fPIC
+# CACHE STRING "Compilation flags")
+#set(C_COMPILE_OPTIONS "" CACHE STRING "Compilation flags for C language.")
+#set(CXX_COMPILE_OPTIONS "" CACHE STRING "Compilation flags for C++ language.")
+#set(PROFILING_COMPILE_OPTIONS
+# -g
+# -O0
+# -pg
+# -Wp,-U_FORTIFY_SOURCE
+# CACHE STRING "Compilation flags for PROFILING build type.")
+#set(DEBUG_COMPILE_OPTIONS
+# -g
+# -ggdb
+# -Wp,-U_FORTIFY_SOURCE
+# CACHE STRING "Compilation flags for DEBUG build type.")
+#set(CCOV_COMPILE_OPTIONS
+# -g
+# -O2
+# --coverage
+# CACHE STRING "Compilation flags for CCOV build type.")
+#set(RELEASE_COMPILE_OPTIONS
+# -g
+# -O2
+# CACHE STRING "Compilation flags for RELEASE build type.")
+
+# Print a helper message when every thing is finished
+# ----------------------------------------------------
+#set(CLOSING_MESSAGE "")
+#set(PACKAGE_MESSAGE "Install widget file using in the target : afm-util install ${PROJECT_NAME}.wgt")
+
+# Prefix path where will be installed the files
+# Default: /usr/local (need root permission to write in)
+# ------------------------------------------------------
+#set(CMAKE_INSTALL_PREFIX $ENV{HOME}/opt)
+
+# (BUG!!!) as PKG_CONFIG_PATH does not work [should be an env variable]
+# ---------------------------------------------------------------------
+set(CMAKE_PREFIX_PATH ${CMAKE_INSTALL_PREFIX}/lib64/pkgconfig ${CMAKE_INSTALL_PREFIX}/lib/pkgconfig)
+set(LD_LIBRARY_PATH ${CMAKE_INSTALL_PREFIX}/lib64 ${CMAKE_INSTALL_PREFIX}/lib)
+
+# Optional location for config.xml.in
+# -----------------------------------
+#set(WIDGET_ICON conf.d/wgt/${PROJECT_ICON} CACHE PATH "Path to the widget icon")
+#set(WIDGET_CONFIG_TEMPLATE ${CMAKE_CURRENT_SOURCE_DIR}/conf.d/wgt/config.xml.in CACHE PATH "Path to widget config file template (config.xml.in)")
+
+# Mandatory widget Mimetype specification of the main unit
+# --------------------------------------------------------------------------
+# Choose between :
+#- text/html : HTML application,
+# content.src designates the home page of the application
+#
+#- application/vnd.agl.native : AGL compatible native,
+# content.src designates the relative path of the binary.
+#
+# - application/vnd.agl.service: AGL service, content.src is not used.
+#
+#- ***application/x-executable***: Native application,
+# content.src designates the relative path of the binary.
+# For such application, only security setup is made.
+#
+set(WIDGET_TYPE MimeType_Not_Set)
+
+# Mandatory Widget entry point file of the main unit
+# --------------------------------------------------------------
+# This is the file that will be executed, loaded,
+# at launch time by the application framework.
+#
+set(WIDGET_ENTRY_POINT EntryPoint_Path_Not_Set)
+
+# Optional dependencies order
+# ---------------------------
+#set(EXTRA_DEPENDENCIES_ORDER)
+
+# Optional Extra global include path
+# -----------------------------------
+#set(EXTRA_INCLUDE_DIRS)
+
+# Optional extra libraries
+# -------------------------
+#set(EXTRA_LINK_LIBRARIES)
+
+# Optional force binding Linking flag
+# ------------------------------------
+# set(BINDINGS_LINK_FLAG LinkOptions )
+
+# Optional force package prefix generation, like widget
+# -----------------------------------------------------
+# set(PKG_PREFIX DestinationPath)
+
+# Optional Application Framework security token
+# and port use for remote debugging.
+#------------------------------------------------------------
+#set(AFB_TOKEN "" CACHE PATH "Default AFB_TOKEN")
+#set(AFB_REMPORT "1234" CACHE PATH "Default AFB_TOKEN")
+
+# Optional schema validator about now only XML, LUA and JSON
+# are supported
+#------------------------------------------------------------
+#set(LUA_CHECKER "luac -o /dev/null" CACHE STRING "LUA compiler")
+#set(XML_CHECKER "xmllint" CACHE STRING "XML linter")
+#set(JSON_CHECKER "json_verify" CACHE STRING "JSON linter")
+
+# This include is mandatory and MUST happens at the end
+# of this file, else you expose you to unexpected behavior
+# -----------------------------------------------------------
+include(${PROJECT_APP_TEMPLATES_DIR}/cmake/common.cmake)
diff --git a/conf.d/app-templates/samples.d/config.xml.in.sample b/conf.d/app-templates/samples.d/config.xml.in.sample
new file mode 100644
index 0000000..b93f3d9
--- /dev/null
+++ b/conf.d/app-templates/samples.d/config.xml.in.sample
@@ -0,0 +1,142 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<widget xmlns="http://www.w3.org/ns/widgets" id="@PROJECT_NAME@" version="@PROJECT_VERSION@">
+ <name>@PROJECT_NAME@</name>
+ <icon src="@PROJECT_ICON@"/>
+ <content src="@WIDGET_ENTRY_POINT@" type="@WIDGET_TYPE@"/>
+ <description>@PROJECT_DESCRIPTION@</description>
+ <author>@PROJECT_AUTHOR@ &lt;@PROJECT_AUTHOR_MAIL@&gt;</author>
+ <license>@PROJECT_LICENSE@</license>
+
+<!-- Feature : required-api ------------------------------------------------------
+#### param name="#target" OPTIONAL
+
+Declares the name of the unit requiring the listed apis.
+Only one instance of the param "#target" is allowed.
+When there is not instance of this param, it behave as if
+the target main was specified.
+
+#### param name=[required api name]
+
+The name is the name of the required API.
+The value describes how to connect to the required api.
+It is either:
+
+ - local: The binding is a local shared object.
+ In that case, the name is the relative path of the
+ shared object to be loaded.
+ - auto: The framework set automatically the kind of
+ the connection to the API
+ - ws: The framework connect using internal websockets
+ - dbus: The framework connect using internal dbus
+ - link: The framework connect in memory by dinamically linking
+
+Example:
+ <feature name="urn:AGL:widget:required-api">
+ <param name="#target" value="low-can" />
+ <param name="<a-required-api>" value="auto" />
+ <param name="<another-required-api>" value="auto" />
+ </feature>
+--------------------------------------------------------------------------------->
+
+<!-- Feature : required-permission ------------------------------------------
+
+List of the permissions required by the unit.
+Each required permission must be explicited using a <param> entry.
+
+#### param name="#target" OPTIONAL
+
+Declares the name of the unit requiring the listed permissions.
+Only one instance of the param "#target" is allowed.
+When there is not instance of this param, it behave as if
+the target main was specified.
+
+#### param name=[required permission name]
+
+The value is either:
+- required: the permission is mandatorily needed except if the feature
+isn't required (required="false") and in that case it is optional.
+- optional: the permission is optional
+
+Example:
+<feature name="urn:AGL:widget:required-permission">
+ <param name="#target" value="geoloc" />
+ <param name="urn:AGL:permission:real-time" value="required" />
+ <param name="urn:AGL:permission:syscall:*" value="required" />
+</feature>
+--------------------------------------------------------------------------------->
+
+<!-- Feature : provided-unit -------------------------------------------------
+This feature is made for declaring new units
+for the widget. Using this feature, a software publisher
+can provide more than one application in the same widget.
+
+#### param name="#target" REQUIRED
+
+Declares the name of the unit. The default unit, the unit
+of the main of the widget, has the name "main". The value
+given here must be unique within the widget file. It will
+be used in other places of the widget config.xml file to
+designate the unit.
+
+Only one instance of the param "#target" is allowed.
+The value can't be "main".
+
+#### param name="content.type" REQUIRED
+
+The mimetype of the provided unit.
+
+#### param name="content.src"
+
+A path to the file (subject to localisation), this is the entry point
+to that unit.
+
+#### other parameters
+
+The items that can be set for the main unit
+can also be set using the params if needed.
+
+ - description
+ - name.content
+ - name.short
+ - ...
+
+Example:
+<feature name="urn:AGL:widget:provided-unit">
+ <param name="#target" value="geoloc" />
+ <param name="description" value="binding of name geoloc" />
+ <param name="content.src" value="index.html" />
+ <param name="content.type" value="application/vnd.agl.service" />
+</feature>
+--------------------------------------------------------------------------------->
+
+<!-- Feature: provided-api ---------------------------------------------------
+Use this feature for exporting one or more API of a unit
+to other widgets of the platform.
+
+This feature is an important feature of the framework.
+
+#### param name="#target" OPTIONAL
+
+Declares the name of the unit exporting the listed apis.
+Only one instance of the param "#target" is allowed.
+When there is not instance of this param, it behave as if
+the target main was specified.
+
+#### param name=[name of exported api]
+
+The name give the name of the api that is exported.
+
+The value is one of the following values:
+
+- ws: export the api using UNIX websocket
+- dbus: export the API using dbus
+- auto: export the api using the default method(s).
+
+Example:
+<feature name="urn:AGL:widget:provided-api">
+ <param name="#target" value="geoloc" />
+ <param name="geoloc" value="auto" />
+ <param name="moonloc" value="auto" />
+</feature>
+--------------------------------------------------------------------------------->
+</widget>
diff --git a/conf.d/app-templates/samples.d/xds-config.env.sample b/conf.d/app-templates/samples.d/xds-config.env.sample
new file mode 100644
index 0000000..a4c51b1
--- /dev/null
+++ b/conf.d/app-templates/samples.d/xds-config.env.sample
@@ -0,0 +1,10 @@
+XDS_PROJECT_ID=W2EAQBA-HQI75XA_unicens2-binding
+XDS_SDK_ID=poky-agl_aarch64_3.99.1+snapshot
+XDS_SERVER_URL=localhost:8000
+
+DOCKER_TARGET=docker-sdk
+
+RSYNC_TARGET=root@192.168.168.11
+RSYNC_PREFIX=./opt
+PROJECT_DIR=/home/seb/tmp/unicens2-binding
+
diff --git a/conf.d/app-templates/template.d/autobuild/agl/autobuild.in b/conf.d/app-templates/template.d/autobuild/agl/autobuild.in
new file mode 100755
index 0000000..4811441
--- /dev/null
+++ b/conf.d/app-templates/template.d/autobuild/agl/autobuild.in
@@ -0,0 +1,63 @@
+#!/usr/bin/make -f
+# Copyright (C) 2015, 2016 "IoT.bzh"
+# Author "Romain Forlot" <romain.forlot@iot.bzh>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+THISFILE := $(lastword $(MAKEFILE_LIST))
+BUILD_DIR := $(abspath $(dir $(THISFILE)/../../../../..)/build)
+DEST := ${BUILD_DIR}/target
+
+.PHONY: all clean distclean configure build package help update
+
+all: help
+
+help:
+ @echo "List of targets available:"
+ @echo ""
+ @echo "- all"
+ @echo "- clean"
+ @echo "- distclean"
+ @echo "- configure"
+ @echo "- build"
+ @echo "- package"
+ @echo ""
+ @echo "Usage: ./conf.d/autobuild/agl/autobuild package DEST=${HOME}/opt"
+ @echo "Don't use your build dir as DEST as wgt file is generated at this location"
+
+update: configure
+ @cmake --build ${BUILD_DIR} --target autobuild
+
+clean:
+ @([ -d ${BUILD_DIR} ] && make -C ${BUILD_DIR} clean) || echo Nothing to clean
+
+distclean:
+ @rm -rf ${BUILD_DIR}
+
+configure: ${BUILD_DIR}/Makefile
+
+build: configure
+ @cmake --build ${BUILD_DIR} ${BUILD_ARGS} --target all
+
+package: build
+ @mkdir -p ${BUILD_DIR}/$@/bin
+ @mkdir -p ${BUILD_DIR}/$@/etc
+ @mkdir -p ${BUILD_DIR}/$@/lib
+ @mkdir -p ${BUILD_DIR}/$@/htdocs
+ @mkdir -p ${BUILD_DIR}/$@/data
+ @cmake --build ${BUILD_DIR} --target widget
+ @mkdir -p ${DEST} && cp ${BUILD_DIR}/*wgt ${DEST}
+
+${BUILD_DIR}/Makefile:
+ @[ -d ${BUILD_DIR} ] || mkdir -p ${BUILD_DIR}
+ @[ -f ${BUILD_DIR}/Makefile ] || (cd ${BUILD_DIR} && cmake ${CONFIGURE_ARGS} ..)
diff --git a/conf.d/app-templates/template.d/autobuild/linux/autobuild.in b/conf.d/app-templates/template.d/autobuild/linux/autobuild.in
new file mode 100755
index 0000000..0b6f06c
--- /dev/null
+++ b/conf.d/app-templates/template.d/autobuild/linux/autobuild.in
@@ -0,0 +1,65 @@
+#!/usr/bin/make -f
+# Copyright (C) 2015, 2016 "IoT.bzh"
+# Author "Romain Forlot" <romain.forlot@iot.bzh>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+THISFILE := $(lastword $(MAKEFILE_LIST))
+BUILD_DIR := $(abspath $(dir $(THISFILE)/../../../../..)/build)
+DEST := ${BUILD_DIR}/target
+
+.PHONY: all clean distclean configure build package help update
+
+all: help
+
+help:
+ @echo "List of targets available:"
+ @echo ""
+ @echo "- all"
+ @echo "- clean"
+ @echo "- distclean"
+ @echo "- configure"
+ @echo "- build"
+ @echo "- package"
+ @echo ""
+ @echo "Usage: ./conf.d/autobuild/agl/autobuild package DEST=${HOME}/opt"
+ @echo "Don't use your build dir as DEST as wgt file is generated at this location"
+
+update: configure
+ @cmake --build ${BUILD_DIR} --target autobuild
+
+clean:
+ @([ -d ${BUILD_DIR} ] && make -C ${BUILD_DIR} clean) || echo Nothing to clean
+
+distclean:
+ @rm -rf ${BUILD_DIR}
+
+configure: ${BUILD_DIR}/Makefile
+
+build: configure
+ @cmake --build ${BUILD_DIR} ${BUILD_ARGS} --target all
+
+package: build
+ @mkdir -p ${BUILD_DIR}/$@/bin
+ @mkdir -p ${BUILD_DIR}/$@/etc
+ @mkdir -p ${BUILD_DIR}/$@/lib
+ @mkdir -p ${BUILD_DIR}/$@/htdocs
+ @mkdir -p ${BUILD_DIR}/$@/data
+ @cmake --build ${BUILD_DIR} --target packaging
+ @cmake --build ${BUILD_DIR} --target widget
+ @mkdir -p ${DEST} && cp ${BUILD_DIR}/*wgt ${DEST}
+
+
+${BUILD_DIR}/Makefile:
+ @[ -d ${BUILD_DIR} ] || mkdir -p ${BUILD_DIR}
+ @[ -f ${BUILD_DIR}/Makefile ] || (cd ${BUILD_DIR} && cmake ${CONFIGURE_ARGS} ..)
diff --git a/conf.d/app-templates/template.d/config.xml.in b/conf.d/app-templates/template.d/config.xml.in
new file mode 100644
index 0000000..35d20b8
--- /dev/null
+++ b/conf.d/app-templates/template.d/config.xml.in
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<widget xmlns="http://www.w3.org/ns/widgets" id="@PROJECT_NAME@" version="@PROJECT_VERSION@">
+ <name>@PROJECT_NAME@</name>
+ <icon src="@PROJECT_ICON@"/>
+ <content src="@WIDGET_ENTRY_POINT@" type="@WIDGET_TYPE@"/>
+ <description>@PROJECT_DESCRIPTION@</description>
+ <author>@PROJECT_AUTHOR@ &lt;@PROJECT_AUTHOR_MAIL@&gt;</author>
+ <license>@PROJECT_LICENSE@</license>
+</widget>
diff --git a/conf.d/app-templates/template.d/deb-config.dsc.in b/conf.d/app-templates/template.d/deb-config.dsc.in
new file mode 100644
index 0000000..28a4835
--- /dev/null
+++ b/conf.d/app-templates/template.d/deb-config.dsc.in
@@ -0,0 +1,15 @@
+Format: @PROJECT_VERSION@
+Source: @NPKG_PROJECT_NAME@
+Binary: @NPKG_PROJECT_NAME@-bin
+Architecture: any
+Version: 2.0-0
+Maintainer: @PROJECT_AUTHOR@ <@PROJECT_AUTHOR_MAIL@>
+Standards-Version: 3.8.2
+Homepage: @PROJECT_URL@
+Build-Depends: debhelper (>= 5),
+ pkg-config,
+ cmake,
+ gcc,
+ g++,
+@DEB_PKG_DEPS@
+Debtransform-Tar: @NPKG_PROJECT_NAME@_@PROJECT_VERSION@.orig.tar.gz
diff --git a/conf.d/app-templates/template.d/deb-config.install.in b/conf.d/app-templates/template.d/deb-config.install.in
new file mode 100644
index 0000000..5858efd
--- /dev/null
+++ b/conf.d/app-templates/template.d/deb-config.install.in
@@ -0,0 +1,2 @@
+/opt/AGL/*
+/etc/profile.d/*
diff --git a/conf.d/app-templates/template.d/debian.changelog.in b/conf.d/app-templates/template.d/debian.changelog.in
new file mode 100644
index 0000000..f72c717
--- /dev/null
+++ b/conf.d/app-templates/template.d/debian.changelog.in
@@ -0,0 +1,5 @@
+@NPKG_PROJECT_NAME@ (@PROJECT_VERSION@-0) UNRELEASED; urgency=low
+
+ * init build
+
+ -- @PROJECT_AUTHOR@ <@PROJECT_AUTHOR_MAIL@> Mon, 25 Dec 2007 10:50:38 +0100
diff --git a/conf.d/app-templates/template.d/debian.compat.in b/conf.d/app-templates/template.d/debian.compat.in
new file mode 100644
index 0000000..45a4fb7
--- /dev/null
+++ b/conf.d/app-templates/template.d/debian.compat.in
@@ -0,0 +1 @@
+8
diff --git a/conf.d/app-templates/template.d/debian.control.in b/conf.d/app-templates/template.d/debian.control.in
new file mode 100644
index 0000000..ea4ad8a
--- /dev/null
+++ b/conf.d/app-templates/template.d/debian.control.in
@@ -0,0 +1,16 @@
+Priority: optional
+Maintainer: @PROJECT_AUTHOR@ <@PROJECT_AUTHOR_MAIL@>
+Source: @NPKG_PROJECT_NAME@
+Build-Depends: debhelper (>= 5),
+ pkg-config,
+ cmake,
+ gcc,
+ g++,
+@DEB_PKG_DEPS@
+Standards-Version: 3.8.2
+Homepage: @PROJECT_URL@
+
+Package: @NPKG_PROJECT_NAME@
+Section: libs
+Architecture: any
+Description: @PROJECT_DESCRIPTION@
diff --git a/conf.d/app-templates/template.d/debian.rules.in b/conf.d/app-templates/template.d/debian.rules.in
new file mode 100644
index 0000000..6bb2825
--- /dev/null
+++ b/conf.d/app-templates/template.d/debian.rules.in
@@ -0,0 +1,87 @@
+#!/usr/bin/make -f
+# -*- makefile -*-
+# Uncomment this to turn on verbose mode.
+export DH_VERBOSE=1
+
+# These are used for cross-compiling and for saving the configure script
+# from having to guess our platform (since we know it already)
+DEB_HOST_GNU_TYPE ?= $(shell dpkg-architecture -qDEB_HOST_GNU_TYPE)
+DEB_BUILD_GNU_TYPE ?= $(shell dpkg-architecture -qDEB_BUILD_GNU_TYPE)
+
+
+CFLAGS = -Wall -g
+
+ifneq (,$(findstring noopt,$(DEB_BUILD_OPTIONS)))
+ CFLAGS += -O0
+else
+ CFLAGS += -O2
+endif
+ifeq (,$(findstring nostrip,$(DEB_BUILD_OPTIONS)))
+ INSTALL_PROGRAM += -s
+endif
+
+configure: configure-stamp
+configure-stamp:
+ dh_testdir
+ #
+ touch configure-stamp
+
+build: build-stamp
+build-stamp: configure-stamp
+ dh_testdir
+ mkdir -p build
+ cd build;cmake ../ -DCMAKE_INSTALL_PREFIX:PATH=/opt/AGL/@PROJECT_NAME@ -DCMAKE_INSTALL_LIBDIR:PATH=lib/$(DEB_HOST_MULTIARCH);$(MAKE)
+ #
+ touch build-stamp
+
+clean:
+ #dh_testdir
+ dh_testroot
+ rm -f configure-stamp build-stamp
+ [ ! -f Makefile ] || $(MAKE) distclean
+ #dh_clean
+
+install: build
+ dh_testdir
+ dh_testroot
+ dh_prep
+ dh_installdirs
+ # Add here commands to install the package into debian/tmp
+ mkdir -p debian/tmp/opt/AGL/@PROJECT_NAME@;cd build;make populate;cp -r package/* ../debian/tmp/opt/AGL/@PROJECT_NAME@/
+ mkdir -p debian/tmp/etc/profile.d
+ echo '#---------- AGL @PROJECT_NAME@ options Start ---------" ' > debian/tmp/etc/profile.d/AGL_@PROJECT_NAME@.sh
+ echo '# Object: AGL cmake option for binder/bindings' >> debian/tmp/etc/profile.d/AGL_@PROJECT_NAME@.sh
+ echo 'export LD_LIBRARY_PATH=/opt/AGL/@PROJECT_NAME@/lib/$(DEB_HOST_MULTIARCH):$$LD_LIBRARY_PATH' >> debian/tmp/etc/profile.d/AGL_@PROJECT_NAME@.sh
+ echo 'export LIBRARY_PATH=/opt/AGL/@PROJECT_NAME@/lib/$(DEB_HOST_MULTIARCH):$$LIBRARY_PATH' >> debian/tmp/etc/profile.d/AGL_@PROJECT_NAME@.sh
+ echo 'export PATH=/opt/AGL/@PROJECT_NAME@/bin:$$PATH' >> debian/tmp/etc/profile.d/AGL_@PROJECT_NAME@.sh
+ echo '#---------- AGL options End ---------' >> debian/tmp/etc/profile.d/AGL_@PROJECT_NAME@.sh
+ # Move all files in their corresponding package
+ dh_install --list-missing -s --sourcedir=debian/tmp
+ # empty dependency_libs in .la files
+ #sed -i "/dependency_libs/ s/'.*'/''/" `find debian/ -name '*.la'`
+
+# Build architecture-independent files here.
+binary-indep: build install
+# We have nothing to do by default.
+
+# Build architecture-dependent files here.
+binary-arch: build install
+ dh_testdir
+ dh_testroot
+ dh_installchangelogs
+ dh_installdocs
+ dh_installexamples
+ dh_installman
+ dh_link
+ dh_strip
+ dh_compress
+ dh_fixperms
+ dh_makeshlibs -V
+ dh_installdeb
+ dh_shlibdeps --dpkg-shlibdeps-params=--ignore-missing-info
+ dh_gencontrol
+ dh_md5sums
+ dh_builddeb
+
+binary: binary-indep binary-arch
+.PHONY: build clean binary-indep binary-arch binary install
diff --git a/conf.d/app-templates/template.d/gdb-native-target.ini.in b/conf.d/app-templates/template.d/gdb-native-target.ini.in
new file mode 100644
index 0000000..1ba6bdb
--- /dev/null
+++ b/conf.d/app-templates/template.d/gdb-native-target.ini.in
@@ -0,0 +1,35 @@
+# gdb-remote.init file for IDE
+# Object: allow to use standard gdb to remote debug a target
+# Usage: remote-target-populate update script under ./build directory
+# Author: Fulup Ar Foll (IoT.bzh)
+# Reference: https://blog.flameeyes.eu/2010/02/remote-debugging-with-gdb-part-2-gdb/
+#
+# Warning: In remote native mode netbeans impose debug-command to point on a local instance of afb-daemon binary
+
+# Fulup Hoops: --ldpath should be absolute as solib-search-path refuse to work as documented
+
+# Start gdbserver on target and connect through SSH link WARNING:
+target remote | ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null @RSYNC_TARGET@ gdbserver - afb-daemon --port=@AFB_REMPORT@ --workdir=$HOME/@RSYNC_PREFIX@/@PROJECT_NAME@ --roothttp=htdocs --ldpaths=$HOME/@RSYNC_PREFIX@/@PROJECT_NAME@/lib --verbose --token=@AFB_TOKEN@
+
+# Disable auto answer no on questions and to set breakpoint
+set confirm off
+
+# Disable auto load of libraries to improved perf
+set auto-solib-add off
+
+# Define path for project libraries
+set solib-search-path ${PROJECT_PKG_BUILD_DIR}
+
+# Replace run by continue (gdb use 'run' when gdbserver wants 'continue')
+define run
+continue
+end
+
+# Manually load project libraries when loaded by afb-daemon
+tbreak @GDB_INITIAL_BREAK@
+commands
+sharedlibrary @RSYNC_PREFIX@/@PROJECT_NAME@
+continue
+end
+
+
diff --git a/conf.d/app-templates/template.d/install-wgt-on-target.sh.in b/conf.d/app-templates/template.d/install-wgt-on-target.sh.in
new file mode 100755
index 0000000..39a69d5
--- /dev/null
+++ b/conf.d/app-templates/template.d/install-wgt-on-target.sh.in
@@ -0,0 +1,19 @@
+#!/bin/sh
+#
+# File: install-wgt-on-target.sh
+# Author: Sebastien Douheret @ IoT.bzh
+# Object: install widget on target
+# Created on 24-May-2017, 09:23:37
+# Usage:
+
+# Do not change manually use 'make remote-target-populate'
+export RSYNC_TARGET=@RSYNC_TARGET@
+export WGT_FILE_L=@CMAKE_CURRENT_BINARY_DIR@/@PROJECT_NAME@.wgt
+export WGT_FILE_T=/tmp/@PROJECT_NAME@.wgt
+
+scp $WGT_FILE_L $RSYNC_TARGET:$WGT_FILE_T \
+ && ssh -o "StrictHostKeyChecking no" -tt $RSYNC_TARGET -- \
+ afm-util install $WGT_FILE_T
+
+# && rm -f $WGT_FILE_T
+
diff --git a/conf.d/app-templates/template.d/rpm-config.spec.in b/conf.d/app-templates/template.d/rpm-config.spec.in
new file mode 100644
index 0000000..34a605d
--- /dev/null
+++ b/conf.d/app-templates/template.d/rpm-config.spec.in
@@ -0,0 +1,62 @@
+###########################################################################
+# Copyright 2015, 2016, 2017 IoT.bzh
+#
+# author: @PROJECT_AUTHOR@ <@PROJECT_AUTHOR_MAIL@>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+###########################################################################
+
+
+Name: @NPKG_PROJECT_NAME@
+Version: @PROJECT_VERSION@
+Release: 1
+Group: AGL
+License: @PROJECT_LICENSE@
+Summary: @PROJECT_DESCRIPTION@
+Url: @PROJECT_URL@
+Source0: %{name}_%{version}.orig.tar.gz
+
+BuildRequires: cmake
+BuildRequires: gcc gcc-c++
+@RPM_PKG_DEPS@
+
+BuildRoot: %{_tmppath}/%{name}-%{version}-build
+
+%define _prefix /opt/AGL/@PROJECT_NAME@
+%define __cmake cmake
+
+%description
+@PROJECT_DESCRIPTION@
+
+%prep
+%setup -q
+
+%build
+%cmake -DCMAKE_INSTALL_PREFIX:PATH=%{_libdir}
+make %{?_smp_mflags}
+
+%install
+CURDIR=$(pwd)
+[ -d build ] && cd build
+make populate
+mkdir -p %{?buildroot}%{_prefix}
+cp -r package/* %{?buildroot}%{_prefix}
+
+cd $CURDIR
+find %{?buildroot}%{_prefix} -type d -exec echo "%dir {}" \;>> pkg_file
+find %{?buildroot}%{_prefix} -type f -exec echo "{}" \;>> pkg_file
+sed -i 's@%{?buildroot}@@g' pkg_file
+
+
+%files -f pkg_file
+%defattr(-,root,root)
diff --git a/conf.d/app-templates/template.d/start-on-target.sh.in b/conf.d/app-templates/template.d/start-on-target.sh.in
new file mode 100755
index 0000000..d5426ed
--- /dev/null
+++ b/conf.d/app-templates/template.d/start-on-target.sh.in
@@ -0,0 +1,25 @@
+#!/bin/sh
+#
+# File: start-on-target.sh
+# Author: Fulup Ar Foll @ IoT.bzh
+# Object: Forward signal (SIGTERM) to remote process
+# Created on 24-May-2017, 09:23:37
+# Usage: remote-target-populate update script under ./build directory
+
+# Do not change manually use 'make remote-target-populate'
+export RSYNC_TARGET=@RSYNC_TARGET@
+export PROJECT_NAME=@PROJECT_NAME@
+export RSYNC_PREFIX=@RSYNC_PREFIX@/@PROJECT_NAME@
+export AFB_REMPORT=@AFB_REMPORT@
+export AFB_TOKEN=@AFB_TOKEN@
+
+exec ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -tt $RSYNC_TARGET << EOF
+ afb-daemon --workdir=$RSYNC_PREFIX --port=$AFB_REMPORT --roothttp=./htdocs --ldpath=./lib --verbose --token=$AFB_TOKEN &
+ PID_DAEMON=\$!
+
+ trap "echo REMOTE-SIGNAL TRAP; kill -15 \$PID_DAEMON" INT QUIT TERM EXIT
+
+ # wait for daemon to finish
+ wait \$PID_DAEMON
+ exit
+EOF
diff --git a/conf.d/app-templates/wgt/icon-default.png b/conf.d/app-templates/wgt/icon-default.png
new file mode 100644
index 0000000..def888b
--- /dev/null
+++ b/conf.d/app-templates/wgt/icon-default.png
Binary files differ
diff --git a/conf.d/app-templates/wgt/icon-html5.png b/conf.d/app-templates/wgt/icon-html5.png
new file mode 100644
index 0000000..a3573ef
--- /dev/null
+++ b/conf.d/app-templates/wgt/icon-html5.png
Binary files differ
diff --git a/conf.d/app-templates/wgt/icon-native.png b/conf.d/app-templates/wgt/icon-native.png
new file mode 100644
index 0000000..fcb5d35
--- /dev/null
+++ b/conf.d/app-templates/wgt/icon-native.png
Binary files differ
diff --git a/conf.d/app-templates/wgt/icon-qml.png b/conf.d/app-templates/wgt/icon-qml.png
new file mode 100644
index 0000000..ba248ea
--- /dev/null
+++ b/conf.d/app-templates/wgt/icon-qml.png
Binary files differ
diff --git a/conf.d/app-templates/wgt/icon-service.png b/conf.d/app-templates/wgt/icon-service.png
new file mode 100644
index 0000000..3aeb6f0
--- /dev/null
+++ b/conf.d/app-templates/wgt/icon-service.png
Binary files differ
diff --git a/conf.d/project/json.d/ahl-binding.json b/conf.d/project/json.d/ahl-binding.json
new file mode 100644
index 0000000..d53a9aa
--- /dev/null
+++ b/conf.d/project/json.d/ahl-binding.json
@@ -0,0 +1,127 @@
+{
+ "description" : "Audio Plugin configuration files",
+ "Audioroles" : {
+ "Description" : "Specify configuration and default value for Audio",
+ "Warning" :{
+ "Attributes" :{
+ "Interruptible" : "No",
+ "Routing" : "None"
+ },
+ "devices" :{
+ "output" : ["Speaker", "Lineout", "HDMI", "Default"]
+ }
+ },
+ "Guidance" : {
+ "Attributes" :{
+ "Interruptible" : "Yes",
+ "Routing" : "None"
+ },
+ "devices" :{
+ "output" : ["Speaker", "Lineout", "Default"]
+ }
+ },
+ "Notification" : {
+ "Attributes" :{
+ "Interruptible" : "Yes",
+ "Routing" : "None"
+ },
+ "devices" :{
+ "output" : ["Speaker", "Lineout", "HDMI", "Default"]
+ }
+ },
+ "Communications" :{
+ "Attributes" :{
+ "Interruptible" : "Yes",
+ "Routing" : "Custom"
+ },
+ "devices" :{
+ "output" : [ "Speaker", "Lineout", "Default"],
+ "input" : [ "BT", "USB", "Headset", "Linein", "Default"]
+ },
+ "Routing" :{
+ "phone":{
+ "Input" : "BT",
+ "Output" : "Speaker"
+ },
+ "usb":{
+ "Input" : "USB",
+ "Output" : "Speaker"
+ },
+ "headset":{
+ "Input" : "Headset",
+ "Output" : "Speaker"
+ },
+ "phone_aux":{
+ "Input" : "BT",
+ "Output" : "Lineout"
+ },
+ "usb_aux":{
+ "Input" : "USB",
+ "Output" : "Lineout"
+ }
+
+ }
+
+
+ },
+ "Entertainment" : {
+ "Attributes" :{
+ "Interruptible" : "Yes",
+ "Routing" : "Custom"
+ },
+ "devices" :{
+ "output" : [ "Headphone", "Speaker", "Lineout", "Default"],
+ "input" : [ "BT", "USB", "Linein", "Default"]
+ },
+ "Routing" : {
+ "phone":{
+ "Input" : "BT",
+ "Output" : "Speaker"
+ },
+ "phone_headphone":{
+ "Input" : "BT",
+ "Output" : "Headphone"
+ },
+ "phone_aux":{
+ "Input" : "BT",
+ "Output" : "Lineout"
+ },
+ "usb":{
+ "Input" : "USB",
+ "Output" : "Speaker"
+ },
+ "usb_headphone":{
+ "Input" : "USB",
+ "Output" : "Headphone"
+ },
+ "usb_aux":{
+ "Input" : "USB",
+ "Output" : "Lineout"
+ },
+ "aux":{
+ "Input" : "Linein",
+ "Output" : "Speaker"
+ },
+ "aux_headphone":{
+ "Input" : "Linein",
+ "Output" : "Headphone"
+ },
+ "aux_aux":{
+ "Input" : "Linein",
+ "Output" : "Lineout"
+ }
+
+ }
+ },
+ "System" : {
+ "Attributes" :{
+ "Interruptible" : "Yes",
+ "Routing" : "All"
+ },
+ "devices" :{
+ "output" : [ "Speaker", "Headset", "Headphone", "Handset", "BT", "HDMI", "Lineout", "USB", "Default"],
+ "input" : [ "BT", "Linein", "USB", "Voice", "Default"]
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/conf.d/project/json.d/onload-audio-control.json b/conf.d/project/json.d/onload-audio-control.json
new file mode 100644
index 0000000..14d6970
--- /dev/null
+++ b/conf.d/project/json.d/onload-audio-control.json
@@ -0,0 +1,137 @@
+{
+ "$schema": "ToBeDone",
+ "metadata": {
+ "label": "audio-policy-control-configuration",
+ "info": "Provide Default Audio Policy Control configuration",
+ "version": "1.0"
+ },
+ "onload": [{
+ "label": "onload-default",
+ "info": "onload initialisation config",
+ "plugin": {
+ "label" : "_MyPlug",
+ "sharelib": "ctl-audio-plugin-sample.ctlso",
+ "lua2c": ["Lua2cHelloWorld1", "Lua2cHelloWorld2"]
+ },
+ "require": ["intel-hda"],
+ "actions": [
+ {
+ "label": "onload-sample-cb",
+ "info": "Call control sharelib install entrypoint",
+ "callback": "SamplePolicyInit",
+ "args": {
+ "arg1": "first_arg",
+ "nextarg": "second arg value"
+ }
+ }, {
+ "label": "onload-sample-api",
+ "info": "Assert AlsaCore Presence",
+ "api": "alsacore",
+ "verb": "ping",
+ "args": {"data": "none"}
+ }, {
+ "label": "onload-hal-lua",
+ "info": "Load avaliable HALs",
+ "lua": "_Alsa_Get_Hal"
+ }
+ ]
+ }],
+ "controls":
+ [
+ {
+ "label": "duckentertainment_lowprioritypriority",
+ "api": "intel-hda",
+ "verb": "SetRTPC",
+ "args": {
+ "value": "-30"
+ }
+ },
+
+ {
+ "label": "multimedia",
+ "actions": {
+ "label": "multimedia-control-lua",
+ "info": "Call Lua Script function Test_Lua_Engin",
+ "lua": "_Audio_Set_Multimedia"
+ }
+ }, {
+ "label": "navigation",
+ "actions": {
+ "label": "navigation-control-lua",
+ "info": "Call Lua Script to set Navigation",
+ "lua": "_Audio_Set_Navigation"
+ }
+ }, {
+ "label": "emergency",
+ "actions": {
+ "label": "emergency-control-ucm",
+ "lua": "_Audio_Set_Emergency"
+ }
+ }, {
+ "label": "multi-step-sample",
+ "info" : "all actions must succeed for control to be accepted",
+ "actions": [{
+ "label": "multimedia-control-cb",
+ "info": "Call Sharelib Sample Callback",
+ "callback": "sampleControlNavigation",
+ "args": {
+ "arg1": "snoopy",
+ "arg2": "toto"
+ }
+ }, {
+ "label": "duckentertainment_lowprioritypriority",
+ "api": "hal",
+ "verb": "SetRTPC",
+ "args": {
+ "value": "-30"
+ }
+ }, {
+ "label": "navigation-control-lua",
+ "info": "Call Lua Script to set Navigation",
+ "lua": "_Audio_Set_Navigation"
+ }]
+ }
+ ],
+ "events":
+ [
+ {
+ "label": "SampleEvent1",
+ "info": "define action when receiving a given event",
+ "actions": [
+ {
+ "label": "Event Callback-1",
+ "callback": "SampleControlEvent",
+ "args": {
+ "arg": "action-1"
+ }
+ }, {
+ "label": "Event Callback-2",
+ "callback": "SampleControlEvent",
+ "args": {
+ "arg": "action-2"
+ }
+ }
+ ]
+ },
+ {
+ "label": "SampleEvent2",
+ "info": "define action when receiving a given event",
+ "actions": [
+ {
+ "label": "Event Callback-1",
+ "callback": "SampleControlEvent",
+ "args": {
+ "arg": "action-1"
+ }
+ }, {
+ "label": "Event Callback-2",
+ "callback": "SampleControlEvent",
+ "args": {
+ "arg": "action-2"
+ }
+ }
+ ]
+ }
+ ]
+}
+
diff --git a/htdocs/CMakeLists.txt b/htdocs/CMakeLists.txt
index e85ca3c..2e51461 100644
--- a/htdocs/CMakeLists.txt
+++ b/htdocs/CMakeLists.txt
@@ -25,18 +25,8 @@ PROJECT_TARGET_ADD(htdocs)
file(GLOB SOURCE_FILES "*.html" "*.js" "*.jpg" "*.css")
- add_custom_target(${TARGET_NAME}
- DEPENDS ${PROJECT_PKG_BUILD_DIR}/${TARGET_NAME}
- )
-
- add_custom_command(
- DEPENDS ${SOURCE_FILES}
- OUTPUT ${PROJECT_PKG_BUILD_DIR}/${TARGET_NAME}
- COMMAND mkdir -p ${PROJECT_PKG_BUILD_DIR}/${TARGET_NAME}
- COMMAND touch ${PROJECT_PKG_BUILD_DIR}/${TARGET_NAME}
- COMMAND cp -r ${SOURCE_FILES} ${PROJECT_PKG_BUILD_DIR}/${TARGET_NAME}
- )
-
+ add_input_files("${SOURCE_FILES}")
+
SET_TARGET_PROPERTIES(${TARGET_NAME} PROPERTIES
LABELS "HTDOCS"
OUTPUT_NAME ${TARGET_NAME}
diff --git a/htdocs/alsa-core.html b/htdocs/alsa-core.html
index e946bdb..deca631 100644
--- a/htdocs/alsa-core.html
+++ b/htdocs/alsa-core.html
@@ -2,23 +2,25 @@
<head>
<link rel="stylesheet" href="AudioBinding.css">
<title>Alsa Low Level Simple Test</title>
-
+
<script type="text/javascript" src="AFB-websock.js"></script>
<script type="text/javascript" src="AudioBinding.js"></script>
</head>
-
-<body onload="init('alsa_registry','alsacore', 'getinfo')">
-
- <button id="connected" onclick="init('alsa_registry','alsacore', 'getinfo');">Binder WS Fail</button>
+
+<body onload="init('alsa_registry','alsacore', 'infoget')">
+
+ <button id="connected" onclick="init('alsa_registry','alsacore', 'infoget');">Binder WS Fail</button>
+ <button id="mnitoring" onclick="window.open('/monitoring/monitor.html','_monitor_audio')">Debug/Monitoring</a></button>
+
<br><br>
<b>Selected SndCard </b>
<select id='alsa_registry'></select>
<b>Select NUMID </b>
- <select id='alsa_ctl_list' onclick="refresh_list(this, 'alsacore', 'getctl', {devid:sndcard, mode:1})">
+ <select id='alsa_ctl_list' onclick="refresh_list(this, 'alsacore', 'ctlget', {devid:sndcard, mode:1})">
<option value='-1'>Refresh NUMID list</option>
- </select>
-
+ </select>
+
<b>API Verbosity </b>
<select id='api_verbosity' onclick='mode=this.value'>
<option value='0'>Quiet</option>
@@ -26,37 +28,39 @@
<option value='2'>Verbose</option>
<option value='3'>Full</option>
</select>
-
+
<br>
<ol>
- <li><button onclick="callbinder('alsacore','getctl', {devid:sndcard, mode:mode})">Get all Alsa Ctls</button></li>
- <li><button onclick="callbinder('alsacore','getctl', {devid:sndcard, mode:mode, ctl:[numid]})">Get Alsa Ctls [numid]</button></li>
- <li><button onclick="callbinder('alsacore','getctl', {devid:sndcard, mode:mode, ctl:[numid,numid+1]})">Get Alsa Ctls [numid,numid+1]</button></li>
+ <li><button onclick="callbinder('alsacore','ctlget', {devid:sndcard, mode:mode})">Get all Alsa Ctls</button></li>
+ <li><button onclick="callbinder('alsacore','ctlget', {devid:sndcard, mode:mode, ctl:numid})">Get Alsa Ctls [numid]</button></li>
+ <li><button onclick="callbinder('alsacore','ctlget', {devid:sndcard, mode:mode, ctl:[numid,numid+1]})">Get Alsa Ctls [numid,numid+1]</button></li>
+ <li><button onclick="callbinder('alsacore','infoget', {})">Get info</button></li>
+ <li><button onclick="callbinder('alsacore','hallist', {})">HAL list</button></li>
+ <br>
+
+
+ <li><button onclick="callbinder('alsacore','ctlset', {devid:sndcard, mode:mode, ctl:[[9,20]]})">Set Alsa Ctl ctl:[[9,20]]</button></li>
+ <li><button onclick="callbinder('alsacore','ctlset', {devid:sndcard, mode:mode, ctl:[{id:46,val:50}]})">Set Alsa Ctl ctl:[{id:46,val:50}]}</button></li>
+ <li><button onclick="callbinder('alsacore','ctlset', {devid:sndcard, mode:mode, ctl:[[6,[20,20]]]})">Set Alsa Ctl ctl:[[6,[20,20]]]</button></li>
+ <li><button onclick="callbinder('alsacore','ctlset', {devid:sndcard, mode:mode, ctl:[{id:6,val:[50,50]}]})">Set Alsa Ctl ctl:[{id:2,val:[50,50]}]</button></li>
+ <li><button onclick="callbinder('alsacore','ctlset', {devid:sndcard, mode:mode, ctl:[{id:6,val:[50,50]}, {id:9,val:50,50}]})">Set Alsa Ctl ctl:[{id:6,val:[50,50]}, {id:9,val:50,50}]</button></li>
<br>
-
-
- <li><button onclick="callbinder('alsacore','setctl', {devid:sndcard, mode:mode, ctl:[[9,20]]})">Set Alsa Ctl ctl:[[9,20]]</button></li>
- <li><button onclick="callbinder('alsacore','setctl', {devid:sndcard, mode:mode, ctl:[{id:9,val:50}]})">Set Alsa Ctl ctl:[{id:9,val:50}]}</button></li>
- <li><button onclick="callbinder('alsacore','setctl', {devid:sndcard, mode:mode, ctl:[[6,[20,20]]]})">Set Alsa Ctl ctl:[[6,[20,20]]]</button></li>
- <li><button onclick="callbinder('alsacore','setctl', {devid:sndcard, mode:mode, ctl:[{id:6,val:[50,50]}]})">Set Alsa Ctl ctl:[{id:2,val:[50,50]}]</button></li>
- <li><button onclick="callbinder('alsacore','setctl', {devid:sndcard, mode:mode, ctl:[{id:6,val:[50,50]}, {id:9,val:50,50}]})">Set Alsa Ctl ctl:[{id:6,val:[50,50]}, {id:9,val:50,50}]</button></li>
- <br>
<li><button onclick="callbinder('alsacore','ucmquery', {devid:sndcard, mode:mode})">List UCM verbs</button></li>
<li><button onclick="callbinder('alsacore','ucmset' , {devid:sndcard, mode:mode, verb:'HiFi'})">Set UCM HiFi</button></li>
<li><button onclick="callbinder('alsacore','ucmset' , {devid:sndcard, mode:mode, verb:'HiFi', dev:'Headphone'})">Set UCM HiFi+Headphone</button></li>
<li><button onclick="callbinder('alsacore','ucmset' , {devid:sndcard, mode:mode, verb:'HiFi', dev:'Headphone', mod:'RecordMedia'})">Set UCM HiFi+Headphone+RecordMedia</button></li>
- <br>
+ <br>
<li><button onclick="callbinder('alsacore','ucmget' , {devid:sndcard, mode:mode, values:['OutputDspName','PlaybackPCM','CapturePCM']})">Get UCM OutputDspName+PlaybackPCM+CapturePCM (SET UCM)</button></li>
- <br>
+ <br>
<li><button onclick="callbinder('alsacore','subscribe', {devid:sndcard})">Subscribe AlsaCtl Events</button></li>
<br>
</ol>
-
+
<div id="main" style="visibility:hidden">
<ol>
<li>Question <pre id="question"></pre>
<li>Response <pre id="output"></pre>
<li>Events: <pre id="outevt"></pre>
</ol>
- </div>
+ </div>
diff --git a/htdocs/audiohl-demo.html b/htdocs/audiohl-demo.html
new file mode 100644
index 0000000..e222707
--- /dev/null
+++ b/htdocs/audiohl-demo.html
@@ -0,0 +1,47 @@
+<html>
+<head>
+ <link rel="stylesheet" href="AudioBinding.css">
+ <title>Audio High Level Test</title>
+
+ <script type="text/javascript" src="AFB-websock.js"></script>
+ <script type="text/javascript" src="AudioBinding.js"></script>
+</head>
+
+<body onload="init('audiohl')">
+
+ <button id="connected" onclick="init('audiohl');">Binder WS Fail</button>
+
+ <li>Use Case 1: Automatic EndPoint Selection</li>
+ <br>
+ <ol>
+ <li><button onclick="callbinder('audiohl','stream_open', {audio_role:4,endpoint_type:1})">stream_open('Entertainment','Sink')</button></li>
+ <li><button onclick="callbinder('audiohl','set_endpoint_volume', {endpoint_type:1,endpoint_id:2,volume:'10+'})">set_endpoint_volume('Sink',2,'10+')</button></li>
+ <li><button onclick="callbinder('audiohl','stream_close', {stream_id:1})">stream_close(streamID)</button></li>
+ <br>
+ </ol>
+
+ <li>Use Case 2: User Select EndPoint</li>
+ <br>
+ <ol>
+ <li><button onclick="callbinder('audiohl','get_sinks', {audio_role:4})">get_sinks('Entertainment')</button></li>
+ <li><button onclick="callbinder('audiohl','stream_open', {audio_role:4,endpoint_type:1,endpoint_id:10})">stream_open('Entertainment','Sink',sinkID)</button></li>
+ <li><button onclick="callbinder('audiohl','post_sound_event', {event_name:'PlaySound',media_name:'HomeButton.wav'})">post_sound_event('PlaySound','HomeButton.wav')</button></li>
+ <li><button onclick="callbinder('audiohl','stream_close', {stream_id:1})">stream_close(streamID)</button></li>
+ <br>
+ </ol>
+
+ <li>Use Case 3: Select Routing</li>
+ <br>
+ <ol>
+ <li><button onclick="callbinder('audiohl','get_available_routings', {audio_role:4})">get_available_routings('Entertainment')</button></li>
+ <li><button onclick="callbinder('audiohl','add_routing', {audio_role:4,routing_id:2})">add_routing('Entertainment',routingID)</button></li>
+ <br>
+ </ol>
+
+ <div id="main" style="visibility:hidden">
+ <ol>
+ <li>Question <pre id="question"></pre>
+ <li>Response <pre id="output"></pre>
+ <li>Events: <pre id="outevt"></pre>
+ </ol>
+ </div>
diff --git a/htdocs/audiohl.html b/htdocs/audiohl.html
index 7cca6d8..31a28fe 100644
--- a/htdocs/audiohl.html
+++ b/htdocs/audiohl.html
@@ -11,31 +11,124 @@
<button id="connected" onclick="init('audiohl');">Binder WS Fail</button>
+ <b>Audio Role</b>
+ <select select_id='audiorole_list' onclick='ar=this.value'>
+ <option selected value=''>Select...</option>
+ <option value='warning' >Warning</option>
+ <option value='guidance'>Guidance</option>
+ <option value='notification'>Notification</option>
+ <option value='communication'>Communication</option>
+ <option value='entertainment'>Entertainment</option>
+ <option value='system'>System</option>
+ <option value='startup'>Startup</option>
+ <option value='shutdown'>Shutdown</option>
+ </select>
+ <b>Endpoint Type</b>
+ <select select_id='endpoint_type_list' onclick='ep_type=this.selectedIndex'>
+ <option value=0>Source</option>
+ <option value=1>Sink</option>
+ <option selected value=''>Select...</option>
+ </select>
+ <b>Endpoint ID</b>
+ <select select_id='endpoint_id_list' onclick='ep_id=this.selectedIndex'>
+ <option value='0'>0</option>
+ <option value='1'>1</option>
+ <option value='2'>2</option>
+ <option value='3'>3</option>
+ <option value='4'>4</option>
+ <option value='5'>5</option>
+ <option value='6'>6</option>
+ <option value='7'>7</option>
+ <option value='8'>8</option>
+ <option value='9'>9</option>
+ <option value='10'>10</option>
+ <option value='11'>11</option>
+ <option value='12'>12</option>
+ <option value='13'>13</option>
+ <option value='14'>14</option>
+ <option value='15'>15</option>
+ <option value='16'>16</option>
+ <option value='17'>17</option>
+ <option value='18'>18</option>
+ <option value='19'>19</option>
+ <option selected value=''>Select...</option>
+ </select>
+ <b>Stream ID</b>
+ <select select_id='stream_id_list' onclick='s_id=this.selectedIndex'>
+ <option value='0'>0</option>
+ <option value='1'>1</option>
+ <option value='2'>2</option>
+ <option value='3'>3</option>
+ <option value='4'>4</option>
+ <option value='5'>5</option>
+ <option value='6'>6</option>
+ <option value='7'>7</option>
+ <option value='8'>8</option>
+ <option value='9'>9</option>
+ <option value='10'>10</option>
+ <option value='11'>11</option>
+ <option value='12'>12</option>
+ <option value='13'>13</option>
+ <option value='14'>14</option>
+ <option value='15'>15</option>
+ <option value='16'>16</option>
+ <option value='17'>17</option>
+ <option value='18'>18</option>
+ <option value='19'>19</option>
+ <option selected value=''>Select...</option>
+ </select>
+ <b>Property</b>
+ <select select_id='property_name_list' onclick='p_name=this.value'>
+ <option value='balance'>Balance</option>
+ <option value='fade'>Fade</option>
+ <option value='eq_low'>EQ Low</option>
+ <option value='eq_mid'>EQ Mid</option>
+ <option value='eq_high'>EQ High</option>
+ <option selected value=''>Select...</option>
+ </select>
+
+ <b>State Name</b>
+ <select select_id='state_name_list' onclick='st_name=this.value'>
+ <option value='mute'>Mute</option>
+ <option value='active'>Active</option>
+ <option selected value=''>Select...</option>
+ </select>
+
+ <b>State Value</b>
+ <select select_id='state_value_list' onclick='st_val=this.value'>
+ <option value='on'>On</option>
+ <option value='off'>Off</option>
+ <option selected value=''>Select...</option>
+ </select>
+ <b>Volume Value</b>
+ <select select_id='value_list' onclick='val=this.value'>
+ <option value='0'>0</option>
+ <option value='20'>20</option>
+ <option value='40'>40</option>
+ <option value='60'>60</option>
+ <option value='80'>80</option>
+ <option value='100'>100</option>
+ <option selected value=''>Select...</option>
+ </select>
+
<br>
<ol>
- <li><button onclick="callbinder('audiohl','get_sources', {})">get_sources {}</button></li>
- <li><button onclick="callbinder('audiohl','get_sources', {audio_role:4})">get_sources('Entertainment')</button></li>
- <li><button onclick="callbinder('audiohl','get_sinks', {})">get_sinks</button></li>
- <li><button onclick="callbinder('audiohl','get_sinks', {audio_role:4})">get_sinks('Entertainment')</button></li>
- <li><button onclick="callbinder('audiohl','stream_open', {audio_role:4,endpoint_type:1})">stream_open('Entertainment','Sink')</button></li>
- <li><button onclick="callbinder('audiohl','stream_open', {audio_role:4,endpoint_type:1,endpoint_id:0})">stream_open('Entertainment','Sink',sinkID)</button></li>
- <li><button onclick="callbinder('audiohl','stream_close', {stream_id:3})">stream_close(streamID)</button></li>
- <li><button onclick="callbinder('audiohl','get_available_routings', {})">get_available_routings</button></li>
- <li><button onclick="callbinder('audiohl','get_available_routings', {audio_role:4})">get_available_routings('Entertainment')</button></li>
- <li><button onclick="callbinder('audiohl','add_routing', {audio_role:4})">add_routing('Entertainment')</button></li>
- <li><button onclick="callbinder('audiohl','add_routing', {audio_role:4,routing_id:2})">add_routing('Entertainment',routingID)</button></li>
- <li><button onclick="callbinder('audiohl','remove_routing', {routing_id:3})">remove_routing(routingID)</button></li>
- <li><button onclick="callbinder('audiohl','set_endpoint_volume', {endpoint_type:1,endpoint_id:1,volume:'10+'})">set_endpoint_volume('Sink',sinkID,'10+')</button></li>
- <li><button onclick="callbinder('audiohl','set_endpoint_volume', {endpoint_type:1,endpoint_id:1,volume:'50%',ramp_time_ms:50})">set_endpoint_volume('Sink',sinkID,'50+',50)</button></li>
- <li><button onclick="callbinder('audiohl','get_endpoint_volume', {endpoint_type:1,endpoint_id:1})">get_endpoint_volume('Sink',sinkID)</button></li>
- <li><button onclick="callbinder('audiohl','set_endpoint_property', {endpoint_type:1,endpoint_id:1,property_name:'balance',value:'75'})">set_endpoint_property('Sink',sinkID,'balance','75')</button></li>
- <li><button onclick="callbinder('audiohl','set_endpoint_property', {endpoint_type:1,endpoint_id:1,property_name:'balance',value:'75',ramp_time_ms:100})">set_endpoint_property('Sink',sinkID,'balance','75',100)</button></li>
- <li><button onclick="callbinder('audiohl','get_endpoint_property', {endpoint_type:1,endpoint_id:1,property_name:'balance'})">get_endpoint_property('Sink',sinkID,'balance')</button></li>
- <li><button onclick="callbinder('audiohl','set_endpoint_state', {endpoint_type:1,endpoint_id:1,state_name:'mute',state_value:'true'})">set_endpoint_state('Sink',sinkID,'mute','true')</button></li>
- <li><button onclick="callbinder('audiohl','get_endpoint_state', {endpoint_type:1,endpoint_id:1,state_name:'suspended'})">get_endpoint_state('Sink',sinkID,'suspended')</button></li>
- <li><button onclick="callbinder('audiohl','post_sound_event', {event_name:'PlaySound',media_name:'HomeButton.wav'})">post_sound_event('PlaySound','HomeButton.wav')</button></li>
+ <li><button onclick="callbinder('audiohl','get_sources', {audio_role:ar})">get_sources(audio_role)</button></li>
+ <li><button onclick="callbinder('audiohl','get_sinks', {audio_role:ar})">get_sinks(audio_role)</button></li>
+ <li><button onclick="callbinder('audiohl','stream_open', {audio_role:ar,endpoint_type:ep_type})">stream_open(audio_role,endpoint_type)</button></li>
+ <li><button onclick="callbinder('audiohl','stream_open', {audio_role:ar,endpoint_type:ep_type,endpoint_id:ep_id})">stream_open(audio_role,enpoint_type,endpoint_id)</button></li>
+ <li><button onclick="callbinder('audiohl','stream_close', {stream_id:s_id})">stream_close(stream_id)</button></li>
+ <li><button onclick="callbinder('audiohl','set_volume', {endpoint_type:ep_type,endpoint_id:ep_id,volume:val})">set_volume(endpoint_type,endpoint_id,value)</button></li>
+ <li><button onclick="callbinder('audiohl','get_volume', {endpoint_type:ep_type,endpoint_id:ep_id})">get_volume(endpoint_type,endpoint_id)</button></li>
+ <li><button onclick="callbinder('audiohl','set_property', {endpoint_type:ep_type,endpoint_id:ep_id,property_name:p_name,value:val})">set_property(endpoint_type,endpoint_id,property,value)</button></li>
+ <li><button onclick="callbinder('audiohl','get_property', {endpoint_type:ep_type,endpoint_id:ep_id,property_name:'balance'})">get_property(endpoint_type,endpoint_id,'balance')</button></li>
+ <li><button onclick="callbinder('audiohl','set_state', {endpoint_type:ep_type,endpoint_id:ep_id,state_name:st_name,state_value:st_val})">set_state(endpoint_type,endpoint_id,stateName,stateVal)</button></li>
+ <li><button onclick="callbinder('audiohl','get_state', {endpoint_type:ep_type,endpoint_id:ep_id,state_name:st_name})">get_state(endpoint_type,endpoint_id,stateName)</button></li>
+ <li><button onclick="callbinder('audiohl','post_sound_event', {event_name:'PlaySound',audio_role:'notification',media_name:'HomeButton.wav'})">post_sound_event('PlaySound','HomeButton.wav')</button></li>
<li><button onclick="callbinder('audiohl','subscribe', {events:['SinkDeviceChanges','StreamStatusChanges']})">subscribe(['SinkDeviceChanges','StreamStatusChanges'])</button>
+ <li><button onclick="callbinder('audiohl','unsubscribe', {events:['SinkDeviceChanges','StreamStatusChanges']})">unsubscribe(['SinkDeviceChanges','StreamStatusChanges'])</button>
</li>
+
<br>
</ol>
diff --git a/htdocs/index.html b/htdocs/index.html
index 9c645cf..3c43f9b 100644
--- a/htdocs/index.html
+++ b/htdocs/index.html
@@ -7,4 +7,5 @@
<li><a href="alsa-core.html">AlsaCore Low Level Binding</a>
<li><a href="alsa-hal.html" >AlsaHAL Hardware Abstraction Layer</a>
<li><a href="audio-control.html">AudioControl Control/Policy API</a>
- <li><a href="audiohl.html">High Level Audio API</a>
+ <li><a href="audiohl.html">High Level Audio API</a>
+ <li><a href="audiohl-demo.html">High Level Audio API Demo</a>
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index cbfe5dc..c0f6ca9 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -27,11 +27,19 @@ macro(SET_TARGET_GENSKEL TARGET_NAME API_DEF_NAME)
endmacro(SET_TARGET_GENSKEL)
+FIND_PACKAGE(PkgConfig REQUIRED)
+PKG_CHECK_MODULES(GLIB_PKG REQUIRED glib-2.0)
+
+# get_cmake_property(_variableNames VARIABLES)
+# foreach (_variableName ${_variableNames})
+# message(STATUS "${_variableName}=${${_variableName}}")
+# endforeach()
+
# Add target to project dependency list
-PROJECT_TARGET_ADD(audiohighlevel-afb)
+PROJECT_TARGET_ADD(audiohighlevel)
# Define project Targets
- ADD_LIBRARY(${TARGET_NAME} MODULE ahl-binding.c ahl-deviceenum.c)
+ ADD_LIBRARY(${TARGET_NAME} MODULE ahl-binding.c ahl-deviceenum.c ahl-config.c ahl-policy.c)
# Generate API-v2 hat from OpenAPI json definition
SET_TARGET_GENSKEL(${TARGET_NAME} ahl-apidef)
@@ -44,9 +52,16 @@ PROJECT_TARGET_ADD(audiohighlevel-afb)
OUTPUT_NAME ${TARGET_NAME}
)
+ # Define target includes
+ TARGET_INCLUDE_DIRECTORIES(${TARGET_NAME}
+ PUBLIC ${GLIB_PKG_INCLUDE_DIRS}
+ )
+
# Library dependencies (include updates automatically)
+ # Find package for GLIB does not seem to export
TARGET_LINK_LIBRARIES(${TARGET_NAME}
- afb-utilities
+ afb-utilities
+ ${GLIB_PKG_LIBRARIES}
${link_libraries}
)
diff --git a/src/ahl-apidef.h b/src/ahl-apidef.h
index fa78681..9a39470 100644
--- a/src/ahl-apidef.h
+++ b/src/ahl-apidef.h
@@ -22,140 +22,118 @@ static const char _afb_description_v2_audiohl[] =
"\"],\"properties\":{\"jtype\":{\"type\":\"string\",\"const\":\"afb-event"
"\"},\"event\":{\"type\":\"string\"},\"data\":{\"type\":\"object\"}}},\"e"
"ndpoint_info\":{\"type\":\"object\",\"required\":[\"endpoint_id\",\"type"
- "\",\"name\"],\"properties\":{\"endpoint_id\":{\"type\":\"int\"},\"type\""
- ":{\"type\":\"enum\"},\"name\":{\"type\":\"string\"}}},\"stream_info\":{\""
- "type\":\"object\",\"required\":[\"stream_id\",\"pcm_name\",\"name\"],\"p"
- "roperties\":{\"stream_id\":{\"type\":\"int\"},\"pcm_name\":{\"type\":\"s"
- "tring\"},\"$ref\":\"#/components/schemas/endpoint_info\"}},\"routing_inf"
- "o\":{\"type\":\"object\",\"required\":[\"routing_id\",\"source_id\",\"si"
- "nk_id\"],\"properties\":{\"routing_id\":{\"type\":\"int\"},\"source_id\""
- ":{\"type\":\"int\"},\"sink_id\":{\"type\":\"int\"}}}},\"x-permissions\":"
- "{\"streamcontrol\":{\"permission\":\"urn:AGL:permission:audio:public:str"
- "eamcontrol\"},\"routingcontrol\":{\"permission\":\"urn:AGL:permission:au"
- "dio:public:routingcontrol\"},\"soundevent\":{\"permission\":\"urn:AGL:pe"
- "rmission:audio:public:soundevent\"}},\"responses\":{\"200\":{\"descripti"
- "on\":\"A complex object array response\",\"content\":{\"application/json"
- "\":{\"schema\":{\"$ref\":\"#/components/schemas/afb-reply\"}}}},\"400\":"
- "{\"description\":\"Invalid arguments\"}}},\"paths\":{\"/get_sources\":{\""
- "description\":\"Retrieve array of available audio sources\",\"get\":{\"p"
- "arameters\":[{\"in\":\"query\",\"name\":\"audio_role\",\"required\":fals"
- "e,\"schema\":{\"type\":\"enum\"}}],\"responses\":{\"200\":{\"$ref\":\"#/"
- "components/responses/200\",\"response\":{\"description\":\"Array of endp"
- "oint info structures\",\"type\":\"array\",\"items\":{\"$ref\":\"#/compon"
- "ents/schemas/endpoint_info\"}}},\"400\":{\"$ref\":\"#/components/respons"
- "es/400\"}}}},\"/get_sinks\":{\"description\":\"Retrieve array of availab"
- "le audio sinks\",\"get\":{\"parameters\":[{\"in\":\"query\",\"name\":\"a"
- "udio_role\",\"required\":false,\"schema\":{\"type\":\"enum\"}}],\"respon"
- "ses\":{\"200\":{\"$ref\":\"#/components/responses/200\",\"response\":{\""
- "description\":\"Array of endpoint info structures\",\"type\":\"array\",\""
- "items\":{\"$ref\":\"#/components/schemas/endpoint_info\"}}},\"400\":{\"$"
- "ref\":\"#/components/responses/400\"}}}},\"/stream_open\":{\"description"
- "\":\"Request opening a stream\",\"get\":{\"x-permissions\":{\"$ref\":\"#"
- "/components/x-permissions/streamcontrol\"},\"parameters\":[{\"in\":\"que"
- "ry\",\"name\":\"audio_role\",\"required\":true,\"schema\":{\"type\":\"en"
- "um\"}},{\"in\":\"query\",\"name\":\"endpoint_type\",\"required\":true,\""
- "schema\":{\"type\":\"enum\"}},{\"in\":\"query\",\"name\":\"endpoint_id\""
- ",\"required\":false,\"schema\":{\"type\":\"int\"}}],\"responses\":{\"200"
- "\":{\"$ref\":\"#/components/responses/200\",\"response\":{\"description\""
- ":\"Stream information structure\",\"$ref\":\"#/components/schemas/stream"
- "_info\"}},\"400\":{\"$ref\":\"#/components/responses/400\"}}}},\"/stream"
- "_close\":{\"description\":\"Request closing a stream\",\"get\":{\"x-perm"
- "issions\":{\"$ref\":\"#/components/x-permissions/streamcontrol\"},\"para"
- "meters\":[{\"in\":\"query\",\"name\":\"stream_id\",\"required\":true,\"s"
- "chema\":{\"type\":\"int\"}}],\"responses\":{\"200\":{\"$ref\":\"#/compon"
- "ents/responses/200\"},\"400\":{\"$ref\":\"#/components/responses/400\"}}"
- "}},\"/get_available_routings\":{\"description\":\"Retrieve array of avai"
- "lable routing info structures\",\"get\":{\"parameters\":[{\"in\":\"query"
- "\",\"name\":\"audio_role\",\"required\":false,\"schema\":{\"type\":\"enu"
- "m\"}}],\"responses\":{\"200\":{\"$ref\":\"#/components/responses/200\",\""
- "response\":{\"description\":\"Array of routing info structures\",\"type\""
- ":\"array\",\"items\":{\"description\":\"Routing info structure {routingI"
- "D, sourceID, sinkID }\",\"type\":\"object\"}}},\"400\":{\"$ref\":\"#/com"
- "ponents/responses/400\"}}}},\"/add_routing\":{\"description\":\"Request "
- "a routing connection between available devices\",\"get\":{\"x-permission"
- "s\":{\"$ref\":\"#/components/x-permissions/routingcontrol\"},\"parameter"
- "s\":[{\"in\":\"query\",\"name\":\"audio_role\",\"required\":true,\"schem"
- "a\":{\"type\":\"enum\"}},{\"in\":\"query\",\"name\":\"routing_id\",\"req"
- "uired\":false,\"schema\":{\"type\":\"int\"}}],\"responses\":{\"200\":{\""
- "$ref\":\"#/components/responses/200\",\"response\":{\"description\":\"Ro"
- "uting information structure\",\"$ref\":\"#/components/schemas/routing_in"
- "fo\"}},\"400\":{\"$ref\":\"#/components/responses/400\"}}}},\"/remove_ro"
- "uting\":{\"description\":\"Request to remove a routing connection betwee"
- "n devices\",\"get\":{\"x-permissions\":{\"$ref\":\"#/components/x-permis"
- "sions/routingcontrol\"},\"parameters\":[{\"in\":\"query\",\"name\":\"rou"
- "ting_id\",\"required\":true,\"schema\":{\"type\":\"int\"}}],\"responses\""
- ":{\"200\":{\"$ref\":\"#/components/responses/200\"},\"400\":{\"$ref\":\""
- "#/components/responses/400\"}}}},\"/set_endpoint_volume\":{\"description"
- "\":\"Set endpoint volume\",\"get\":{\"x-permissions\":{\"$ref\":\"#/comp"
- "onents/x-permissions/streamcontrol\"},\"parameters\":[{\"in\":\"query\","
- "\"name\":\"endpoint_type\",\"required\":true,\"schema\":{\"type\":\"enum"
- "\"}},{\"in\":\"query\",\"name\":\"endpoint_id\",\"required\":true,\"sche"
- "ma\":{\"type\":\"int\"}},{\"in\":\"query\",\"name\":\"volume\",\"require"
- "d\":true,\"schema\":{\"type\":\"string\"}},{\"in\":\"query\",\"name\":\""
- "ramp_time_ms\",\"required\":false,\"schema\":{\"type\":\"int\"}}],\"resp"
- "onses\":{\"200\":{\"$ref\":\"#/components/responses/200\"},\"400\":{\"$r"
- "ef\":\"#/components/responses/400\"}}}},\"/get_endpoint_volume\":{\"desc"
- "ription\":\"Get endpoint volume\",\"get\":{\"parameters\":[{\"in\":\"que"
- "ry\",\"name\":\"endpoint_type\",\"required\":true,\"schema\":{\"type\":\""
- "enum\"}},{\"in\":\"query\",\"name\":\"endpoint_id\",\"required\":true,\""
- "schema\":{\"type\":\"int\"}}],\"responses\":{\"200\":{\"$ref\":\"#/compo"
- "nents/responses/200\",\"response\":{\"description\":\"Endpoint volume va"
- "lue\",\"type\":\"double\"}},\"400\":{\"$ref\":\"#/components/responses/4"
- "00\"}}}},\"/set_endpoint_property\":{\"description\":\"Set endpoint prop"
- "erty value\",\"get\":{\"x-permissions\":{\"$ref\":\"#/components/x-permi"
- "ssions/streamcontrol\"},\"parameters\":[{\"in\":\"query\",\"name\":\"end"
- "point_type\",\"required\":true,\"schema\":{\"type\":\"enum\"}},{\"in\":\""
- "query\",\"name\":\"endpoint_id\",\"required\":false,\"schema\":{\"type\""
- ":\"int\"}},{\"in\":\"query\",\"name\":\"property_name\",\"required\":tru"
- "e,\"schema\":{\"type\":\"string\"}},{\"in\":\"query\",\"name\":\"value\""
- ",\"required\":true,\"schema\":{\"type\":\"string\"}},{\"in\":\"query\",\""
- "name\":\"ramp_time_ms\",\"required\":false,\"schema\":{\"type\":\"int\"}"
- "}],\"responses\":{\"200\":{\"$ref\":\"#/components/responses/200\"},\"40"
- "0\":{\"$ref\":\"#/components/responses/400\"}}}},\"/get_endpoint_propert"
- "y\":{\"description\":\"Get endpoint property value\",\"get\":{\"paramete"
- "rs\":[{\"in\":\"query\",\"name\":\"endpoint_type\",\"required\":true,\"s"
- "chema\":{\"type\":\"enum\"}},{\"in\":\"query\",\"name\":\"endpoint_id\","
- "\"required\":false,\"schema\":{\"type\":\"int\"}},{\"in\":\"query\",\"na"
- "me\":\"property_name\",\"required\":true,\"schema\":{\"type\":\"string\""
+ "\",\"device_name\",\"device_uri\"],\"properties\":{\"endpoint_id\":{\"ty"
+ "pe\":\"int\"},\"type\":{\"type\":\"enum\"},\"device_name\":{\"type\":\"s"
+ "tring\"},\"device_uri_type\":{\"type\":\"string\"}}},\"stream_info\":{\""
+ "type\":\"object\",\"required\":[\"stream_id\",\"endpoint_info\"],\"prope"
+ "rties\":{\"stream_id\":{\"type\":\"int\"},\"$ref\":\"#/components/schema"
+ "s/endpoint_info\"}}},\"x-permissions\":{\"streamcontrol\":{\"permission\""
+ ":\"urn:AGL:permission:audio:public:streamcontrol\"},\"routingcontrol\":{"
+ "\"permission\":\"urn:AGL:permission:audio:public:routingcontrol\"},\"sou"
+ "ndevent\":{\"permission\":\"urn:AGL:permission:audio:public:soundevent\""
+ "}},\"responses\":{\"200\":{\"description\":\"A complex object array resp"
+ "onse\",\"content\":{\"application/json\":{\"schema\":{\"$ref\":\"#/compo"
+ "nents/schemas/afb-reply\"}}}},\"400\":{\"description\":\"Invalid argumen"
+ "ts\"}}},\"paths\":{\"/get_sources\":{\"description\":\"Retrieve array of"
+ " available audio sources\",\"get\":{\"parameters\":[{\"in\":\"query\",\""
+ "name\":\"audio_role\",\"required\":true,\"schema\":{\"type\":\"string\"}"
+ "}],\"responses\":{\"200\":{\"$ref\":\"#/components/responses/200\",\"res"
+ "ponse\":{\"description\":\"Array of endpoint info structures\",\"type\":"
+ "\"array\",\"items\":{\"$ref\":\"#/components/schemas/endpoint_info\"}}},"
+ "\"400\":{\"$ref\":\"#/components/responses/400\"}}}},\"/get_sinks\":{\"d"
+ "escription\":\"Retrieve array of available audio sinks\",\"get\":{\"para"
+ "meters\":[{\"in\":\"query\",\"name\":\"audio_role\",\"required\":true,\""
+ "schema\":{\"type\":\"string\"}}],\"responses\":{\"200\":{\"$ref\":\"#/co"
+ "mponents/responses/200\",\"response\":{\"description\":\"Array of endpoi"
+ "nt info structures\",\"type\":\"array\",\"items\":{\"$ref\":\"#/componen"
+ "ts/schemas/endpoint_info\"}}},\"400\":{\"$ref\":\"#/components/responses"
+ "/400\"}}}},\"/stream_open\":{\"description\":\"Request opening a stream\""
+ ",\"get\":{\"x-permissions\":{\"$ref\":\"#/components/x-permissions/strea"
+ "mcontrol\"},\"parameters\":[{\"in\":\"query\",\"name\":\"audio_role\",\""
+ "required\":true,\"schema\":{\"type\":\"string\"}},{\"in\":\"query\",\"na"
+ "me\":\"endpoint_type\",\"required\":true,\"schema\":{\"type\":\"enum\"}}"
+ ",{\"in\":\"query\",\"name\":\"endpoint_id\",\"required\":false,\"schema\""
+ ":{\"type\":\"int\"}}],\"responses\":{\"200\":{\"$ref\":\"#/components/re"
+ "sponses/200\",\"response\":{\"description\":\"Stream information structu"
+ "re\",\"$ref\":\"#/components/schemas/stream_info\"}},\"400\":{\"$ref\":\""
+ "#/components/responses/400\"}}}},\"/stream_close\":{\"description\":\"Re"
+ "quest closing a stream\",\"get\":{\"x-permissions\":{\"$ref\":\"#/compon"
+ "ents/x-permissions/streamcontrol\"},\"parameters\":[{\"in\":\"query\",\""
+ "name\":\"stream_id\",\"required\":true,\"schema\":{\"type\":\"int\"}}],\""
+ "responses\":{\"200\":{\"$ref\":\"#/components/responses/200\"},\"400\":{"
+ "\"$ref\":\"#/components/responses/400\"}}}},\"/set_volume\":{\"descripti"
+ "on\":\"Set volume\",\"get\":{\"x-permissions\":{\"$ref\":\"#/components/"
+ "x-permissions/streamcontrol\"},\"parameters\":[{\"in\":\"query\",\"name\""
+ ":\"endpoint_type\",\"required\":true,\"schema\":{\"type\":\"enum\"}},{\""
+ "in\":\"query\",\"name\":\"endpoint_id\",\"required\":true,\"schema\":{\""
+ "type\":\"int\"}},{\"in\":\"query\",\"name\":\"volume\",\"required\":true"
+ ",\"schema\":{\"type\":\"string\"}},{\"in\":\"query\",\"name\":\"ramp_tim"
+ "e_ms\",\"required\":false,\"schema\":{\"type\":\"int\"}}],\"responses\":"
+ "{\"200\":{\"$ref\":\"#/components/responses/200\"},\"400\":{\"$ref\":\"#"
+ "/components/responses/400\"}}}},\"/get_volume\":{\"description\":\"Get v"
+ "olume\",\"get\":{\"parameters\":[{\"in\":\"query\",\"name\":\"endpoint_t"
+ "ype\",\"required\":true,\"schema\":{\"type\":\"enum\"}},{\"in\":\"query\""
+ ",\"name\":\"endpoint_id\",\"required\":true,\"schema\":{\"type\":\"int\""
"}}],\"responses\":{\"200\":{\"$ref\":\"#/components/responses/200\",\"re"
- "sponse\":{\"description\":\"Property value\",\"type\":\"double\"}},\"400"
- "\":{\"$ref\":\"#/components/responses/400\"}}}},\"/set_endpoint_state\":"
- "{\"description\":\"Set endpoint state\",\"get\":{\"x-permissions\":{\"$r"
- "ef\":\"#/components/x-permissions/streamcontrol\"},\"parameters\":[{\"in"
- "\":\"query\",\"name\":\"endpoint_type\",\"required\":true,\"schema\":{\""
+ "sponse\":{\"description\":\"Endpoint volume value\",\"type\":\"double\"}"
+ "},\"400\":{\"$ref\":\"#/components/responses/400\"}}}},\"/set_property\""
+ ":{\"description\":\"Set property value\",\"get\":{\"x-permissions\":{\"$"
+ "ref\":\"#/components/x-permissions/streamcontrol\"},\"parameters\":[{\"i"
+ "n\":\"query\",\"name\":\"endpoint_type\",\"required\":true,\"schema\":{\""
"type\":\"enum\"}},{\"in\":\"query\",\"name\":\"endpoint_id\",\"required\""
- ":true,\"schema\":{\"type\":\"int\"}},{\"in\":\"query\",\"name\":\"state_"
- "name\",\"required\":true,\"schema\":{\"type\":\"string\"}},{\"in\":\"que"
- "ry\",\"name\":\"state_value\",\"required\":true,\"schema\":{\"type\":\"s"
- "tring\"}}],\"responses\":{\"200\":{\"$ref\":\"#/components/responses/200"
- "\"},\"400\":{\"$ref\":\"#/components/responses/400\"}}}},\"/get_endpoint"
- "_state\":{\"description\":\"Get endpoint state value\",\"get\":{\"parame"
- "ters\":[{\"in\":\"query\",\"name\":\"endpoint_type\",\"required\":true,\""
- "schema\":{\"type\":\"enum\"}},{\"in\":\"query\",\"name\":\"endpoint_id\""
- ",\"required\":true,\"schema\":{\"type\":\"int\"}},{\"in\":\"query\",\"na"
- "me\":\"state_name\",\"required\":true,\"schema\":{\"type\":\"string\"}}]"
- ",\"responses\":{\"200\":{\"$ref\":\"#/components/responses/200\",\"respo"
- "nse\":{\"description\":\"Endpoint state value\",\"type\":\"string\"}},\""
- "400\":{\"$ref\":\"#/components/responses/400\"}}}},\"/post_sound_event\""
- ":{\"description\":\"Post sound event\",\"get\":{\"x-permissions\":{\"$re"
- "f\":\"#/components/x-permissions/soundevent\"},\"parameters\":[{\"in\":\""
- "query\",\"name\":\"event_name\",\"required\":true,\"schema\":{\"type\":\""
- "string\"}},{\"in\":\"query\",\"name\":\"audio_role\",\"required\":false,"
- "\"schema\":{\"type\":\"enum\"}},{\"in\":\"query\",\"name\":\"media_name\""
- ",\"required\":false,\"schema\":{\"type\":\"string\"}},{\"in\":\"query\","
- "\"name\":\"audio_context\",\"required\":false,\"schema\":{\"type\":\"obj"
- "ect\"}}],\"responses\":{\"200\":{\"$ref\":\"#/components/responses/200\""
- "},\"400\":{\"$ref\":\"#/components/responses/400\"}}}},\"/subscribe\":{\""
- "description\":\"Subscribe to audio high level events\",\"get\":{\"parame"
- "ters\":[{\"in\":\"query\",\"name\":\"events\",\"required\":true,\"schema"
- "\":{\"type\":\"array\",\"items\":{\"type\":\"string\"}}}],\"responses\":"
+ ":false,\"schema\":{\"type\":\"int\"}},{\"in\":\"query\",\"name\":\"prope"
+ "rty_name\",\"required\":true,\"schema\":{\"type\":\"string\"}},{\"in\":\""
+ "query\",\"name\":\"value\",\"required\":true,\"schema\":{\"type\":\"stri"
+ "ng\"}},{\"in\":\"query\",\"name\":\"ramp_time_ms\",\"required\":false,\""
+ "schema\":{\"type\":\"int\"}}],\"responses\":{\"200\":{\"$ref\":\"#/compo"
+ "nents/responses/200\"},\"400\":{\"$ref\":\"#/components/responses/400\"}"
+ "}}},\"/get_property\":{\"description\":\"Get property value\",\"get\":{\""
+ "parameters\":[{\"in\":\"query\",\"name\":\"endpoint_type\",\"required\":"
+ "true,\"schema\":{\"type\":\"enum\"}},{\"in\":\"query\",\"name\":\"endpoi"
+ "nt_id\",\"required\":false,\"schema\":{\"type\":\"int\"}},{\"in\":\"quer"
+ "y\",\"name\":\"property_name\",\"required\":true,\"schema\":{\"type\":\""
+ "string\"}}],\"responses\":{\"200\":{\"$ref\":\"#/components/responses/20"
+ "0\",\"response\":{\"description\":\"Property value\",\"type\":\"double\""
+ "}},\"400\":{\"$ref\":\"#/components/responses/400\"}}}},\"/set_state\":{"
+ "\"description\":\"Set state\",\"get\":{\"x-permissions\":{\"$ref\":\"#/c"
+ "omponents/x-permissions/streamcontrol\"},\"parameters\":[{\"in\":\"query"
+ "\",\"name\":\"endpoint_type\",\"required\":true,\"schema\":{\"type\":\"e"
+ "num\"}},{\"in\":\"query\",\"name\":\"endpoint_id\",\"required\":true,\"s"
+ "chema\":{\"type\":\"int\"}},{\"in\":\"query\",\"name\":\"state_name\",\""
+ "required\":true,\"schema\":{\"type\":\"string\"}},{\"in\":\"query\",\"na"
+ "me\":\"state_value\",\"required\":true,\"schema\":{\"type\":\"string\"}}"
+ "],\"responses\":{\"200\":{\"$ref\":\"#/components/responses/200\"},\"400"
+ "\":{\"$ref\":\"#/components/responses/400\"}}}},\"/get_state\":{\"descri"
+ "ption\":\"Get state value\",\"get\":{\"parameters\":[{\"in\":\"query\",\""
+ "name\":\"endpoint_type\",\"required\":true,\"schema\":{\"type\":\"enum\""
+ "}},{\"in\":\"query\",\"name\":\"endpoint_id\",\"required\":true,\"schema"
+ "\":{\"type\":\"int\"}},{\"in\":\"query\",\"name\":\"state_name\",\"requi"
+ "red\":true,\"schema\":{\"type\":\"string\"}}],\"responses\":{\"200\":{\""
+ "$ref\":\"#/components/responses/200\",\"response\":{\"description\":\"En"
+ "dpoint state value\",\"type\":\"string\"}},\"400\":{\"$ref\":\"#/compone"
+ "nts/responses/400\"}}}},\"/post_sound_event\":{\"description\":\"Post so"
+ "und event\",\"get\":{\"x-permissions\":{\"$ref\":\"#/components/x-permis"
+ "sions/soundevent\"},\"parameters\":[{\"in\":\"query\",\"name\":\"event_n"
+ "ame\",\"required\":true,\"schema\":{\"type\":\"string\"}},{\"in\":\"quer"
+ "y\",\"name\":\"audio_role\",\"required\":true,\"schema\":{\"type\":\"str"
+ "ing\"}},{\"in\":\"query\",\"name\":\"media_name\",\"required\":false,\"s"
+ "chema\":{\"type\":\"string\"}},{\"in\":\"query\",\"name\":\"audio_contex"
+ "t\",\"required\":false,\"schema\":{\"type\":\"object\"}}],\"responses\":"
"{\"200\":{\"$ref\":\"#/components/responses/200\"},\"400\":{\"$ref\":\"#"
- "/components/responses/400\"}}}}}}"
+ "/components/responses/400\"}}}},\"/subscribe\":{\"description\":\"Subscr"
+ "ibe to audio high level events\",\"get\":{\"parameters\":[{\"in\":\"quer"
+ "y\",\"name\":\"events\",\"required\":true,\"schema\":{\"type\":\"array\""
+ ",\"items\":{\"type\":\"string\"}}}],\"responses\":{\"200\":{\"$ref\":\"#"
+ "/components/responses/200\"},\"400\":{\"$ref\":\"#/components/responses/"
+ "400\"}}}},\"/unsubscribe\":{\"description\":\"Unubscribe to audio high l"
+ "evel events\",\"get\":{\"parameters\":[{\"in\":\"query\",\"name\":\"even"
+ "ts\",\"required\":true,\"schema\":{\"type\":\"array\",\"items\":{\"type\""
+ ":\"string\"}}}],\"responses\":{\"200\":{\"$ref\":\"#/components/response"
+ "s/200\"},\"400\":{\"$ref\":\"#/components/responses/400\"}}}}}}"
;
static const struct afb_auth _afb_auths_v2_audiohl[] = {
{ .type = afb_auth_Permission, .text = "urn:AGL:permission:audio:public:streamcontrol" },
- { .type = afb_auth_Permission, .text = "urn:AGL:permission:audio:public:routingcontrol" },
{ .type = afb_auth_Permission, .text = "urn:AGL:permission:audio:public:soundevent" }
};
@@ -163,131 +141,121 @@ static const struct afb_auth _afb_auths_v2_audiohl[] = {
void audiohlapi_get_sinks(struct afb_req req);
void audiohlapi_stream_open(struct afb_req req);
void audiohlapi_stream_close(struct afb_req req);
- void audiohlapi_get_available_routings(struct afb_req req);
- void audiohlapi_add_routing(struct afb_req req);
- void audiohlapi_remove_routing(struct afb_req req);
- void audiohlapi_set_endpoint_volume(struct afb_req req);
- void audiohlapi_get_endpoint_volume(struct afb_req req);
- void audiohlapi_set_endpoint_property(struct afb_req req);
- void audiohlapi_get_endpoint_property(struct afb_req req);
- void audiohlapi_set_endpoint_state(struct afb_req req);
- void audiohlapi_get_endpoint_state(struct afb_req req);
+ void audiohlapi_set_volume(struct afb_req req);
+ void audiohlapi_get_volume(struct afb_req req);
+ void audiohlapi_set_property(struct afb_req req);
+ void audiohlapi_get_property(struct afb_req req);
+ void audiohlapi_set_state(struct afb_req req);
+ void audiohlapi_get_state(struct afb_req req);
void audiohlapi_post_sound_event(struct afb_req req);
void audiohlapi_subscribe(struct afb_req req);
+ void audiohlapi_unsubscribe(struct afb_req req);
static const struct afb_verb_v2 _afb_verbs_v2_audiohl[] = {
{
.verb = "get_sources",
.callback = audiohlapi_get_sources,
.auth = NULL,
- .info = NULL,
+ .info = "Retrieve array of available audio sources",
.session = AFB_SESSION_NONE_V2
},
{
.verb = "get_sinks",
.callback = audiohlapi_get_sinks,
.auth = NULL,
- .info = NULL,
+ .info = "Retrieve array of available audio sinks",
.session = AFB_SESSION_NONE_V2
},
{
.verb = "stream_open",
.callback = audiohlapi_stream_open,
.auth = &_afb_auths_v2_audiohl[0],
- .info = NULL,
+ .info = "Request opening a stream",
.session = AFB_SESSION_NONE_V2
},
{
.verb = "stream_close",
.callback = audiohlapi_stream_close,
.auth = &_afb_auths_v2_audiohl[0],
- .info = NULL,
- .session = AFB_SESSION_NONE_V2
- },
- {
- .verb = "get_available_routings",
- .callback = audiohlapi_get_available_routings,
- .auth = NULL,
- .info = NULL,
- .session = AFB_SESSION_NONE_V2
- },
- {
- .verb = "add_routing",
- .callback = audiohlapi_add_routing,
- .auth = &_afb_auths_v2_audiohl[1],
- .info = NULL,
- .session = AFB_SESSION_NONE_V2
- },
- {
- .verb = "remove_routing",
- .callback = audiohlapi_remove_routing,
- .auth = &_afb_auths_v2_audiohl[1],
- .info = NULL,
+ .info = "Request closing a stream",
.session = AFB_SESSION_NONE_V2
},
{
- .verb = "set_endpoint_volume",
- .callback = audiohlapi_set_endpoint_volume,
+ .verb = "set_volume",
+ .callback = audiohlapi_set_volume,
.auth = &_afb_auths_v2_audiohl[0],
- .info = NULL,
+ .info = "Set volume",
.session = AFB_SESSION_NONE_V2
},
{
- .verb = "get_endpoint_volume",
- .callback = audiohlapi_get_endpoint_volume,
+ .verb = "get_volume",
+ .callback = audiohlapi_get_volume,
.auth = NULL,
- .info = NULL,
+ .info = "Get volume",
.session = AFB_SESSION_NONE_V2
},
{
- .verb = "set_endpoint_property",
- .callback = audiohlapi_set_endpoint_property,
+ .verb = "set_property",
+ .callback = audiohlapi_set_property,
.auth = &_afb_auths_v2_audiohl[0],
- .info = NULL,
+ .info = "Set property value",
.session = AFB_SESSION_NONE_V2
},
{
- .verb = "get_endpoint_property",
- .callback = audiohlapi_get_endpoint_property,
+ .verb = "get_property",
+ .callback = audiohlapi_get_property,
.auth = NULL,
- .info = NULL,
+ .info = "Get property value",
.session = AFB_SESSION_NONE_V2
},
{
- .verb = "set_endpoint_state",
- .callback = audiohlapi_set_endpoint_state,
+ .verb = "set_state",
+ .callback = audiohlapi_set_state,
.auth = &_afb_auths_v2_audiohl[0],
- .info = NULL,
+ .info = "Set state",
.session = AFB_SESSION_NONE_V2
},
{
- .verb = "get_endpoint_state",
- .callback = audiohlapi_get_endpoint_state,
+ .verb = "get_state",
+ .callback = audiohlapi_get_state,
.auth = NULL,
- .info = NULL,
+ .info = "Get state value",
.session = AFB_SESSION_NONE_V2
},
{
.verb = "post_sound_event",
.callback = audiohlapi_post_sound_event,
- .auth = &_afb_auths_v2_audiohl[2],
- .info = NULL,
+ .auth = &_afb_auths_v2_audiohl[1],
+ .info = "Post sound event",
.session = AFB_SESSION_NONE_V2
},
{
.verb = "subscribe",
.callback = audiohlapi_subscribe,
.auth = NULL,
- .info = NULL,
+ .info = "Subscribe to audio high level events",
.session = AFB_SESSION_NONE_V2
},
- { .verb = NULL }
+ {
+ .verb = "unsubscribe",
+ .callback = audiohlapi_unsubscribe,
+ .auth = NULL,
+ .info = "Unubscribe to audio high level events",
+ .session = AFB_SESSION_NONE_V2
+ },
+ {
+ .verb = NULL,
+ .callback = NULL,
+ .auth = NULL,
+ .info = NULL,
+ .session = 0
+ }
};
const struct afb_binding_v2 afbBindingV2 = {
.api = "audiohl",
.specification = _afb_description_v2_audiohl,
- .info = NULL,
+ .info = "Audio high level API for AGL applications",
.verbs = _afb_verbs_v2_audiohl,
.preinit = NULL,
.init = AhlBindingInit,
diff --git a/src/ahl-apidef.json b/src/ahl-apidef.json
index cbe25a9..45067ea 100644
--- a/src/ahl-apidef.json
+++ b/src/ahl-apidef.json
@@ -97,30 +97,21 @@
},
"endpoint_info": {
"type": "object",
- "required": [ "endpoint_id", "type", "name" ],
+ "required": [ "endpoint_id", "type", "device_name", "device_uri" ],
"properties": {
"endpoint_id": { "type": "int" },
"type": { "type": "enum" },
- "name": { "type": "string" }
+ "device_name": { "type": "string" },
+ "device_uri_type": { "type": "string" }
}
},
"stream_info": {
"type": "object",
- "required": [ "stream_id", "pcm_name", "name" ],
+ "required": [ "stream_id", "endpoint_info" ],
"properties": {
"stream_id": { "type": "int" },
- "pcm_name": { "type": "string" },
"$ref": "#/components/schemas/endpoint_info"
}
- },
- "routing_info": {
- "type": "object",
- "required": [ "routing_id", "source_id", "sink_id" ],
- "properties": {
- "routing_id": { "type": "int" },
- "source_id": { "type": "int" },
- "sink_id": { "type": "int" }
- }
}
},
"x-permissions": {
@@ -158,8 +149,8 @@
{
"in": "query",
"name": "audio_role",
- "required": false,
- "schema": { "type": "enum" }
+ "required": true,
+ "schema": { "type": "string" }
}
],
"responses": {
@@ -182,8 +173,8 @@
{
"in": "query",
"name": "audio_role",
- "required": false,
- "schema": { "type": "enum" }
+ "required": true,
+ "schema": { "type": "string" }
}
],
"responses": {
@@ -209,7 +200,7 @@
"in": "query",
"name": "audio_role",
"required": true,
- "schema": { "type": "enum" }
+ "schema": { "type": "string" }
},
{
"in": "query",
@@ -254,83 +245,8 @@
}
}
},
- "/get_available_routings": {
- "description": "Retrieve array of available routing info structures",
- "get": {
- "parameters": [
- {
- "in": "query",
- "name": "audio_role",
- "required": false,
- "schema": { "type": "enum" }
- }
- ],
- "responses": {
- "200": {
- "$ref": "#/components/responses/200",
- "response": {
- "description": "Array of routing info structures",
- "type": "array",
- "items": {
- "description": "Routing info structure {routingID, sourceID, sinkID }",
- "type": "object"
- }
- }
- },
- "400": { "$ref": "#/components/responses/400" }
- }
- }
- },
- "/add_routing": {
- "description": "Request a routing connection between available devices",
- "get": {
- "x-permissions": { "$ref": "#/components/x-permissions/routingcontrol" },
- "parameters": [
- {
- "in": "query",
- "name": "audio_role",
- "required": true,
- "schema": { "type": "enum" }
- },
- {
- "in": "query",
- "name": "routing_id",
- "required": false,
- "schema": { "type": "int" }
- }
- ],
- "responses": {
- "200": {
- "$ref": "#/components/responses/200",
- "response": {
- "description": "Routing information structure",
- "$ref": "#/components/schemas/routing_info"
- }
- },
- "400": { "$ref": "#/components/responses/400" }
- }
- }
- },
- "/remove_routing": {
- "description": "Request to remove a routing connection between devices",
- "get": {
- "x-permissions": { "$ref": "#/components/x-permissions/routingcontrol" },
- "parameters": [
- {
- "in": "query",
- "name": "routing_id",
- "required": true,
- "schema": { "type": "int" }
- }
- ],
- "responses": {
- "200": { "$ref": "#/components/responses/200" },
- "400": { "$ref": "#/components/responses/400" }
- }
- }
- },
- "/set_endpoint_volume": {
- "description": "Set endpoint volume",
+ "/set_volume": {
+ "description": "Set volume",
"get": {
"x-permissions": { "$ref": "#/components/x-permissions/streamcontrol" },
"parameters": [
@@ -365,8 +281,8 @@
}
}
},
- "/get_endpoint_volume": {
- "description": "Get endpoint volume",
+ "/get_volume": {
+ "description": "Get volume",
"get": {
"parameters": [
{
@@ -394,8 +310,8 @@
}
}
},
- "/set_endpoint_property": {
- "description": "Set endpoint property value",
+ "/set_property": {
+ "description": "Set property value",
"get": {
"x-permissions": { "$ref": "#/components/x-permissions/streamcontrol" },
"parameters": [
@@ -436,8 +352,8 @@
}
}
},
- "/get_endpoint_property": {
- "description": "Get endpoint property value",
+ "/get_property": {
+ "description": "Get property value",
"get": {
"parameters": [
{
@@ -471,8 +387,8 @@
}
}
},
- "/set_endpoint_state": {
- "description": "Set endpoint state",
+ "/set_state": {
+ "description": "Set state",
"get": {
"x-permissions": { "$ref": "#/components/x-permissions/streamcontrol" },
"parameters": [
@@ -507,8 +423,8 @@
}
}
},
- "/get_endpoint_state": {
- "description": "Get endpoint state value",
+ "/get_state": {
+ "description": "Get state value",
"get": {
"parameters": [
{
@@ -556,8 +472,8 @@
{
"in": "query",
"name": "audio_role",
- "required": false,
- "schema": { "type": "enum" }
+ "required": true,
+ "schema": { "type": "string" }
},
{
"in": "query",
@@ -596,6 +512,32 @@
"400": { "$ref": "#/components/responses/400" }
}
}
+ },
+ "/unsubscribe": {
+ "description": "Unubscribe to audio high level events",
+ "get": {
+ "parameters": [
+ {
+ "in": "query",
+ "name": "events",
+ "required": true,
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "$ref": "#/components/responses/200"
+ },
+ "400": {
+ "$ref": "#/components/responses/400"
+ }
+ }
+ }
}
}
}
diff --git a/src/ahl-binding.c b/src/ahl-binding.c
index 99236bd..4b075c0 100644
--- a/src/ahl-binding.c
+++ b/src/ahl-binding.c
@@ -18,87 +18,167 @@
#define _GNU_SOURCE
#include <stdio.h>
#include <string.h>
-#include <time.h>
#include "ahl-binding.h"
#include "ahl-apidef.h" // Generated from JSON OpenAPI
#include "wrap-json.h"
-// TODO: json_object_put to free JSON objects? potential leaks currently
+// Global high-level binding context
+AHLCtxT g_AHLCtx;
+
+// Workshop development topics:
+// - Associating streamID with a client id of afb. Has to be done with sessions?
+// - Declaring dependencies on other binding services (examples)
+// - json_object_put to free JSON objects? potential leaks currently
+// - no events on new HAL registered to alsacore?
+// - unregistering event subscription examples?
+// - discussion how to use property and event system to dispatch external parameterization (e.g. Wwise/Fiberdyne)
+// - discussion where to best isolate policy (controller or HLB plugin)
+// - Other HLB attributes to pass through (e.g. interrupted behavior)
+// - DBScale TLV warning
+// - GLib (internal) dependency
+// - HAL registration dependency (initialization order)
+// - Binding startup arguments for config file path
+// - Can we use the same HAL for different card numbers?
+// - Example use of volume ramping in HAL?
+// - Binding termination function
+// - AGL persistence framework?
+// - How to provide API services with config.xml (secrets and all)
-// Helper macros/func for packaging JSON objects from C structures
+// Helper macros/func for packaging JSON objects from C structures
#define EndpointInfoStructToJSON(__JSON_OBJECT__, __ENDPOINTINFOSTRUCT__) \
- wrap_json_pack(&__JSON_OBJECT__, "{s:i,s:i,s:s}", "endpoint_id", __ENDPOINTINFOSTRUCT__.endpoint_id, "type", __ENDPOINTINFOSTRUCT__.type, "name", __ENDPOINTINFOSTRUCT__.name);
-
-#define RoutingInfoStructToJSON(__JSON_OBJECT__, __ROUTINGINFOSTRUCT__) \
- wrap_json_pack(&__JSON_OBJECT__, "{s:i,s:i,s:i}", "routing_id", __ROUTINGINFOSTRUCT__.routing_id, "source_id", __ROUTINGINFOSTRUCT__.source_id, "sink_id", __ROUTINGINFOSTRUCT__.sink_id);
-
+ wrap_json_pack(&__JSON_OBJECT__, "{s:i,s:i,s:s,s:i}",\
+ "endpoint_id", __ENDPOINTINFOSTRUCT__.endpointID, \
+ "endpoint_type", __ENDPOINTINFOSTRUCT__.type, \
+ "device_name", __ENDPOINTINFOSTRUCT__.deviceName, \
+ "device_uri_type", __ENDPOINTINFOSTRUCT__.deviceURIType);
+
static void StreamInfoStructToJSON(json_object **streamInfoJ, StreamInfoT streamInfo)
{
json_object *endpointInfoJ;
- EndpointInfoStructToJSON(endpointInfoJ,streamInfo.endpoint_info);
- wrap_json_pack(streamInfoJ, "{s:i,s:s}", "stream_id", streamInfo.stream_id, "pcm_name", streamInfo.pcm_name);
+ EndpointInfoStructToJSON(endpointInfoJ,streamInfo.endpointInfo);
+ wrap_json_pack(streamInfoJ, "{s:i}", "stream_id", streamInfo.streamID);
json_object_object_add(*streamInfoJ,"endpoint_info",endpointInfoJ);
}
+static streamID_t CreateNewStreamID()
+{
+ streamID_t newID = g_AHLCtx.nextStreamID;
+ g_AHLCtx.nextStreamID++;
+ return newID;
+}
+
+static int FindRoleIndex( const char * in_pAudioRole)
+{
+ int index = -1; // Not found
+ for (int i = 0; i < g_AHLCtx.policyCtx.iNumberRoles; i++)
+ {
+ GString gs = g_array_index( g_AHLCtx.policyCtx.pAudioRoles, GString, i );
+ if ( strcasecmp(gs.str,in_pAudioRole) == 0 )
+ index = i;
+ }
+ return index;
+}
+
+static EndpointInfoT * GetEndpointInfo(endpointID_t in_endpointID, EndpointTypeT in_endpointType)
+{
+ EndpointInfoT * pEndpointInfo = NULL;
+ for (int i = 0; i < g_AHLCtx.policyCtx.iNumberRoles; i++)
+ {
+ GArray * pRoleDeviceArray = NULL;
+ if (in_endpointType == ENDPOINTTYPE_SOURCE){
+ pRoleDeviceArray = g_ptr_array_index( g_AHLCtx.policyCtx.pSourceEndpoints, i );
+ }
+ else{
+ pRoleDeviceArray = g_ptr_array_index( g_AHLCtx.policyCtx.pSinkEndpoints, i );
+ }
+ for (int j = 0; j < pRoleDeviceArray->len; j++) {
+ EndpointInfoT * pCurEndpointInfo = &g_array_index(pRoleDeviceArray,EndpointInfoT,j);
+ if (pCurEndpointInfo->endpointID == in_endpointID) {
+ pEndpointInfo = pCurEndpointInfo;
+ break;
+ }
+ }
+ }
+ return pEndpointInfo;
+}
+
// Binding initialization
PUBLIC int AhlBindingInit()
{
-
int errcount = 0;
+ int err = 0;
+
+ // This API uses services from low level audio
+ err = afb_daemon_require_api_v2("alsacore",1) ;
+ if( err != 0 )
+ {
+ AFB_ERROR("Audio high level API requires alsacore API to be available");
+ return 1;
+ }
+
+ // Parse high-level binding JSON configuration file (will build device lists)
+ errcount += ParseHLBConfig();
- // Initialize list of available sources/sinks using lower level services
- errcount += EnumerateSources();
- errcount += EnumerateSinks();
+ // Initialize list of active streams
+ g_AHLCtx.policyCtx.pActiveStreams = g_array_new(FALSE,TRUE,sizeof(StreamInfoT));
- // TODO: Register for device changes from lower level services
+ // TODO: Register for device changes ALSA low level audio service
- // TODO: Parse high-level binding configuration file
+ // TODO: Perform other binding initialization tasks (e.g. broadcast service ready event?)
- // TODO: Perform other binding initialization tasks
+ // TODO: Use AGL persistence framework to retrieve and set inital state/volumes/properties
AFB_DEBUG("Audio high-level Binding success errcount=%d", errcount);
return errcount;
}
+// TODO: AhlBindingTerm()?
+// // TODO: Use AGL persistence framework to retrieve and set inital state/volumes
+
+// TODO: OnEventFunction
+// if ALSA device availability change -> update source / sink list
+// Call policy to attempt to assign role based on information (e.g. device name)
+// Policy should also determine priority (insert device in right spot in the list)
+
PUBLIC void audiohlapi_get_sources(struct afb_req req)
{
json_object *sourcesJ = NULL;
json_object *sourceJ = NULL;
json_object *queryJ = NULL;
- AudioRoleT audioRole = AUDIOROLE_MAXVALUE;
-
+ char * audioRole = NULL;
+
queryJ = afb_req_json(req);
- int err = wrap_json_unpack(queryJ, "{s?i}", "audio_role", &audioRole);
+ int err = wrap_json_unpack(queryJ, "{s:s}", "audio_role", &audioRole);
if (err) {
afb_req_fail_f(req, "Invalid arguments", "Args not a valid json object query=%s", json_object_get_string(queryJ));
return;
}
- if (audioRole != AUDIOROLE_MAXVALUE)
- {
- AFB_DEBUG("Filtering according to specified audio role=%d", audioRole);
- }
-
- // Fake run-time data for test purposes
- EndpointInfoT sources[3];
- sources[0].endpoint_id = 0;
- sources[0].type = 0;
- sources[0].name = "Source0";
- sources[1].endpoint_id = 1;
- sources[1].type = 1;
- sources[1].name = "Source1";
- sources[2].endpoint_id = 2;
- sources[2].type = 2;
- sources[2].name = "Source2";
-
sourcesJ = json_object_new_array();
- for ( unsigned int i = 0 ; i < 3; i++)
+
+ AFB_DEBUG("Filtering devices according to specified audio role=%s", audioRole);
+ // Check that the role requested exists in current configuration
+ int roleIndex = FindRoleIndex(audioRole);
+ if ( roleIndex == -1)
{
- EndpointInfoStructToJSON(sourceJ, sources[i]);
- json_object_array_add(sourcesJ, sourceJ);
+ afb_req_fail_f(req, "Invalid arguments", "Requested audio role does not exist in current configuration -> %s", json_object_get_string(queryJ));
+ return;
}
+ // List device only for current audio role and device type (pick the role device list)
+ int iRoleIndex = FindRoleIndex(audioRole);
+ if (iRoleIndex >= 0)
+ {
+ GArray * pRoleSourceDeviceArray = g_ptr_array_index( g_AHLCtx.policyCtx.pSourceEndpoints, iRoleIndex );
+ int iNumberDevices = pRoleSourceDeviceArray->len;
+ for ( int j = 0 ; j < iNumberDevices; j++)
+ {
+ EndpointInfoT sourceInfo = g_array_index(pRoleSourceDeviceArray,EndpointInfoT,j);
+ EndpointInfoStructToJSON(sourceJ, sourceInfo);
+ json_object_array_add(sourcesJ, sourceJ);
+ }
+ }
afb_req_success(req, sourcesJ, "List of sources");
}
@@ -108,38 +188,38 @@ PUBLIC void audiohlapi_get_sinks(struct afb_req req)
json_object *sinksJ = NULL;
json_object *sinkJ = NULL;
json_object *queryJ = NULL;
- AudioRoleT audioRole = AUDIOROLE_MAXVALUE;
+ char * audioRole = NULL;
queryJ = afb_req_json(req);
- int err = wrap_json_unpack(queryJ, "{s?i}", "audio_role", &audioRole);
+ int err = wrap_json_unpack(queryJ, "{s:s}", "audio_role", &audioRole);
if (err) {
afb_req_fail_f(req, "Invalid arguments", "Args not a valid json object query=%s", json_object_get_string(queryJ));
return;
}
- if (audioRole != AUDIOROLE_MAXVALUE)
- {
- AFB_DEBUG("Filtering according to specified audio role=%d", audioRole);
- }
-
- // Fake run-time data for test purposes
- EndpointInfoT sinks[3];
- sinks[0].endpoint_id = 0;
- sinks[0].type = 0;
- sinks[0].name = "Sink0";
- sinks[1].endpoint_id = 1;
- sinks[1].type = 1;
- sinks[1].name = "Sink1";
- sinks[2].endpoint_id = 2;
- sinks[2].type = 2;
- sinks[2].name = "Sink2";
-
sinksJ = json_object_new_array();
- for ( unsigned int i = 0 ; i < 3; i++)
+
+ AFB_DEBUG("Filtering devices according to specified audio role=%s", audioRole);
+ // Check that the role requested exists in current configuration
+ int roleIndex = FindRoleIndex(audioRole);
+ if ( roleIndex == -1)
{
- EndpointInfoStructToJSON(sinkJ, sinks[i]);
- json_object_array_add(sinksJ, sinkJ);
+ afb_req_fail_f(req, "Invalid arguments", "Requested audio role does not exist in current configuration -> %s", json_object_get_string(queryJ));
+ return;
}
+ // List device only for current audio role and device type (pick the role device list)
+ int iRoleIndex = FindRoleIndex(audioRole);
+ if (iRoleIndex >= 0)
+ {
+ GArray * pRoleSinkDeviceArray = g_ptr_array_index( g_AHLCtx.policyCtx.pSinkEndpoints, iRoleIndex );
+ int iNumberDevices = pRoleSinkDeviceArray->len;
+ for ( int j = 0 ; j < iNumberDevices; j++)
+ {
+ EndpointInfoT sinkInfo = g_array_index(pRoleSinkDeviceArray,EndpointInfoT,j);
+ EndpointInfoStructToJSON(sinkJ, sinkInfo);
+ json_object_array_add(sinksJ, sinkJ);
+ }
+ }
afb_req_success(req, sinksJ, "List of sinks");
}
@@ -149,30 +229,75 @@ PUBLIC void audiohlapi_stream_open(struct afb_req req)
json_object *streamInfoJ = NULL;
StreamInfoT streamInfo;
json_object *queryJ = NULL;
- AudioRoleT audioRole;
+ char * audioRole = NULL;
EndpointTypeT endpointType;
endpointID_t endpointID = UNDEFINED_ID;
+ int policyAllowed = 0;
+ EndpointInfoT endpointInfo;
queryJ = afb_req_json(req);
- int err = wrap_json_unpack(queryJ, "{s:i,s:i,s?i}", "audio_role", &audioRole, "endpoint_type", &endpointType, "endpoint_id", &endpointID);
+ int err = wrap_json_unpack(queryJ, "{s:s,s:i,s?i}", "audio_role", &audioRole, "endpoint_type", &endpointType, "endpoint_id", &endpointID);
if (err) {
afb_req_fail_f(req, "Invalid arguments", "Args not a valid json object query=%s", json_object_get_string(queryJ));
return;
}
- AFB_DEBUG("Parsed input arguments = audio_role:%d endpointType:%d endpointID:%d", audioRole,endpointType,endpointID);
+ AFB_DEBUG("Parsed input arguments = audio_role:%s endpoint_type:%d endpoint_id:%d", audioRole,endpointType,endpointID);
+
+ int iRoleIndex = FindRoleIndex(audioRole);
+ if (iRoleIndex < 0) {
+ afb_req_fail_f(req, "Invalid audio role", "Audio role was not found in configuration -> %s",audioRole);
+ return;
+ }
+
+ GArray * pRoleDeviceArray = NULL;
+ if (endpointType == ENDPOINTTYPE_SOURCE){
+ pRoleDeviceArray = g_ptr_array_index( g_AHLCtx.policyCtx.pSourceEndpoints, iRoleIndex );
+ }
+ else{
+ pRoleDeviceArray = g_ptr_array_index( g_AHLCtx.policyCtx.pSinkEndpoints, iRoleIndex );
+ }
+ if (pRoleDeviceArray->len == 0) {
+ afb_req_fail_f(req, "No available devices", "No available devices for role:%s and device type:%d",audioRole,endpointType);
+ return;
+ }
if (endpointID == UNDEFINED_ID)
{
- // TODO: Go through configuration and available devices to find best device for specified role
- endpointID = 2;
+ // Assign a device based on configuration priority (first in the list for requested role and endpoint type)
+ endpointInfo = g_array_index(pRoleDeviceArray,EndpointInfoT,0);
+ }
+ else{
+ // Find specified endpoint ID in list of devices
+ int iNumberDevices = pRoleDeviceArray->len;
+ int iEndpointFound = 0;
+ for ( int j = 0 ; j < iNumberDevices; j++)
+ {
+ endpointInfo = g_array_index(pRoleDeviceArray,EndpointInfoT,j);
+ if (endpointInfo.endpointID == endpointID) {
+ iEndpointFound = 1;
+ break;
+ }
+ }
+ if (iEndpointFound == 0) {
+ afb_req_fail_f(req, "Endpoint not available", "Specified endpoint not available for role:%s and device type:%d endpoint id %d",audioRole,endpointType,endpointID);
+ return;
+ }
}
- // Fake run-time data for test purposes
- streamInfo.stream_id = 12;
- streamInfo.pcm_name = "plug:Entertainment";
- streamInfo.endpoint_info.endpoint_id = endpointID;
- streamInfo.endpoint_info.type = endpointType;
- streamInfo.endpoint_info.name = "MainSpeakers";
+ // Call policy to verify whether creating a new audio stream is allowed in current context and possibly take other actions
+ policyAllowed = Policy_OpenStream(audioRole, endpointType, endpointID);
+ if (policyAllowed == AUDIOHL_POLICY_REJECT)
+ {
+ afb_req_fail(req, "Audio policy violation", "Open stream not allowed in current context");
+ return;
+ }
+
+ // Create stream
+ streamInfo.streamID = CreateNewStreamID(); // create new ID
+ streamInfo.endpointInfo = endpointInfo;
+
+ // Push stream on active stream list
+ g_array_append_val( g_AHLCtx.policyCtx.pActiveStreams, streamInfo );
StreamInfoStructToJSON(&streamInfoJ,streamInfo);
@@ -194,124 +319,94 @@ PUBLIC void audiohlapi_stream_close(struct afb_req req)
// TODO: Validate that the application ID from which the stream close is coming is the one that opened it, otherwise fail and do nothing
+ // Remove from active stream list (if present)
+ int iNumActiveStreams = g_AHLCtx.policyCtx.pActiveStreams->len;
+ int iStreamFound = 0;
+ for ( int i = 0; i < iNumActiveStreams ; i++ ) {
+ StreamInfoT streamInfo = g_array_index(g_AHLCtx.policyCtx.pActiveStreams,StreamInfoT,i);
+ if (streamInfo.streamID == streamID){
+ g_array_remove_index(g_AHLCtx.policyCtx.pActiveStreams,i);
+ iStreamFound = 1;
+ break;
+ }
+ }
+
+ if (iStreamFound == 0) {
+ afb_req_fail_f(req, "Stream not found", "Specified stream not currently active stream_id -> %d",streamID);
+ return;
+ }
+
afb_req_success(req, NULL, "Stream close completed");
}
-// Routings
-PUBLIC void audiohlapi_get_available_routings(struct afb_req req)
+// Endpoints
+PUBLIC void audiohlapi_set_volume(struct afb_req req)
{
- json_object *routingsJ;
- json_object *routingJ;
json_object *queryJ = NULL;
- AudioRoleT audioRole = AUDIOROLE_MAXVALUE;
+ endpointID_t endpointID = UNDEFINED_ID;
+ EndpointTypeT endpointType = ENDPOINTTYPE_MAXVALUE;
+ char * volumeStr = NULL;
+ int rampTimeMS = 0;
+ int policyAllowed = 0;
queryJ = afb_req_json(req);
- int err = wrap_json_unpack(queryJ, "{s?i}", "audio_role", &audioRole);
+ int err = wrap_json_unpack(queryJ, "{s:i,s:i,s:s,s?i}", "endpoint_type", &endpointType,"endpoint_id",&endpointID,"volume",&volumeStr,"ramp_time_ms",&rampTimeMS);
if (err) {
afb_req_fail_f(req, "Invalid arguments", "Args not a valid json object query=%s", json_object_get_string(queryJ));
return;
}
+ AFB_DEBUG("Parsed input arguments = endpoint_type:%d endpoint_id:%d volume:%s ramp_time_ms: %d", endpointType,endpointID,volumeStr,rampTimeMS);
+
+ // TODO: Parse volume string to support increment/absolute/percent notation
+ int vol = atoi(volumeStr);
- if (audioRole != AUDIOROLE_MAXVALUE)
+ policyAllowed = Policy_SetVolume(endpointType, endpointID, volumeStr, rampTimeMS); // TODO: Potentially retrieve modified value by policy (e.g. volume limit)
+ if (!policyAllowed)
{
- AFB_DEBUG("Filtering according to specified audio role=%d", audioRole);
- }
-
- // Fake run-time data for test purposes
- RoutingInfoT routings[3];
- routings[0].source_id = 0;
- routings[0].sink_id = 0;
- routings[0].routing_id = 0;
- routings[1].source_id = 1;
- routings[1].sink_id = 1;
- routings[1].routing_id = 1;
- routings[2].source_id = 2;
- routings[2].sink_id = 2;
- routings[2].routing_id = 2;
-
- routingsJ = json_object_new_array();
- for (unsigned int i = 0; i < 3; i++)
- {
- RoutingInfoStructToJSON(routingJ, routings[i]);
- json_object_array_add(routingsJ, routingJ);
+ afb_req_fail(req, "Audio policy violation", "Set volume not allowed in current context");
+ return;
}
- afb_req_success(req, routingsJ, "List of available routings");
-}
-
-PUBLIC void audiohlapi_add_routing(struct afb_req req)
-{
- json_object *queryJ = NULL;
- AudioRoleT audioRole = AUDIOROLE_MAXVALUE;
- routingID_t routingID = UNDEFINED_ID;
- json_object *routingJ = NULL;
-
- queryJ = afb_req_json(req);
- int err = wrap_json_unpack(queryJ, "{s:i,s?i}", "audio_role", &audioRole,"routing_id",routingID);
- if (err) {
- afb_req_fail_f(req, "Invalid arguments", "Args not a valid json object query=%s", json_object_get_string(queryJ));
+ // TODO: Cache during device enumeration for efficiency
+ EndpointInfoT * pEndpointInfo = GetEndpointInfo(endpointID,endpointType);
+ if (pEndpointInfo == NULL)
+ {
+ afb_req_fail_f(req, "Endpoint not found", "Endpoint information not found for id:%d type%d",endpointID,endpointType);
return;
}
- AFB_DEBUG("Parsed input arguments = audio_role:%d routing_id:%d", audioRole,routingID);
-
- // Fake run-time data for test purposes
- RoutingInfoT routingInfo;
- routingInfo.routing_id = routingID;
- routingInfo.source_id = 3;
- routingInfo.sink_id = 4;
- RoutingInfoStructToJSON(routingJ,routingInfo);
-
- afb_req_success(req,routingJ, "Selected routing information");
-}
+ // Using audio role available from endpoint to target the right HAL control (build string based on convention)
+ char halControlName[AUDIOHL_MAXHALCONTROLNAME_LENGTH];
+ strncpy(halControlName,pEndpointInfo->audioRole,AUDIOHL_MAXHALCONTROLNAME_LENGTH);
+ strcat(halControlName,"_Ramp"); // Or _Vol for direct control (no ramping)
-PUBLIC void audiohlapi_remove_routing(struct afb_req req)
-{
- json_object *queryJ = NULL;
- routingID_t routingID = UNDEFINED_ID;
-
- queryJ = afb_req_json(req);
- int err = wrap_json_unpack(queryJ, "{s:i}", "routing_id", &routingID);
+ // Set endpoint volume using HAL services (leveraging ramps etc.)
+ json_object *j_response, *j_query = NULL;
+
+ // Package query
+ err = wrap_json_pack(&j_query,"{s:s,s:i}","label",halControlName, "val",vol);
if (err) {
- afb_req_fail_f(req, "Invalid arguments", "Args not a valid json object query=%s", json_object_get_string(queryJ));
+ afb_req_fail_f(req, "Invalid query for HAL ctlset", "Invalid query for HAL ctlset: %s",json_object_to_json_string(j_query));
return;
}
- AFB_DEBUG("Parsed input arguments = routing_id:%d", routingID);
- // TODO: Validate that the application ID from which the stream close is coming is the one that opened it, otherwise fail and do nothing
-
- afb_req_success(req, NULL, "Remove routing completed");
-}
-
-// Endpoints
-PUBLIC void audiohlapi_set_endpoint_volume(struct afb_req req)
-{
- json_object *queryJ = NULL;
- endpointID_t endpointID = UNDEFINED_ID;
- EndpointTypeT endpointType = ENDPOINTTYPE_MAXVALUE;
- char * volumeStr = NULL;
- int rampTimeMS = 0;
-
- queryJ = afb_req_json(req);
- int err = wrap_json_unpack(queryJ, "{s:i,s:i,s:s,s?i}", "endpoint_type", &endpointType,"endpoint_id",&endpointID,"volume",&volumeStr,"ramp_time_ms",&rampTimeMS);
+ // Set the volume using the HAL
+ err = afb_service_call_sync(pEndpointInfo->halAPIName, "ctlset", j_query, &j_response);
if (err) {
- afb_req_fail_f(req, "Invalid arguments", "Args not a valid json object query=%s", json_object_get_string(queryJ));
+ afb_req_fail_f(req, "HAL ctlset failure", "Could not ctlset on HAL: %s",pEndpointInfo->halAPIName);
return;
}
- AFB_DEBUG("Parsed input arguments = endpoint_type:%d endpoint_id:%d volume:%s ramp_time_ms: %d", endpointType,endpointID,volumeStr,rampTimeMS);
-
- // TODO: Parse volume string to support increment/absolute/percent notation
-
+ AFB_DEBUG("HAL ctlset response=%s", json_object_to_json_string(j_response));
+
afb_req_success(req, NULL, "Set volume completed");
}
-PUBLIC void audiohlapi_get_endpoint_volume(struct afb_req req)
+PUBLIC void audiohlapi_get_volume(struct afb_req req)
{
json_object *queryJ = NULL;
endpointID_t endpointID = UNDEFINED_ID;
EndpointTypeT endpointType = ENDPOINTTYPE_MAXVALUE;
json_object *volumeJ;
- double volume = 0.0;
queryJ = afb_req_json(req);
int err = wrap_json_unpack(queryJ, "{s:i,s:i}", "endpoint_type", &endpointType,"endpoint_id",&endpointID);
@@ -321,13 +416,55 @@ PUBLIC void audiohlapi_get_endpoint_volume(struct afb_req req)
}
AFB_DEBUG("Parsed input arguments = endpoint_type:%d endpoint_id:%d", endpointType,endpointID);
- volume = 87.0; // TODO: Get actual volume value
- volumeJ = json_object_new_double(volume);
+ // TODO: Cache during device enumeration for efficiency
+ EndpointInfoT * pEndpointInfo = GetEndpointInfo(endpointID,endpointType);
+ if (pEndpointInfo == NULL)
+ {
+ afb_req_fail_f(req, "Endpoint not found", "Endpoint information not found for id:%d type%d",endpointID,endpointType);
+ return;
+ }
+
+ // Using audio role available from endpoint to target the right HAL control (build string based on convention)
+ char halControlName[AUDIOHL_MAXHALCONTROLNAME_LENGTH];
+ strncpy(halControlName,pEndpointInfo->audioRole,AUDIOHL_MAXHALCONTROLNAME_LENGTH);
+ strcat(halControlName,"_Vol"); // Use current value, not ramp target
+
+ // Set endpoint volume using HAL services (leveraging ramps etc.)
+ json_object *j_response, *j_query = NULL;
+
+ // Package query
+ err = wrap_json_pack(&j_query,"{s:s}","label",halControlName);
+ if (err) {
+ afb_req_fail_f(req, "Invalid query for HAL ctlget", "Invalid query for HAL ctlget: %s",json_object_to_json_string(j_query));
+ return;
+ }
+
+ // Set the volume using the HAL
+ err = afb_service_call_sync(pEndpointInfo->halAPIName, "ctlget", j_query, &j_response);
+ if (err) {
+ afb_req_fail_f(req, "HAL ctlget failure", "Could not ctlget on HAL: %s",pEndpointInfo->halAPIName);
+ return;
+ }
+ AFB_INFO("HAL ctlget response=%s", json_object_to_json_string(j_response));
+
+ // Parse response
+ json_object * jRespObj = NULL;
+ json_object_object_get_ex(j_response, "response", &jRespObj);
+ json_object * jVal = NULL;
+ json_object_object_get_ex(jRespObj, "val", &jVal);
+ int val1 = 0, val2 = 0; // Why 2 values?
+ err = wrap_json_unpack(jVal, "[ii]", &val1, &val2);
+ if (err) {
+ afb_req_fail_f(req,"Volume retrieve failed", "Could not retrieve volume value -> %s", json_object_get_string(jVal));
+ return;
+ }
+
+ volumeJ = json_object_new_double((double)val1);
afb_req_success(req, volumeJ, "Retrieved volume value");
}
-PUBLIC void audiohlapi_set_endpoint_property(struct afb_req req)
+PUBLIC void audiohlapi_set_property(struct afb_req req)
{
json_object *queryJ = NULL;
endpointID_t endpointID = UNDEFINED_ID;
@@ -335,6 +472,7 @@ PUBLIC void audiohlapi_set_endpoint_property(struct afb_req req)
char * propertyName = NULL;
char * propValueStr = NULL;
int rampTimeMS = 0;
+ int policyAllowed = 0;
queryJ = afb_req_json(req);
int err = wrap_json_unpack(queryJ, "{s:i,s:i,s:s,s:s,s?i}", "endpoint_type", &endpointType,"endpoint_id",&endpointID,"property_name",&propertyName,"value",&propValueStr,"ramp_time_ms",&rampTimeMS);
@@ -346,10 +484,23 @@ PUBLIC void audiohlapi_set_endpoint_property(struct afb_req req)
// TODO: Parse property value string to support increment/absolute/percent notation
+ // Call policy to allow custom policy actions in current context
+ policyAllowed = Policy_SetProperty(endpointType, endpointID, propertyName, propValueStr, rampTimeMS); // TODO: Potentially retrieve modified value by policy (e.g. parameter limit)
+ if (!policyAllowed)
+ {
+ afb_req_fail(req, "Audio policy violation", "Set endpoint property not allowed in current context");
+ return;
+ }
+
+ // TODO: Set endpoint property (dispatch on right service target)
+ // Property targets (Internal,Wwise,Fiberdyne) (e.g. Wwise.ParamX, Fiberdyne.ParamY, Internal.ParamZ)
+ // Cache value in property list
+ // TBD
+
afb_req_success(req, NULL, "Set property completed");
}
-PUBLIC void audiohlapi_get_endpoint_property(struct afb_req req)
+PUBLIC void audiohlapi_get_property(struct afb_req req)
{
json_object *queryJ = NULL;
endpointID_t endpointID = UNDEFINED_ID;
@@ -366,19 +517,22 @@ PUBLIC void audiohlapi_get_endpoint_property(struct afb_req req)
}
AFB_DEBUG("Parsed input arguments = endpoint_type:%d endpoint_id:%d property_name:%s", endpointType,endpointID,propertyName);
- value = 93.0; // TODO: Get actual property value
+ // TODO: Retrieve cached property value
+
+ value = 93.0; // TODO: Get actual property value
propertyValJ = json_object_new_double(value);
afb_req_success(req, propertyValJ, "Retrieved property value");
}
-PUBLIC void audiohlapi_set_endpoint_state(struct afb_req req)
+PUBLIC void audiohlapi_set_state(struct afb_req req)
{
json_object *queryJ = NULL;
endpointID_t endpointID = UNDEFINED_ID;
EndpointTypeT endpointType = ENDPOINTTYPE_MAXVALUE;
char * stateName = NULL;
char * stateValue = NULL;
+ int policyAllowed = 0;
queryJ = afb_req_json(req);
int err = wrap_json_unpack(queryJ, "{s:i,s:i,s:s,s:s}", "endpoint_type", &endpointType,"endpoint_id",&endpointID,"state_name",&stateName,"state_value",&stateValue);
@@ -388,10 +542,28 @@ PUBLIC void audiohlapi_set_endpoint_state(struct afb_req req)
}
AFB_DEBUG("Parsed input arguments = endpoint_type:%d endpoint_id:%d state_name:%s state_value:%s", endpointType,endpointID,stateName,stateValue);
+ // Check that state provided is within list of known state for this config
+ char * pDefaultStateValue = g_hash_table_lookup(g_AHLCtx.pDefaultStatesHT, stateName);
+ if (pDefaultStateValue == NULL)
+ {
+ afb_req_fail_f(req, "Invalid arguments", "State provided is not known to configuration query=%s", stateName);
+ return;
+ }
+
+ // Call policy to allow custom policy actions in current context
+ policyAllowed = Policy_SetState(endpointType, endpointID, stateName, stateValue); // TODO: Potentially retrieve modified value by policy (e.g. state change)
+ if (!policyAllowed)
+ {
+ afb_req_fail(req, "Audio policy violation", "Set endpoint state not allowed in current context");
+ return;
+ }
+
+ // Change the state of the endpoint as requested
+
afb_req_success(req, NULL, "Set endpoint state completed");
}
-PUBLIC void audiohlapi_get_endpoint_state(struct afb_req req)
+PUBLIC void audiohlapi_get_state(struct afb_req req)
{
json_object *queryJ = NULL;
endpointID_t endpointID = UNDEFINED_ID;
@@ -409,6 +581,7 @@ PUBLIC void audiohlapi_get_endpoint_state(struct afb_req req)
AFB_DEBUG("Parsed input arguments = endpoint_type:%d endpoint_id:%d state_name:%s", endpointType,endpointID,stateName);
stateValJ = json_object_new_string(stateValue);
+ // return cached value
afb_req_success(req, stateValJ, "Retrieved state value");
}
@@ -419,18 +592,27 @@ PUBLIC void audiohlapi_post_sound_event(struct afb_req req)
json_object *queryJ = NULL;
char * eventName = NULL;
char * mediaName = NULL;
- AudioRoleT audioRole;
+ char * audioRole = NULL;
json_object *audioContext = NULL;
+ int policyAllowed = 0;
queryJ = afb_req_json(req);
- int err = wrap_json_unpack(queryJ, "{s:s,s?i,s?s,s?o}", "event_name", &eventName,"audio_role",&audioRole,"media_name",&mediaName,"audio_context",&audioContext);
+ int err = wrap_json_unpack(queryJ, "{s:s,s:s,s?s,s?o}", "event_name", &eventName,"audio_role",&audioRole,"media_name",&mediaName,"audio_context",&audioContext);
if (err) {
afb_req_fail_f(req, "Invalid arguments", "Args not a valid json object query=%s", json_object_get_string(queryJ));
return;
}
- AFB_DEBUG("Parsed input arguments = event_name:%s audio_role:%d media_name:%s", eventName,audioRole,mediaName);
+ AFB_DEBUG("Parsed input arguments = event_name:%s audio_role:%s media_name:%s", eventName,audioRole,mediaName);
- // TODO: Post sound event to rendering services
+ // Call policy to allow custom policy actions in current context (e.g. cancel playback)
+ policyAllowed = Policy_PostSoundEvent(eventName, audioRole, mediaName, (void*)audioContext); // TODO: Potentially retrieve modified value by policy (e.g. change media)
+ if (!policyAllowed)
+ {
+ afb_req_fail(req, "Audio policy violation", "Post sound event not allowed in current context");
+ return;
+ }
+
+ // TODO: Post sound event to rendering services (e.g. gstreamer file player wrapper or simple ALSA player)
afb_req_success(req, NULL, "Posted sound event");
}
@@ -448,3 +630,15 @@ PUBLIC void audiohlapi_subscribe(struct afb_req req)
afb_req_success(req, NULL, "Subscribe to events finished");
}
+
+PUBLIC void audiohlapi_unsubscribe(struct afb_req req)
+{
+ // json_object *queryJ = NULL;
+
+ // queryJ = afb_req_json(req);
+
+ // TODO: Iterate through array length, parsing the string value to actual events
+ // TODO: Unsubscribe to appropriate events from other services
+
+ afb_req_success(req, NULL, "Subscribe to events finished");
+}
diff --git a/src/ahl-binding.h b/src/ahl-binding.h
index 7bd0653..2422963 100644
--- a/src/ahl-binding.h
+++ b/src/ahl-binding.h
@@ -15,82 +15,85 @@
* limitations under the License.
*/
-
#ifndef AHL_BINDING_INCLUDE
#define AHL_BINDING_INCLUDE
#define AFB_BINDING_VERSION 2
#include <afb/afb-binding.h>
#include <json-c/json.h>
+#include <glib.h>
+
+#include "ahl-interface.h"
#ifndef PUBLIC
#define PUBLIC
#endif
-#define UNDEFINED_ID -1
-
-typedef int endpointID_t;
-typedef int streamID_t;
-typedef int routingID_t;
-
-typedef enum EndpointType {
- ENDPOINTTYPE_SOURCE = 0, // source devices
- ENDPOINTTYPE_SINK, // sink devices
- ENDPOINTTYPE_MAXVALUE // Enum count, keep at the end
-} EndpointTypeT;
+/////////////// Binding private information //////////////////
-typedef enum AudioRole {
- AUDIOROLE_WARNING = 0, // Safety-relevant or critical alerts/alarms
- AUDIOROLE_GUIDANCE, // Important user information where user action is expected (e.g. navigation instruction)
- AUDIOROLE_NOTIFICATION, // HMI or else notifications (e.g. touchscreen events, speech recognition on/off,...)
- AUDIOROLE_COMMUNICATIONS, // Voice communications (e.g. handsfree, speech recognition)
- AUDIOROLE_ENTERTAINMENT, // Multimedia content (e.g. tuner, media player, etc.)
- AUDIOROLE_SYSTEM, // System level content
- AUDIOROLE_DEFAULT, // No specific audio role (legacy applications)
- AUDIOROLE_MAXVALUE // Enum count, keep at the end
-} AudioRoleT;
-
-typedef enum AudioDeviceClass {
- AUDIODEVICE_SPEAKERMAIN = 0,
- AUDIODEVICE_SPEAKERHEADREST,
- AUDIODEVICE_HEADSET,
- AUDIODEVICE_HEADPHONE,
- AUDIODEVICE_LINEOUT,
- AUDIODEVICE_LINEIN,
- AUDIODEVICE_BLUETOOTH,
- AUDIODEVICE_HANDSET,
- AUDIODEVICE_HDMI,
- AUDIODEVICE_USB,
- AUDIODEVICE_TONES,
- AUDIODEVICE_VOICE,
- AUDIODEVICE_PHONELINK,
- AUDIODEVICE_DEFAULT,
- AUDIODEVICE_MAXVALUE // Enum count, keep at the end
-} AudioDeviceClassT;
+#define AUDIOHL_MAX_DEVICE_URI_LENGTH 256
+#define AUDIOHL_MAX_DEVICE_NAME_LENGTH 256
+#define AUDIOHL_MAX_AUDIOROLE_LENGTH 128
+#define AUDIOHL_MAX_HALAPINAME_LENGTH 64
+#define AUDIOHL_POLICY_ACCEPT 1
+#define AUDIOHL_POLICY_REJECT 0
typedef struct EndpointInfo
{
- endpointID_t endpoint_id;
- EndpointTypeT type;
- char * name;
- // TODO: Consider adding associated device class
+ endpointID_t endpointID; // Unique endpoint ID (per type)
+ EndpointTypeT type; // Source or sink device
+ char deviceName[AUDIOHL_MAX_DEVICE_NAME_LENGTH]; // Device name for applications to display
+ char deviceURI[AUDIOHL_MAX_DEVICE_URI_LENGTH]; // Associated URI
+ DeviceURITypeT deviceURIType; // Device URI type (includes audio domain information)
+ char audioRole[AUDIOHL_MAX_AUDIOROLE_LENGTH]; // Audio role that registered this endpoint -> private
+ char halAPIName[AUDIOHL_MAX_AUDIOROLE_LENGTH]; // HAL associated with the device (for volume control) -> private
+ int cardNum; // HW card number -> private
+ int deviceNum; // HW device number -> private
+ int subDeviceNum; // HW sub device number -> private
+ // Cached endpoint properties
+ GHashTable * pStatesHT; // Keep all known states in key value pairs
} EndpointInfoT;
typedef struct StreamInfo {
- streamID_t stream_id;
- char * pcm_name;
- EndpointInfoT endpoint_info;
+ streamID_t streamID;
+ EndpointInfoT endpointInfo;
} StreamInfoT;
-typedef struct RoutingInfo {
- routingID_t routing_id;
- endpointID_t source_id;
- endpointID_t sink_id;
-} RoutingInfoT;
+// Parts of the context that are visible to the policy (for state based decisions)
+typedef struct AHLPolicyCtx {
+ GPtrArray * pSourceEndpoints; // Array of source end points for each audio role (GArray*)
+ GPtrArray * pSinkEndpoints; // Array of sink end points for each audio role (GArray*)
+ GArray * pRolePriority; // List of role priorities (int). TODO: Should be hash table with role name as key
+ GArray * pAudioRoles; // List of audio roles (GString)
+ GArray * pActiveStreams; // List of active streams (StreamInfoT)
+ int iNumberRoles; // Number of audio roles from configuration
+ // TODO: Global properties -> exposed to policy
+} AHLPolicyCtxT;
+
+// Global binding context
+typedef struct AHLCtx {
+ AHLPolicyCtxT policyCtx;
+ endpointID_t nextSourceEndpointID; // Counter to assign new ID
+ endpointID_t nextSinkEndpointID; // Counter to assign new ID
+ endpointID_t nextStreamID; // Counter to assign new ID
+ GArray * pHALList; // List of HAL dependencies
+ GHashTable * pDefaultStatesHT; // List of states and default values known to configuration
+} AHLCtxT;
PUBLIC int AhlBindingInit();
// ahl-deviceenum.c
-PUBLIC int EnumerateSources();
-PUBLIC int EnumerateSinks();
+PUBLIC int EnumerateSources(json_object * in_jSourceArray, int in_iRoleIndex, char * in_pRoleName);
+PUBLIC int EnumerateSinks(json_object * in_jSinkArray, int in_iRoleIndex, char * in_pRoleName);
+// ahl-config.c
+PUBLIC int ParseHLBConfig();
+// ahl-policy.c
+PUBLIC int Policy_OpenStream(char *pAudioRole, EndpointTypeT endpointType, endpointID_t endpointID);
+PUBLIC int Policy_SetVolume(EndpointTypeT endpointType, endpointID_t endpointID, char *volumeStr, int rampTimeMS);
+PUBLIC int Policy_SetProperty(EndpointTypeT endpointType, endpointID_t endpointID, char *propertyName, char *propValueStr, int rampTimeMS);
+PUBLIC int Policy_SetState(EndpointTypeT endpointType, endpointID_t endpointID, char *pStateName, char *pStateValue);
+PUBLIC int Policy_PostSoundEvent(char *eventName, char *audioRole, char *mediaName, void *audioContext);
+PUBLIC int Policy_AudioDeviceChange();
-#endif
+#define AUDIOHL_MAXHALCONTROLNAME_LENGTH 128
+
+#endif // AHL_BINDING_INCLUDE
diff --git a/src/ahl-config.c b/src/ahl-config.c
new file mode 100644
index 0000000..432910f
--- /dev/null
+++ b/src/ahl-config.c
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2017 "Audiokinetic Inc"
+ * Author Francois Thibault <fthibault@audiokinetic.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <string.h>
+#include <json-c/json.h>
+#include "wrap-json.h"
+
+#include "ahl-binding.h"
+
+// TODO: Term() to free all allocs upon exit...
+
+extern AHLCtxT g_AHLCtx;
+
+PUBLIC int ParseHLBConfig() {
+ char * versionStr = NULL;
+ json_object * jAudioRoles = NULL;
+ json_object * jHALList = NULL;
+ json_object * jStateList = NULL;
+
+ // TODO: This should be retrieve from binding startup arguments
+ char configfile_path[256];
+ sprintf(configfile_path, "%s/opt/config/ahl-config.json", getenv("HOME"));
+ AFB_INFO("High-level config file -> %s\n", configfile_path);
+
+ // Open configuration file
+ json_object *config_JFile=json_object_from_file(configfile_path);
+ if(config_JFile == NULL)
+ {
+ AFB_ERROR("Error: Can't open configuration file -> %s",configfile_path);
+ return 1;
+ }
+
+ int err = wrap_json_unpack(config_JFile, "{s:s,s:o,s:o,s:o}", "version", &versionStr,"audio_roles",&jAudioRoles,"hal_list",&jHALList,"state_list",&jStateList);
+ if (err) {
+ AFB_ERROR("Invalid configuration file -> %s", configfile_path);
+ return 1;
+ }
+
+ // Parse and populate list of known states
+ g_AHLCtx.pDefaultStatesHT = g_hash_table_new(g_str_hash, g_str_equal);
+ int iStateListLength = json_object_array_length(jStateList);
+ for ( int i = 0; i < iStateListLength; i++)
+ {
+ char * pStateName = NULL;
+ char * pStateVal = NULL;
+ json_object * jStateobject = json_object_array_get_idx(jStateList,i);
+ err = wrap_json_unpack(jStateobject, "{s:s,s:s}", "name", &pStateName,"default_val",&pStateVal);
+ if (err) {
+ AFB_ERROR("Invalid state object -> %s", json_object_get_string(jStateobject));
+ return 1;
+ }
+
+ g_hash_table_insert(g_AHLCtx.pDefaultStatesHT, pStateName, pStateVal);
+ }
+
+ int iHALListLength = json_object_array_length(jHALList);
+ int iNumberOfRoles = json_object_array_length(jAudioRoles);
+ // Dynamically allocated based on number or roles found
+ g_AHLCtx.pHALList = g_array_sized_new(FALSE, TRUE, sizeof(GString), iHALListLength);
+ g_AHLCtx.policyCtx.pRolePriority = g_array_sized_new(FALSE, TRUE, sizeof(int), iNumberOfRoles);
+ g_AHLCtx.policyCtx.pAudioRoles = g_array_sized_new(FALSE, TRUE, sizeof(GString), iNumberOfRoles);
+ g_AHLCtx.policyCtx.pSourceEndpoints = g_ptr_array_sized_new(iNumberOfRoles);
+ g_AHLCtx.policyCtx.pSinkEndpoints = g_ptr_array_sized_new(iNumberOfRoles);
+ g_AHLCtx.policyCtx.iNumberRoles = iNumberOfRoles;
+
+ for (unsigned int i = 0; i < iHALListLength; i++)
+ {
+ char * pHAL = NULL;
+ json_object * jHAL = json_object_array_get_idx(jHALList,i);
+ pHAL = (char *)json_object_get_string(jHAL);
+ GString * gHALName = g_string_new( pHAL );
+ g_array_append_val( g_AHLCtx.pHALList, *gHALName );
+
+ // Set dependency on HAL
+ err = afb_daemon_require_api_v2(pHAL,1) ;
+ if( err != 0 )
+ {
+ AFB_ERROR("Audio high level API could not set dependenvy on API: %s",pHAL);
+ return 1;
+ }
+ }
+
+ for (unsigned int i = 0; i < iNumberOfRoles; i++)
+ {
+ int priority = 0;
+ json_object * jAudioRole = json_object_array_get_idx(jAudioRoles,i);
+ json_object * jOutputDevices = NULL;
+ json_object * jInputDevices = NULL;
+ char * pRoleName = NULL;
+ int iNumOutDevices = 0;
+ int iNumInDevices = 0;
+
+ err = wrap_json_unpack(jAudioRole, "{s:s,s:i,s?o,s?o}", "name", &pRoleName,"priority",&priority,"output",&jOutputDevices,"input",&jInputDevices);
+ if (err) {
+ AFB_ERROR("Invalid audio role configuration : %s", json_object_to_json_string(jAudioRole));
+ return 1;
+ }
+
+ if (jOutputDevices)
+ iNumOutDevices = json_object_array_length(jOutputDevices);
+ if (jInputDevices)
+ iNumInDevices = json_object_array_length(jInputDevices);
+
+ GString * gRoleName = g_string_new( pRoleName );
+ g_array_append_val( g_AHLCtx.policyCtx.pAudioRoles, *gRoleName );
+ g_array_append_val( g_AHLCtx.policyCtx.pRolePriority, priority );
+
+ // Sources
+ GArray * pRoleSourceDeviceArray = g_array_new(FALSE, TRUE, sizeof(EndpointInfoT));
+ g_ptr_array_add(g_AHLCtx.policyCtx.pSourceEndpoints,pRoleSourceDeviceArray);
+ if (iNumInDevices) {
+ err = EnumerateSources(jInputDevices,i,pRoleName);
+ if (err) {
+ AFB_ERROR("Invalid input devices : %s", json_object_to_json_string(jInputDevices));
+ return 1;
+ }
+ }
+ GArray * pRoleSinkDeviceArray = g_array_new(FALSE, TRUE, sizeof(EndpointInfoT));
+ g_ptr_array_add(g_AHLCtx.policyCtx.pSinkEndpoints,pRoleSinkDeviceArray);
+ if (iNumOutDevices) {
+ err = EnumerateSinks(jOutputDevices,i,pRoleName);
+ if (err) {
+ AFB_ERROR("Invalid output devices : %s", json_object_to_json_string(jOutputDevices));
+ return 1;
+ }
+ }
+ }
+
+ // Build lists of all device URI referenced in config file (input/output)
+ AFB_DEBUG ("Audio high-level - Parse high-level audio configuration done");
+ return 0;
+}
diff --git a/src/ahl-deviceenum.c b/src/ahl-deviceenum.c
index 6629de2..b866d52 100644
--- a/src/ahl-deviceenum.c
+++ b/src/ahl-deviceenum.c
@@ -18,23 +18,417 @@
#define _GNU_SOURCE
#include <stdio.h>
#include <string.h>
-#include <time.h>
+#include <alsa/asoundlib.h>
+#include <alsa/pcm.h>
+#include <json-c/json.h>
+#include "wrap-json.h"
#include "ahl-binding.h"
-PUBLIC int EnumerateSources() {
-
- // TODO: Use lower level services to build a list of available source devices
+extern AHLCtxT g_AHLCtx;
+
+static endpointID_t CreateNewSourceID()
+{
+ endpointID_t newID = g_AHLCtx.nextSourceEndpointID;
+ g_AHLCtx.nextSourceEndpointID++;
+ return newID;
+}
+
+static endpointID_t CreateNewSinkID()
+{
+ endpointID_t newID = g_AHLCtx.nextSinkEndpointID;
+ g_AHLCtx.nextSinkEndpointID++;
+ return newID;
+}
+
+// Watchout: This function uses strtok and is destructive on the input string (use a copy)
+static int SeparateDomainFromDeviceURI( char * in_pDeviceURI, char ** out_pDomain, char ** out_pDevice)
+{
+ *out_pDomain = strtok(in_pDeviceURI, ".");
+ if (*out_pDomain == NULL)
+ {
+ AFB_ERROR("Error tokenizing device URI -> %s",in_pDeviceURI);
+ return 1;
+ }
+ // TODO: Validate domain is known string (e.g. ALSA,Pulse,GStreamer)
+ *out_pDevice = strtok(NULL, ".");
+ if (*out_pDevice == NULL)
+ {
+ AFB_ERROR("Error tokenizing device URI -> %s",in_pDeviceURI);
+ return 1;
+ }
+ return 0;
+}
+
+static int IsAlsaDomain(const char * in_pDomainStr)
+{
+ return (int) (strcasecmp(in_pDomainStr,AUDIOHL_DOMAIN_ALSA) == 0);
+}
+
+static int IsPulseDomain(const char * in_pDomainStr)
+{
+ return (int) (strcasecmp(in_pDomainStr,AUDIOHL_DOMAIN_PULSE) == 0);
+}
+
+static int IsGStreamerDomain(const char * in_pDomainStr)
+{
+ return (int) (strcasecmp(in_pDomainStr,AUDIOHL_DOMAIN_GSTREAMER) == 0);
+}
+
+static int IsExternalDomain(const char * in_pDomainStr)
+{
+ return (int) (strcasecmp(in_pDomainStr,AUDIOHL_DOMAIN_EXTERNAL) == 0);
+}
+
+static int FillALSAPCMInfo( snd_pcm_t * in_pPcmHandle, EndpointInfoT * out_pEndpointInfo )
+{
+ snd_pcm_type_t pcmType = 0;
+ snd_pcm_info_t * pPcmInfo = NULL;
+ int iAlsaRet = 0;
+ const char * pDeviceName = NULL;
+ int retVal = 0;
+
+ // retrieve PCM type
+ pcmType = snd_pcm_type(in_pPcmHandle);
+ switch (pcmType) {
+ case SND_PCM_TYPE_HW:
+ out_pEndpointInfo->deviceURIType = DEVICEURITYPE_ALSA_HW;
+ break;
+ case SND_PCM_TYPE_DMIX:
+ out_pEndpointInfo->deviceURIType = DEVICEURITYPE_ALSA_DMIX;
+ break;
+ case SND_PCM_TYPE_SOFTVOL:
+ out_pEndpointInfo->deviceURIType = DEVICEURITYPE_ALSA_SOFTVOL;
+ break;
+ default:
+ out_pEndpointInfo->deviceURIType = DEVICEURITYPE_ALSA_OTHER;
+ break;
+ }
+
+ iAlsaRet = snd_pcm_info_malloc(&pPcmInfo);
+ if (iAlsaRet < 0)
+ {
+ AFB_WARNING("Error allocating PCM info structure");
+ retVal = 1;
+ goto End;
+ }
+
+ iAlsaRet = snd_pcm_info(in_pPcmHandle,pPcmInfo);
+ if (iAlsaRet < 0)
+ {
+ AFB_WARNING("Error retrieving PCM device info");
+ retVal = 1;
+ goto End;
+ }
+
+ // Populate target device name (for application display)
+ pDeviceName = snd_pcm_info_get_name(pPcmInfo);
+ if (pDeviceName == NULL)
+ {
+ AFB_WARNING("No Alsa device name available");
+ retVal = 1;
+ goto End;
+ // Could potentially assign a "default" name and carry on with this device
+ }
+ strncpy(out_pEndpointInfo->deviceName,pDeviceName,AUDIOHL_MAX_DEVICE_NAME_LENGTH); // Overwritten by HAL name if available
+
+ // get card number
+ out_pEndpointInfo->cardNum = snd_pcm_info_get_card(pPcmInfo);
+ if ( out_pEndpointInfo->cardNum < 0 )
+ {
+ AFB_WARNING("No Alsa card number available");
+ retVal = 1;
+ goto End;
+ }
+ // get device number
+ out_pEndpointInfo->deviceNum = snd_pcm_info_get_device(pPcmInfo);
+ if ( out_pEndpointInfo->deviceNum < 0 )
+ {
+ AFB_WARNING("No Alsa device number available");
+ retVal = 1;
+ goto End;
+ }
+
+ // get sub-device number
+ out_pEndpointInfo->subDeviceNum = snd_pcm_info_get_subdevice(pPcmInfo);
+ if ( out_pEndpointInfo->subDeviceNum < 0 )
+ {
+ AFB_WARNING("No Alsa subdevice number available");
+ retVal = 1;
+ goto End;
+ }
+
+End:
+ if(pPcmInfo) {
+ snd_pcm_info_free(pPcmInfo);
+ pPcmInfo = NULL;
+ }
+
+ return retVal;
+}
+
+static int RetrieveAssociatedHALAPIName(EndpointInfoT * io_pEndpointInfo)
+{
+ json_object *j_response, *j_query = NULL;
+ int err;
+ err = afb_service_call_sync("alsacore", "hallist", j_query, &j_response);
+ if (err) {
+ AFB_ERROR("Could not retrieve list of HAL from ALSA core");
+ return 1;
+ }
+ AFB_DEBUG("ALSAcore hallist response=%s", json_object_to_json_string(j_response));
+
+ // Look through returned list for matching card
+ int found = 0;
+ json_object * jRespObj = NULL;
+ json_object_object_get_ex(j_response, "response", &jRespObj);
+ int iNumHAL = json_object_array_length(jRespObj);
+ for ( int i = 0 ; i < iNumHAL; i++)
+ {
+ json_object * jHAL = json_object_array_get_idx(jRespObj,i);
+ char * pDevIDStr = NULL;
+ char * pAPIName = NULL;
+ char * pShortName = NULL;
+
+ int err = wrap_json_unpack(jHAL, "{s:s,s:s,s:s}", "devid", &pDevIDStr,"api", &pAPIName,"shortname",&pShortName);
+ if (err) {
+ AFB_ERROR("Could not retrieve devid string=%s", json_object_get_string(jHAL));
+ return 1;
+ }
+
+ // Retrieve card number (e.g. hw:0)
+ int iCardNum = atoi(pDevIDStr+3);
+ if (iCardNum == io_pEndpointInfo->cardNum) {
+ strncpy(io_pEndpointInfo->halAPIName,pAPIName,AUDIOHL_MAX_AUDIOROLE_LENGTH);
+ strncpy(io_pEndpointInfo->deviceName,pShortName,AUDIOHL_MAX_DEVICE_NAME_LENGTH);
+ found = 1;
+ break;
+ }
+ }
+ return !found;
+}
+
+static int InitializeEndpointStates( EndpointInfoT * out_pEndpointInfo )
+{
+ //out_pEndpointInfo = g_array_sized_new(FALSE,TRUE,sizeof)
+ // for list of known states
+ return 0;
+}
+
+// For a given audio role
+PUBLIC int EnumerateSources(json_object * in_jSourceArray, int in_iRoleIndex, char * in_pRoleName) {
+
+ int iNumberDevices = json_object_array_length(in_jSourceArray);
+
+ // Parse and validate list of available devices
+ for (unsigned int i = 0; i < iNumberDevices; i++)
+ {
+ char fullDeviceURI[AUDIOHL_MAX_DEVICE_URI_LENGTH]; // strtok is destructive
+ char * pDeviceURIDomain = NULL;
+ char * pFullDeviceURI = NULL;
+ char * pDeviceURIPCM = NULL;
+ int err = 0;
+ EndpointInfoT endpointInfo;
+
+ json_object * jSource = json_object_array_get_idx(in_jSourceArray,i);
+
+ // strip domain name from URI
+ pFullDeviceURI = (char *)json_object_get_string(jSource);
+ strncpy(fullDeviceURI,pFullDeviceURI,AUDIOHL_MAX_DEVICE_URI_LENGTH);
+ err = SeparateDomainFromDeviceURI(fullDeviceURI,&pDeviceURIDomain,&pDeviceURIPCM);
+ if (err)
+ {
+ AFB_WARNING("Invalid device URI string -> %s",fullDeviceURI);
+ continue;
+ }
+
+ // non ALSA URI are simply passed to application (no validation) at this time
+ // In Non ALSA case devices in config are assumed to be available, if not application can fallback on explicit device selection
+ endpointInfo.cardNum = -1;
+ endpointInfo.deviceNum = -1;
+ endpointInfo.cardNum = -1;
+ strncpy(endpointInfo.deviceName,pDeviceURIPCM,AUDIOHL_MAX_DEVICE_NAME_LENGTH); // Overwritten by HAL name if available
+
+ if (IsAlsaDomain(pDeviceURIDomain))
+ {
+ // TODO: Missing support for loose name matching
+ // This will require using ALSA hints to get PCM names
+ // And would iterate over all available devices matching string (possibly all if no filtering is desired for a certain role)
+
+ snd_pcm_t * pPcmHandle = NULL;
+
+ // Get PCM handle
+ err = snd_pcm_open(&pPcmHandle, pDeviceURIPCM, SND_PCM_STREAM_CAPTURE, 0);
+ if (err < 0)
+ {
+ AFB_NOTICE("Alsa PCM device was not found -> %s", pDeviceURIPCM);
+ continue;
+ }
+
+ err = FillALSAPCMInfo(pPcmHandle,&endpointInfo);
+ if (err) {
+ AFB_WARNING("Unable to retrieve PCM information for PCM -> %s",pDeviceURIPCM);
+ snd_pcm_close(pPcmHandle);
+ continue;
+ }
+
+ snd_pcm_close(pPcmHandle);
+
+ // Retrieve HAL API name
+ err = RetrieveAssociatedHALAPIName(&endpointInfo);
+ if (err) {
+ AFB_WARNING("SetVolume will fail without HAL association ->%s",endpointInfo.deviceURI);
+ // Choose not to skip anyhow...
+ }
+ }
+ else if (IsPulseDomain(pDeviceURIDomain)) {
+ // Pulse domain
+ // For now display name is device URI directly, could extrapolated using more heuristics or even usins Pulse API later on
+ endpointInfo.deviceURIType = DEVICEURITYPE_PULSE;
+ }
+ else if (IsGStreamerDomain(pDeviceURIDomain)){
+ // GStreamer domain
+ // For now display name is device URI directly, could extrapolated using more heuristics or even usins GStreamer API later on
+ endpointInfo.deviceURIType = DEVICEURITYPE_GSTREAMER;
+ }
+ else if (IsExternalDomain(pDeviceURIDomain)){
+ // External domain
+ endpointInfo.deviceURIType = DEVICEURITYPE_EXTERNAL;
+ }
+ else {
+ // Unknown domain
+ AFB_WARNING("Unknown domain in device URI string -> %s",fullDeviceURI);
+ continue;
+ }
+
+ err = InitializeEndpointStates( &endpointInfo );
+ if (err) {
+ AFB_ERROR("Cannot initialize endpoint states for URI -> %s",fullDeviceURI);
+ continue;
+ }
+
+ strncpy(endpointInfo.deviceURI,pDeviceURIPCM,AUDIOHL_MAX_DEVICE_URI_LENGTH);
+ strncpy(endpointInfo.audioRole,in_pRoleName,AUDIOHL_MAX_AUDIOROLE_LENGTH);
+ endpointInfo.endpointID = CreateNewSourceID();
+ endpointInfo.type = ENDPOINTTYPE_SOURCE;
+
+ // add to structure to list of available source devices
+ GArray * pRoleSourceDeviceArray = g_ptr_array_index( g_AHLCtx.policyCtx.pSourceEndpoints, in_iRoleIndex );
+ g_array_append_val(pRoleSourceDeviceArray, endpointInfo);
+
+ } // for all devices
+
AFB_DEBUG ("Audio high-level - Enumerate sources done");
return 0;
}
-PUBLIC int EnumerateSinks() {
-
- // TODO: Use lower level services to build a list of available sink devices
-
+// For a given audio role
+PUBLIC int EnumerateSinks(json_object * in_jSinkArray, int in_iRoleIndex, char * in_pRoleName) {
+
+ int iNumberDevices = json_object_array_length(in_jSinkArray);
+
+ // Parse and validate list of available devices
+ for (unsigned int i = 0; i < iNumberDevices; i++)
+ {
+ char fullDeviceURI[AUDIOHL_MAX_DEVICE_URI_LENGTH]; // strtok is destructive
+ char * pDeviceURIDomain = NULL;
+ char * pFullDeviceURI = NULL;
+ char * pDeviceURIPCM = NULL;
+ int err = 0;
+ EndpointInfoT endpointInfo;
+
+ json_object * jSink = json_object_array_get_idx(in_jSinkArray,i);
+
+ // strip domain name from URI
+ pFullDeviceURI = (char*)json_object_get_string(jSink);
+ strncpy(fullDeviceURI,pFullDeviceURI,AUDIOHL_MAX_DEVICE_URI_LENGTH);
+ err = SeparateDomainFromDeviceURI(fullDeviceURI,&pDeviceURIDomain,&pDeviceURIPCM);
+ if (err)
+ {
+ AFB_WARNING("Invalid device URI string -> %s",fullDeviceURI);
+ continue;
+ }
+
+ // non ALSA URI are simply passed to application (no validation) at this time
+ // In Non ALSA case devices in config are assumed to be available, if not application can fallback on explicit device selection
+
+ endpointInfo.cardNum = -1;
+ endpointInfo.deviceNum = -1;
+ endpointInfo.cardNum = -1;
+ strncpy(endpointInfo.deviceName,pDeviceURIPCM,AUDIOHL_MAX_DEVICE_NAME_LENGTH);
+
+ if (IsAlsaDomain(pDeviceURIDomain))
+ {
+ // TODO: Missing support for loose name matching
+ // This will require using ALSA hints to get PCM names
+ // And would iterate over all available devices matching string (possibly all if no filtering is desired for a certain role)
+
+ snd_pcm_t * pPcmHandle = NULL;
+
+ // get PCM handle
+ err = snd_pcm_open(&pPcmHandle, pDeviceURIPCM, SND_PCM_STREAM_PLAYBACK, 0);
+ if (err < 0)
+ {
+ AFB_NOTICE("Alsa PCM device was not found -> %s", pDeviceURIPCM);
+ continue;
+ }
+
+ err = FillALSAPCMInfo(pPcmHandle,&endpointInfo);
+ if (err) {
+ AFB_WARNING("Unable to retrieve PCM information for PCM -> %s",pDeviceURIPCM);
+ snd_pcm_close(pPcmHandle);
+ continue;
+ }
+
+ snd_pcm_close(pPcmHandle);
+
+ // Retrieve HAL API name
+ err = RetrieveAssociatedHALAPIName(&endpointInfo);
+ if (err) {
+ //AFB_WARNING("SetVolume w fail without HAL association ->%s",endpointInfo.deviceURI);
+ continue;
+ }
+ }
+ else if (IsPulseDomain(pDeviceURIDomain)) {
+ // Pulse domain
+ // For now display name is device URI directly, could extrapolated using more heuristics or even usins Pulse API later on
+ endpointInfo.deviceURIType = DEVICEURITYPE_PULSE;
+
+ }
+ else if (IsGStreamerDomain(pDeviceURIDomain)){
+ // GStreamer domain
+ // For now display name is device URI directly, could extrapolated using more heuristics or even usins GStreamer API later on
+ endpointInfo.deviceURIType = DEVICEURITYPE_GSTREAMER;
+ }
+ else if (IsExternalDomain(pDeviceURIDomain)){
+ // External domain
+
+ endpointInfo.deviceURIType = DEVICEURITYPE_EXTERNAL;
+ }
+ else {
+ // Unknown domain
+ AFB_WARNING("Unknown domain in device URI string -> %s",fullDeviceURI);
+ continue;
+ }
+
+ err = InitializeEndpointStates( &endpointInfo );
+ if (err) {
+ AFB_ERROR("Cannot initialize endpoint states for URI -> %s",fullDeviceURI);
+ continue;
+ }
+
+ strncpy(endpointInfo.deviceURI,pDeviceURIPCM,AUDIOHL_MAX_DEVICE_URI_LENGTH);
+ strncpy(endpointInfo.audioRole,in_pRoleName,AUDIOHL_MAX_AUDIOROLE_LENGTH);
+ endpointInfo.endpointID = CreateNewSinkID();
+ endpointInfo.type = ENDPOINTTYPE_SINK;
+
+ // add to structure to list of available source devices
+ GArray * pRoleSinkDeviceArray = g_ptr_array_index( g_AHLCtx.policyCtx.pSinkEndpoints, in_iRoleIndex );
+ g_array_append_val(pRoleSinkDeviceArray, endpointInfo);
+
+ } // for all devices
+
AFB_DEBUG ("Audio high-level - Enumerate sinks done");
return 0;
}
-
diff --git a/src/ahl-interface.h b/src/ahl-interface.h
new file mode 100644
index 0000000..781bb05
--- /dev/null
+++ b/src/ahl-interface.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2017 "Audiokinetic Inc"
+ * Author Francois Thibault <fthibault@audiokinetic.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef AHL_INTERFACE_INCLUDE
+#define AHL_INTERFACE_INCLUDE
+
+#define UNDEFINED_ID -1
+
+typedef int endpointID_t;
+typedef int streamID_t;
+
+typedef enum EndpointType {
+ ENDPOINTTYPE_SOURCE = 0, // source devices
+ ENDPOINTTYPE_SINK, // sink devices
+ ENDPOINTTYPE_MAXVALUE // Enum count, keep at the end
+} EndpointTypeT;
+
+// Standardized name for common audio roles (not enforced in any way, just helps system being more compatible)
+#define AUDIOROLE_WARNING "warning" // Safety-relevant or critical alerts/alarms
+#define AUDIOROLE_GUIDANCE "guidance" // Important user information where user action is expected (e.g. navigation instruction)
+#define AUDIOROLE_NOTIFICATION "notification" // HMI or else notifications (e.g. touchscreen events, speech recognition on/off,...)
+#define AUDIOROLE_COMMUNICATION "communications" // Voice communications (e.g. handsfree, speech recognition)
+#define AUDIOROLE_ENTERTAINMENT "entertainment" // Multimedia content (e.g. tuner, media player, etc.)
+#define AUDIOROLE_SYSTEM "system" // System level content or development
+#define AUDIOROLE_STARTUP "startup" // Early (startup) sound
+#define AUDIOROLE_SHUTDOWN "shutdown" // Late (shutdown) sound
+#define AUDIOROLE_NONE "none" // Non-assigned / legacy applications
+
+typedef enum DeviceURIType {
+ DEVICEURITYPE_ALSA_HW = 0, // Alsa hardware device URI
+ DEVICEURITYPE_ALSA_DMIX, // Alsa Dmix device URI (only for playback devices)
+ DEVICEURITYPE_ALSA_DSNOOP, // Alsa DSnoop device URI (only for capture devices)
+ DEVICEURITYPE_ALSA_SOFTVOL, // Alsa softvol device URI
+ DEVICEURITYPE_ALSA_OTHER, // Alsa domain URI device of unspecified type
+ DEVICEURITYPE_PULSE, // Pulse device URI
+ DEVICEURITYPE_GSTREAMER, // GStreamer device URI
+ DEVICEURITYPE_EXTERNAL, // Device URI for external ECU device
+ DEVICEURITYPE_MAXVALUE // Enum count, keep at the end
+} DeviceURITypeT;
+
+// Standardized list of properties (string used for extensibility)
+#define AUDIOHL_PROPERTY_BALANCE "balance"
+#define AUDIOHL_PROPERTY_FADE "fade"
+#define AUDIOHL_PROPERTY_EQ_LOW "eq_low"
+#define AUDIOHL_PROPERTY_EQ_MID "eq_mid"
+#define AUDIOHL_PROPERTY_EQ_HIGH "eq_high"
+
+// Standardized list of state names/values (string used for extensibility)
+#define AUDIOHL_STATE_NAME_ACTIVE "active"
+#define AUDIOHL_STATE_NAME_MUTE "mute"
+#define AUDIOHL_STATE_VALUE_ON "on"
+#define AUDIOHL_STATE_VALUE_OFF "off"
+
+// Known audio domain string definitions (for configuration file format)
+#define AUDIOHL_DOMAIN_ALSA "Alsa"
+#define AUDIOHL_DOMAIN_PULSE "Pulse"
+#define AUDIOHL_DOMAIN_GSTREAMER "GStreamer"
+#define AUDIOHL_DOMAIN_EXTERNAL "External"
+
+#endif // AHL_INTERFACE_INCLUDE
diff --git a/src/ahl-policy.c b/src/ahl-policy.c
new file mode 100644
index 0000000..7741afd
--- /dev/null
+++ b/src/ahl-policy.c
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2017 "Audiokinetic Inc"
+ * Author Francois Thibault <fthibault@audiokinetic.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <string.h>
+
+#include "ahl-binding.h"
+
+
+// This file provides example of custom, business logic driven policy actions that can affect behavior of the high level
+// TODO: Currently only isolated in separate source file. Objective is to make this to at least a shared lib plug-in
+// Going a step further would be to implement this within afb-controler policy plug-in (would require bi-directional access to HLB context)
+
+extern AHLCtxT g_AHLCtx; // TODO: Cannot stay if moved to external module
+
+// Attribute of high-level binding (parsed), policy enforced
+typedef enum InterruptedBehavior {
+ INTERRUPTEDBEHAVIOR_CONTINUE = 0, // Continue to play when interrupted (e.g. media will be ducked)
+ INTERRUPTEDBEHAVIOR_CANCEL, // Abort playback when interrupted (e.g. non-important HMI feedback that does not make sense later)
+ INTERRUPTEDBEHAVIOR_PAUSE, // Pause source when interrupted, to be resumed afterwards (e.g. non-temporal guidance)
+ INTERRUPTEDBEHAVIOR_MAXVALUE, // Enum count, keep at the end
+} InterruptedBehaviorT;
+
+PUBLIC int Policy_OpenStream(char *pAudioRole, EndpointTypeT endpointType, endpointID_t endpointID)
+{
+ return 1; // Policy allowed
+}
+
+PUBLIC int Policy_SetVolume(EndpointTypeT endpointType, endpointID_t endpointID, char *volumeStr, int rampTimeMS)
+{
+ return 1; // Policy allowed
+}
+
+PUBLIC int Policy_SetProperty(EndpointTypeT endpointType, endpointID_t endpointID, char *propertyName, char *propValueStr, int rampTimeMS)
+{
+ return 1; // Policy allowed
+}
+
+
+PUBLIC int Policy_SetState(EndpointTypeT endpointType, endpointID_t endpointID, char *pStateName, char *pStateValue)
+{
+
+
+ //Mute
+ if(strcmp(pStateName, AUDIOHL_STATE_NAME_MUTE) == 0)
+ {
+ if(strcmp(pStateName, AUDIOHL_STATE_NAME_MUTE) == 0)
+
+
+ return AUDIOHL_POLICY_ACCEPT;
+ }
+
+ //Retrieve global context
+
+
+ //Active rule check
+
+ //Ducking rule settings
+
+
+ return AUDIOHL_POLICY_ACCEPT;
+}
+
+PUBLIC int Policy_PostSoundEvent(char *eventName, char *audioRole, char *mediaName, void *audioContext)
+{
+ return 1; // Policy allowed
+}
+
+PUBLIC int Policy_AudioDeviceChange()
+{
+ // Allow or disallow a new audio device
+ // Raise events to engage device switching
+ // Policy can also assign audio role(s) for device
+ return 1; // Policy allowed
+} \ No newline at end of file