finish nimble blehr porting and do some kernel improve

This commit is contained in:
SheldonDai
2019-10-09 12:15:59 +08:00
parent 45c18c896c
commit aad1564e09
663 changed files with 314162 additions and 68 deletions

View File

@@ -0,0 +1,5 @@
# Dummy NPL build
*.o
/porting/examples/dummy/dummy
/porting/examples/linux/nimble-linux
/porting/examples/linux_blemesh/nimble-linux-blemesh

View File

@@ -0,0 +1,28 @@
# Can't easily add license to rat-excludes file.
.rat-excludes
# Ignore documentation folder
docs
# Non-source files
RELEASE_NOTES.md
.gitignore
README.md
pts-gap.txt
pts-gatt.txt
pts-l2cap.txt
pts-sm.txt
94654-20170317-085122560.tpg
94654-20170317-085441153.pts
# tinycrypt - BSD License.
tinycrypt
# Bluetooth Mesh - Apache 2.0 License
mesh
# Queue implementation - BSD License
queue.h
# mbuf implementation - BSD License
os_mbuf.c

View File

@@ -0,0 +1,133 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you 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.
language: go
_addons: &addon_conf
apt:
sources:
- ubuntu-toolchain-r-test
packages:
- gcc-multilib
- gcc-7-multilib
go:
- "1.11"
git:
depth: false
matrix:
include:
# newt build <targets>
- os: linux
addons: *addon_conf
env:
- TEST=BUILD_TARGETS
- VM_AMOUNT=4
- TARGET_SET=1
- os: linux
addons: *addon_conf
env:
- TEST=BUILD_TARGETS
- VM_AMOUNT=4
- TARGET_SET=2
- os: linux
addons: *addon_conf
env:
- TEST=BUILD_TARGETS
- VM_AMOUNT=4
- TARGET_SET=3
- os: linux
addons: *addon_conf
env:
- TEST=BUILD_TARGETS
- VM_AMOUNT=4
- TARGET_SET=4
# newt test all (Linux)
- os: linux
addons: *addon_conf
env:
- TEST=TEST_ALL
- VM_AMOUNT=2
- TARGET_SET=1
- os: linux
addons: *addon_conf
env:
- TEST=TEST_ALL
- VM_AMOUNT=2
- TARGET_SET=2
# ports
- os: linux
addons: *addon_conf
env:
- TEST=BUILD_PORTS
- VM_AMOUNT=1
- TARGET_SET=1
# newt test all
- os: osx
osx_image: xcode9.2
env:
- TEST=TEST_ALL
- VM_AMOUNT=3
- TARGET_SET=1
- os: osx
osx_image: xcode9.2
env:
- TEST=TEST_ALL
- VM_AMOUNT=3
- TARGET_SET=2
- os: osx
osx_image: xcode9.2
env:
- TEST=TEST_ALL
- VM_AMOUNT=3
- TARGET_SET=3
before_install:
- printenv
- export GOPATH=$HOME/gopath
- go version
install:
- git clone https://github.com/runtimeco/mynewt-travis-ci $HOME/ci
- chmod +x $HOME/ci/*.sh
- $HOME/ci/${TRAVIS_OS_NAME}_travis_install.sh
before_script:
- newt version
- gcc --version
- if [ "${TEST}" != "TEST_ALL" ]; then arm-none-eabi-gcc --version; fi
- cp -R $HOME/ci/mynewt-nimble-project.yml project.yml
- mkdir -p targets
- cp -R $HOME/ci/mynewt-nimble-targets targets
- $HOME/ci/prepare_test.sh $VM_AMOUNT
- mkdir -p repos && pushd repos/
- git clone --depth=1 https://github.com/apache/mynewt-core apache-mynewt-core
- git clone --depth=1 https://github.com/JuulLabs-OSS/mcuboot mcuboot
- popd
script:
- $HOME/ci/run_test.sh
cache:
directories:
- $HOME/TOOLCHAIN
- $HOME/Library/Caches/Homebrew

View File

@@ -0,0 +1,267 @@
# Coding Style for Apache NimBLE
Apache NimBLE project is part of Apache Mynewt projct and follows its coding
style.
# Coding Style for Apache Mynewt Core
This document is meant to define the coding style for Apache Mynewt, and
all subprojects of Apache Mynewt. This covers C and Assembly coding
conventions, *only*. Other languages (such as Go), have their own
coding conventions.
## Headers
* All files that are newly written, should have the Apache License clause
at the top of them.
* For files that are copied from another source, but contain an Apache
compatible license, the original license header shall be maintained.
* For more information on applying the Apache license, the definitive
source is here: http://www.apache.org/dev/apply-license.html
* The Apache License clause for the top of files is as follows:
```no-highlight
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
```
## Whitespace and Braces
* Code must be indented to 4 spaces, tabs should not be used.
* Do not add whitespace at the end of a line.
* Put space after keywords (for, if, return, switch, while).
* for, else, if, while statements must have braces around their
code blocks, i.e., do:
```
if (x) {
assert(0);
} else {
assert(0);
}
```
Not:
```
if (x)
assert(0);
else
assert(0);
```
* Braces for statements must be on the same line as the statement. Good:
```
for (i = 0; i < 10; i++) {
if (i == 5) {
break;
} else {
continue;
}
}
```
Not:
```
for (i = 0; i < 10; i++)
{ <-- brace must be on same line as for
if (i == 5) {
break;
} <-- no new line between else
else {
continue;
}
}
```
* After a function declaration, the braces should be on a newline, i.e. do:
```
static void *
function(int var1, int var2)
{
```
not:
```
static void *
function(int var1, int var2) {
```
## Line Length and Wrap
* Line length should never exceed 79 columns.
* When you have to wrap a long statement, put the operator at the end of the
line. i.e.:
```
if (x &&
y == 10 &&
b)
```
Not:
```
if (x
&& y == 10
&& b)
```
## Comments
* No C++ style comments allowed.
* When using a single line comment, put it above the line of code that you
intend to comment, i.e., do:
```
/* check variable */
if (a) {
```
Not:
```
if (a) { /* check variable */
```
* All public APIs should be commented with Doxygen style comments describing
purpose, parameters and return values. Private APIs need not be documented.
## Header files
* Header files must contain the following structure:
* Apache License (see above)
* ```#ifdef``` aliasing, to prevent multiple includes
* ```#include``` directives for other required header files
* ```#ifdef __cplusplus``` wrappers to maintain C++ friendly APIs
* Contents of the header file
* ```#ifdef``` aliasing, shall be in the following format, where
the package name is "os" and the file name is "callout.h":
```no-highlight
#ifndef _OS_CALLOUT_H
#define _OS_CALLOUT_H
```
* ```#include``` directives must happen prior to the cplusplus
wrapper.
* The cplusplus wrapper must have the following format, and precedes
any contents of the header file:
```no-highlight
#ifdef __cplusplus
#extern "C" {
##endif
```
## Naming
* Names of functions, structures and variables must be in all lowercase.
* Names should be as short as possible, but no shorter.
* Globally visible names must be prefixed with the name of the module,
followed by the '_' character, i.e.:
```
os_callout_init(&c)
```
Not:
```
callout_init(c)
```
## Functions
* No spaces after function names when calling a function, i.e, do:
```
rc = function(a)
```
Not:
```
rc = function (a)
```
* Arguments to function calls should have spaces between the comma, i.e. do:
```
rc = function(a, b)
```
Not:
```
rc = function(a,b)
```
* The function type must be on a line by itself preceding the function, i.e. do:
```
static void *
function(int var1, int var2)
{
```
Not:
```
static void *function(int var1, int var2)
{
```
* In general, for functions that return values that denote success or error, 0
shall be success, and non-zero shall be the failure code.
## Variables and Macros
* Do not use typedefs for structures. This makes it impossible for
applications to use pointers to those structures opaquely.
* typedef may be used for non-structure types, where it is beneficial to
hide or alias the underlying type used (e.g. ```os_time_t```.) Indicate
typedefs by applying the ```_t``` marker to them.
* Place all function-local variable definitions at the top of the function body, before any statements.
## Compiler Directives
* Code must compile cleanly with -Wall enabled.

View File

@@ -0,0 +1,217 @@
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 {yyyy} {name of copyright owner}
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
This product bundles queue.h 8.5, which is available under the "3-clause BSD"
license. For details, see porting/nimble/include/os/queue.h
This product partly derives from FreeBSD, which is available under the
"3-clause BSD" license. For details, see:
* porting/nimble/src/os_mbuf.c
This product bundles Gary S. Brown's CRC32 implementation, which is available
under the following license:
COPYRIGHT (C) 1986 Gary S. Brown. You may use this program, or
code or tables extracted from it, as desired without restriction.
This product bundles tinycrypt, which is available under the "3-clause BSD"
license. For details, and bundled files see:
* ext/tinycrypt/LICENSE

View File

@@ -0,0 +1,8 @@
Apache Mynewt NimBLE
Copyright 2015-2018 The Apache Software Foundation
This product includes software developed at
The Apache Software Foundation (http://www.apache.org/).
Portions of this software were developed at
Runtime Inc, copyright 2015.

View File

@@ -0,0 +1,173 @@
<!--
#
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you 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.
#
-->
<img src="http://mynewt.apache.org/img/logo.svg" width="250" alt="Apache Mynewt">
## Overview
Apache NimBLE is an open-source Bluetooth 5.0 stack (both Host & Controller)
that completely replaces the proprietary SoftDevice on Nordic chipsets. It is
part of [Apache Mynewt project](https://github.com/apache/mynewt-core).
Features highlight:
- Support for 251 byte packet size
- Support for all 4 roles concurrently - Broadcaster, Observer, Peripheral and Central
- Support for up to 32 simultaneous connections.
- Legacy and SC (secure connections) SMP support (pairing and bonding).
- Advertising Extensions.
- Periodic Advertising.
- Coded (aka Long Range) and 2M PHYs.
- Bluetooth Mesh.
## Supported hardware
Controller supports Nordic nRF51 and nRF52 chipsets. Host runs on any board
and architecture [supported](https://github.com/apache/mynewt-core#overview)
by Apache Mynewt OS.
## Browsing
If you are browsing around the source tree, and want to see some of the
major functional chunks, here are a few pointers:
- nimble/controller: Contains code for controller including Link Layer and HCI implementation
([controller](https://github.com/apache/mynewt-nimble/tree/master/nimble/controller))
- nimble/drivers: Contains drivers for supported radio transceivers (Nordic nRF51 and nRF52)
([drivers](https://github.com/apache/mynewt-nimble/tree/master/nimble/drivers))
- nimble/host: Contains code for host subsystem. This includes protocols like
L2CAP and ATT, support for HCI commands and events, Generic Access Profile (GAP),
Generic Attribute Profile (GATT) and Security Manager (SM).
([host](https://github.com/apache/mynewt-nimble/tree/master/nimble/host))
- nimble/host/mesh: Contains code for Bluetooth Mesh subsystem.
([mesh](https://github.com/apache/mynewt-nimble/tree/master/nimble/host/mesh))
- nimble/transport: Contains code for supported transport protocols between host
and controller. This includes UART, emSPI and RAM (used in combined build when
host and controller run on same CPU)
([transport](https://github.com/apache/mynewt-nimble/tree/master/nimble/transport))
- porting: Contains implementation of NimBLE Porting Layer (NPL) for supported
operating systems
([porting](https://github.com/apache/mynewt-nimble/tree/master/porting))
- ext: Contains external libraries used by NimBLE. Those are used if not
provided by OS
([ext](https://github.com/apache/mynewt-nimble/tree/master/ext))
- kernel: Contains the core of the RTOS ([kernel/os](https://github.com/apache/mynewt-core/tree/master/kernel/os))
## Sample Applications
There are also some sample applications that show how to Apache Mynewt NimBLE
stack. These sample applications are located in the `apps/` directory of
Apache Mynewt [repo](https://github.com/apache/mynewt-core). Some examples:
* [blecent](https://github.com/apache/mynewt-core/tree/master/apps/blecent):
A basic central device with no user interface. This application scans for
a peripheral that supports the alert notification service (ANS). Upon
discovering such a peripheral, blecent connects and performs a characteristic
read, characteristic write, and notification subscription.
* [blehci](https://github.com/apache/mynewt-core/tree/master/apps/blehci):
Implements a BLE controller-only application. A separate host-only
implementation, such as Linux's BlueZ, can interface with this application via
HCI over UART.
* [bleprph](https://github.com/apache/mynewt-core/tree/master/apps/bleprph): An
implementation of a minimal BLE peripheral.
* [btshell](https://github.com/apache/mynewt-core/tree/master/apps/btshell): A
shell-like application allowing to configure and use most of NimBLE
functionality from command line.
* [bleuart](https://github.com/apache/mynewt-core/tree/master/apps/bleuart):
Implements a simple BLE peripheral that supports the Nordic
UART / Serial Port Emulation service
(https://developer.nordicsemi.com/nRF5_SDK/nRF51_SDK_v8.x.x/doc/8.0.0/s110/html/a00072.html).
* [test](https://github.com/apache/mynewt-core/tree/master/apps/test): Test
project which can be compiled either with the simulator, or on a per-architecture basis.
Test will run all the package's unit tests.
# Getting Help
If you are having trouble using or contributing to Apache Mynewt NimBLE, or just
want to talk to a human about what you're working on, you can contact us via the
[developers mailing list](mailto:dev@mynewt.apache.org).
Although not a formal channel, you can also find a number of core developers
on the #mynewt channel on Freenode IRC or #general channel on [Mynewt Slack](https://join.slack.com/mynewt/shared_invite/MTkwMTg1ODM1NTg5LTE0OTYxNzQ4NzQtZTU1YmNhYjhkMg)
Also, be sure to checkout the [Frequently Asked Questions](https://mynewt.apache.org/faq/answers)
for some help troubleshooting first.
# Contributing
Anybody who works with Apache Mynewt can be a contributing member of the
community that develops and deploys it. The process of releasing an operating
system for microcontrollers is never done: and we welcome your contributions
to that effort.
More information can be found at the Community section of the Apache Mynewt
website, located [here](https://mynewt.apache.org/community).
## Pull Requests
Apache Mynewt welcomes pull request via Github. Discussions are done on Github,
but depending on the topic, can also be relayed to the official Apache Mynewt
developer mailing list dev@mynewt.apache.org.
If you are suggesting a new feature, please email the developer list directly,
with a description of the feature you are planning to work on.
## Filing Bugs
Bugs can be filed on the
[Apache Mynewt NimBLE Issues](https://github.com/apache/mynewt-nimble/issues).
Please label the issue as a "Bug".
Where possible, please include a self-contained reproduction case!
## Feature Requests
Feature requests should also be filed on the
[Apache Mynewt NimBLE Bug Tracker](https://github.com/apache/mynewt-nimble/issues).
Please label the issue as a "Feature" or "Enhancement" depending on the scope.
## Writing Tests
We love getting newt tests! Apache Mynewt is a huge undertaking, and improving
code coverage is a win for every Apache Mynewt user.
<!--
TODO
## Writing Documentation
Contributing to documentation (in addition to writing tests), is a great way
to get involved with the Apache Mynewt project.
The Mynewt NimBLE documentation is found in [/docs](/docs).
-->
# License
The code in this repository is all under either the Apache 2 license, or a
license compatible with the Apache 2 license. See the LICENSE file for more
information.

View File

@@ -0,0 +1,27 @@
# RELEASE NOTES
16 July 2019 - Apache NimBLE v1.2.0
For full release notes, please visit the
[Apache Mynewt Wiki](https://cwiki.apache.org/confluence/display/MYNEWT/Release+Notes).
Apache NimBLE is an open-source Bluetooth 5.0 stack (both Host & Controller) that completely
replaces the proprietary SoftDevice on Nordic chipsets.
New features in this version of NimBLE include:
* Perdiodic Advertising support with up to 1650 bytes of data (scanner and advertiser)
* Support for scan request notification in GAP API
* Updated host qualification ID
* Qualification related bugfixes
* GAP API doxygen documentation update
* BLE Mesh improvements - fixes and resync with latest Zephyr code
* RIOT OS port fixes and improvements
* btshell sample application improvements
* improvements for bttester application
* Controller duplicates filtering improvements
* Memory and CPU usage optimizations in controller
If working on next-generation RTOS and Bluetooth protocol stack
sounds exciting to you, get in touch, by sending a mail to the Apache Mynewt
Developer's list, dev@mynewt.apache.org.

View File

@@ -0,0 +1,37 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you 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.
#
pkg.name: apps/blecent
pkg.type: app
pkg.description: Simple BLE central application.
pkg.author: "Apache Mynewt <dev@mynewt.apache.org>"
pkg.homepage: "http://mynewt.apache.org/"
pkg.keywords:
pkg.deps:
- "@apache-mynewt-core/kernel/os"
- "@apache-mynewt-core/sys/console/full"
- "@apache-mynewt-core/sys/log/full"
- "@apache-mynewt-core/sys/log/modlog"
- "@apache-mynewt-core/sys/stats/full"
- nimble/controller
- nimble/host
- nimble/host/util
- nimble/host/services/gap
- nimble/host/services/gatt
- nimble/host/store/ram
- nimble/transport/ram

View File

@@ -0,0 +1,111 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 H_BLECENT_
#define H_BLECENT_
#include "os/mynewt.h"
#include "modlog/modlog.h"
#ifdef __cplusplus
extern "C" {
#endif
struct ble_hs_adv_fields;
struct ble_gap_conn_desc;
struct ble_hs_cfg;
union ble_store_value;
union ble_store_key;
#define BLECENT_SVC_ALERT_UUID 0x1811
#define BLECENT_CHR_SUP_NEW_ALERT_CAT_UUID 0x2A47
#define BLECENT_CHR_NEW_ALERT 0x2A46
#define BLECENT_CHR_SUP_UNR_ALERT_CAT_UUID 0x2A48
#define BLECENT_CHR_UNR_ALERT_STAT_UUID 0x2A45
#define BLECENT_CHR_ALERT_NOT_CTRL_PT 0x2A44
/** Misc. */
void print_bytes(const uint8_t *bytes, int len);
void print_mbuf(const struct os_mbuf *om);
char *addr_str(const void *addr);
void print_uuid(const ble_uuid_t *uuid);
void print_conn_desc(const struct ble_gap_conn_desc *desc);
void print_adv_fields(const struct ble_hs_adv_fields *fields);
/** Peer. */
struct peer_dsc {
SLIST_ENTRY(peer_dsc) next;
struct ble_gatt_dsc dsc;
};
SLIST_HEAD(peer_dsc_list, peer_dsc);
struct peer_chr {
SLIST_ENTRY(peer_chr) next;
struct ble_gatt_chr chr;
struct peer_dsc_list dscs;
};
SLIST_HEAD(peer_chr_list, peer_chr);
struct peer_svc {
SLIST_ENTRY(peer_svc) next;
struct ble_gatt_svc svc;
struct peer_chr_list chrs;
};
SLIST_HEAD(peer_svc_list, peer_svc);
struct peer;
typedef void peer_disc_fn(const struct peer *peer, int status, void *arg);
struct peer {
SLIST_ENTRY(peer) next;
uint16_t conn_handle;
/** List of discovered GATT services. */
struct peer_svc_list svcs;
/** Keeps track of where we are in the service discovery process. */
uint16_t disc_prev_chr_val;
struct peer_svc *cur_svc;
/** Callback that gets executed when service discovery completes. */
peer_disc_fn *disc_cb;
void *disc_cb_arg;
};
int peer_disc_all(uint16_t conn_handle, peer_disc_fn *disc_cb,
void *disc_cb_arg);
const struct peer_dsc *
peer_dsc_find_uuid(const struct peer *peer, const ble_uuid_t *svc_uuid,
const ble_uuid_t *chr_uuid, const ble_uuid_t *dsc_uuid);
const struct peer_chr *
peer_chr_find_uuid(const struct peer *peer, const ble_uuid_t *svc_uuid,
const ble_uuid_t *chr_uuid);
const struct peer_svc *
peer_svc_find_uuid(const struct peer *peer, const ble_uuid_t *uuid);
int peer_delete(uint16_t conn_handle);
int peer_add(uint16_t conn_handle);
int peer_init(int max_peers, int max_svcs, int max_chrs, int max_dscs);
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -0,0 +1,521 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
#include <assert.h>
#include <string.h>
#include "os/mynewt.h"
#include "bsp/bsp.h"
/* BLE */
#include "nimble/ble.h"
#include "controller/ble_ll.h"
#include "host/ble_hs.h"
#include "host/util/util.h"
/* RAM HCI transport. */
#include "transport/ram/ble_hci_ram.h"
/* Mandatory services. */
#include "services/gap/ble_svc_gap.h"
#include "services/gatt/ble_svc_gatt.h"
/* Application-specified header. */
#include "blecent.h"
static int blecent_gap_event(struct ble_gap_event *event, void *arg);
/**
* Application callback. Called when the read of the ANS Supported New Alert
* Category characteristic has completed.
*/
static int
blecent_on_read(uint16_t conn_handle,
const struct ble_gatt_error *error,
struct ble_gatt_attr *attr,
void *arg)
{
MODLOG_DFLT(INFO, "Read complete; status=%d conn_handle=%d", error->status,
conn_handle);
if (error->status == 0) {
MODLOG_DFLT(INFO, " attr_handle=%d value=", attr->handle);
print_mbuf(attr->om);
}
MODLOG_DFLT(INFO, "\n");
return 0;
}
/**
* Application callback. Called when the write to the ANS Alert Notification
* Control Point characteristic has completed.
*/
static int
blecent_on_write(uint16_t conn_handle,
const struct ble_gatt_error *error,
struct ble_gatt_attr *attr,
void *arg)
{
MODLOG_DFLT(INFO,
"Write complete; status=%d conn_handle=%d attr_handle=%d\n",
error->status, conn_handle, attr->handle);
return 0;
}
/**
* Application callback. Called when the attempt to subscribe to notifications
* for the ANS Unread Alert Status characteristic has completed.
*/
static int
blecent_on_subscribe(uint16_t conn_handle,
const struct ble_gatt_error *error,
struct ble_gatt_attr *attr,
void *arg)
{
MODLOG_DFLT(INFO, "Subscribe complete; status=%d conn_handle=%d "
"attr_handle=%d\n",
error->status, conn_handle, attr->handle);
return 0;
}
/**
* Performs three concurrent GATT operations against the specified peer:
* 1. Reads the ANS Supported New Alert Category characteristic.
* 2. Writes the ANS Alert Notification Control Point characteristic.
* 3. Subscribes to notifications for the ANS Unread Alert Status
* characteristic.
*
* If the peer does not support a required service, characteristic, or
* descriptor, then the peer lied when it claimed support for the alert
* notification service! When this happens, or if a GATT procedure fails,
* this function immediately terminates the connection.
*/
static void
blecent_read_write_subscribe(const struct peer *peer)
{
const struct peer_chr *chr;
const struct peer_dsc *dsc;
uint8_t value[2];
int rc;
/* Read the supported-new-alert-category characteristic. */
chr = peer_chr_find_uuid(peer,
BLE_UUID16_DECLARE(BLECENT_SVC_ALERT_UUID),
BLE_UUID16_DECLARE(BLECENT_CHR_SUP_NEW_ALERT_CAT_UUID));
if (chr == NULL) {
MODLOG_DFLT(ERROR, "Error: Peer doesn't support the Supported New "
"Alert Category characteristic\n");
goto err;
}
rc = ble_gattc_read(peer->conn_handle, chr->chr.val_handle,
blecent_on_read, NULL);
if (rc != 0) {
MODLOG_DFLT(ERROR, "Error: Failed to read characteristic; rc=%d\n",
rc);
goto err;
}
/* Write two bytes (99, 100) to the alert-notification-control-point
* characteristic.
*/
chr = peer_chr_find_uuid(peer,
BLE_UUID16_DECLARE(BLECENT_SVC_ALERT_UUID),
BLE_UUID16_DECLARE(BLECENT_CHR_ALERT_NOT_CTRL_PT));
if (chr == NULL) {
MODLOG_DFLT(ERROR, "Error: Peer doesn't support the Alert "
"Notification Control Point characteristic\n");
goto err;
}
value[0] = 99;
value[1] = 100;
rc = ble_gattc_write_flat(peer->conn_handle, chr->chr.val_handle,
value, sizeof value, blecent_on_write, NULL);
if (rc != 0) {
MODLOG_DFLT(ERROR, "Error: Failed to write characteristic; rc=%d\n",
rc);
}
/* Subscribe to notifications for the Unread Alert Status characteristic.
* A central enables notifications by writing two bytes (1, 0) to the
* characteristic's client-characteristic-configuration-descriptor (CCCD).
*/
dsc = peer_dsc_find_uuid(peer,
BLE_UUID16_DECLARE(BLECENT_SVC_ALERT_UUID),
BLE_UUID16_DECLARE(BLECENT_CHR_UNR_ALERT_STAT_UUID),
BLE_UUID16_DECLARE(BLE_GATT_DSC_CLT_CFG_UUID16));
if (dsc == NULL) {
MODLOG_DFLT(ERROR, "Error: Peer lacks a CCCD for the Unread Alert "
"Status characteristic\n");
goto err;
}
value[0] = 1;
value[1] = 0;
rc = ble_gattc_write_flat(peer->conn_handle, dsc->dsc.handle,
value, sizeof value, blecent_on_subscribe, NULL);
if (rc != 0) {
MODLOG_DFLT(ERROR, "Error: Failed to subscribe to characteristic; "
"rc=%d\n", rc);
goto err;
}
return;
err:
/* Terminate the connection. */
ble_gap_terminate(peer->conn_handle, BLE_ERR_REM_USER_CONN_TERM);
}
/**
* Called when service discovery of the specified peer has completed.
*/
static void
blecent_on_disc_complete(const struct peer *peer, int status, void *arg)
{
if (status != 0) {
/* Service discovery failed. Terminate the connection. */
MODLOG_DFLT(ERROR, "Error: Service discovery failed; status=%d "
"conn_handle=%d\n", status, peer->conn_handle);
ble_gap_terminate(peer->conn_handle, BLE_ERR_REM_USER_CONN_TERM);
return;
}
/* Service discovery has completed successfully. Now we have a complete
* list of services, characteristics, and descriptors that the peer
* supports.
*/
MODLOG_DFLT(ERROR, "Service discovery complete; status=%d "
"conn_handle=%d\n", status, peer->conn_handle);
/* Now perform three concurrent GATT procedures against the peer: read,
* write, and subscribe to notifications.
*/
blecent_read_write_subscribe(peer);
}
/**
* Initiates the GAP general discovery procedure.
*/
static void
blecent_scan(void)
{
uint8_t own_addr_type;
struct ble_gap_disc_params disc_params;
int rc;
/* Figure out address to use while advertising (no privacy for now) */
rc = ble_hs_id_infer_auto(0, &own_addr_type);
if (rc != 0) {
MODLOG_DFLT(ERROR, "error determining address type; rc=%d\n", rc);
return;
}
/* Tell the controller to filter duplicates; we don't want to process
* repeated advertisements from the same device.
*/
disc_params.filter_duplicates = 1;
/**
* Perform a passive scan. I.e., don't send follow-up scan requests to
* each advertiser.
*/
disc_params.passive = 1;
/* Use defaults for the rest of the parameters. */
disc_params.itvl = 0;
disc_params.window = 0;
disc_params.filter_policy = 0;
disc_params.limited = 0;
rc = ble_gap_disc(own_addr_type, BLE_HS_FOREVER, &disc_params,
blecent_gap_event, NULL);
if (rc != 0) {
MODLOG_DFLT(ERROR, "Error initiating GAP discovery procedure; rc=%d\n",
rc);
}
}
/**
* Indicates whether we should tre to connect to the sender of the specified
* advertisement. The function returns a positive result if the device
* advertises connectability and support for the Alert Notification service.
*/
static int
blecent_should_connect(const struct ble_gap_disc_desc *disc)
{
struct ble_hs_adv_fields fields;
int rc;
int i;
/* The device has to be advertising connectability. */
if (disc->event_type != BLE_HCI_ADV_RPT_EVTYPE_ADV_IND &&
disc->event_type != BLE_HCI_ADV_RPT_EVTYPE_DIR_IND) {
return 0;
}
rc = ble_hs_adv_parse_fields(&fields, disc->data, disc->length_data);
if (rc != 0) {
return rc;
}
/* The device has to advertise support for the Alert Notification
* service (0x1811).
*/
for (i = 0; i < fields.num_uuids16; i++) {
if (ble_uuid_u16(&fields.uuids16[i].u) == BLECENT_SVC_ALERT_UUID) {
return 1;
}
}
return 0;
}
/**
* Connects to the sender of the specified advertisement of it looks
* interesting. A device is "interesting" if it advertises connectability and
* support for the Alert Notification service.
*/
static void
blecent_connect_if_interesting(const struct ble_gap_disc_desc *disc)
{
int rc;
/* Don't do anything if we don't care about this advertiser. */
if (!blecent_should_connect(disc)) {
return;
}
/* Scanning must be stopped before a connection can be initiated. */
rc = ble_gap_disc_cancel();
if (rc != 0) {
MODLOG_DFLT(DEBUG, "Failed to cancel scan; rc=%d\n", rc);
return;
}
/* Try to connect the the advertiser. Allow 30 seconds (30000 ms) for
* timeout.
*/
rc = ble_gap_connect(BLE_OWN_ADDR_PUBLIC, &disc->addr, 30000, NULL,
blecent_gap_event, NULL);
if (rc != 0) {
MODLOG_DFLT(ERROR, "Error: Failed to connect to device; addr_type=%d "
"addr=%s\n",
disc->addr.type, addr_str(disc->addr.val));
return;
}
}
/**
* The nimble host executes this callback when a GAP event occurs. The
* application associates a GAP event callback with each connection that is
* established. blecent uses the same callback for all connections.
*
* @param event The event being signalled.
* @param arg Application-specified argument; unused by
* blecent.
*
* @return 0 if the application successfully handled the
* event; nonzero on failure. The semantics
* of the return code is specific to the
* particular GAP event being signalled.
*/
static int
blecent_gap_event(struct ble_gap_event *event, void *arg)
{
struct ble_gap_conn_desc desc;
struct ble_hs_adv_fields fields;
int rc;
switch (event->type) {
case BLE_GAP_EVENT_DISC:
rc = ble_hs_adv_parse_fields(&fields, event->disc.data,
event->disc.length_data);
if (rc != 0) {
return 0;
}
/* An advertisment report was received during GAP discovery. */
print_adv_fields(&fields);
/* Try to connect to the advertiser if it looks interesting. */
blecent_connect_if_interesting(&event->disc);
return 0;
case BLE_GAP_EVENT_CONNECT:
/* A new connection was established or a connection attempt failed. */
if (event->connect.status == 0) {
/* Connection successfully established. */
MODLOG_DFLT(INFO, "Connection established ");
rc = ble_gap_conn_find(event->connect.conn_handle, &desc);
assert(rc == 0);
print_conn_desc(&desc);
MODLOG_DFLT(INFO, "\n");
/* Remember peer. */
rc = peer_add(event->connect.conn_handle);
if (rc != 0) {
MODLOG_DFLT(ERROR, "Failed to add peer; rc=%d\n", rc);
return 0;
}
/* Perform service discovery. */
rc = peer_disc_all(event->connect.conn_handle,
blecent_on_disc_complete, NULL);
if (rc != 0) {
MODLOG_DFLT(ERROR, "Failed to discover services; rc=%d\n", rc);
return 0;
}
} else {
/* Connection attempt failed; resume scanning. */
MODLOG_DFLT(ERROR, "Error: Connection failed; status=%d\n",
event->connect.status);
blecent_scan();
}
return 0;
case BLE_GAP_EVENT_DISCONNECT:
/* Connection terminated. */
MODLOG_DFLT(INFO, "disconnect; reason=%d ", event->disconnect.reason);
print_conn_desc(&event->disconnect.conn);
MODLOG_DFLT(INFO, "\n");
/* Forget about peer. */
peer_delete(event->disconnect.conn.conn_handle);
/* Resume scanning. */
blecent_scan();
return 0;
case BLE_GAP_EVENT_DISC_COMPLETE:
MODLOG_DFLT(INFO, "discovery complete; reason=%d\n",
event->disc_complete.reason);
return 0;
case BLE_GAP_EVENT_ENC_CHANGE:
/* Encryption has been enabled or disabled for this connection. */
MODLOG_DFLT(INFO, "encryption change event; status=%d ",
event->enc_change.status);
rc = ble_gap_conn_find(event->enc_change.conn_handle, &desc);
assert(rc == 0);
print_conn_desc(&desc);
return 0;
case BLE_GAP_EVENT_NOTIFY_RX:
/* Peer sent us a notification or indication. */
MODLOG_DFLT(INFO, "received %s; conn_handle=%d attr_handle=%d "
"attr_len=%d\n",
event->notify_rx.indication ?
"indication" :
"notification",
event->notify_rx.conn_handle,
event->notify_rx.attr_handle,
OS_MBUF_PKTLEN(event->notify_rx.om));
/* Attribute data is contained in event->notify_rx.attr_data. */
return 0;
case BLE_GAP_EVENT_MTU:
MODLOG_DFLT(INFO, "mtu update event; conn_handle=%d cid=%d mtu=%d\n",
event->mtu.conn_handle,
event->mtu.channel_id,
event->mtu.value);
return 0;
case BLE_GAP_EVENT_REPEAT_PAIRING:
/* We already have a bond with the peer, but it is attempting to
* establish a new secure link. This app sacrifices security for
* convenience: just throw away the old bond and accept the new link.
*/
/* Delete the old bond. */
rc = ble_gap_conn_find(event->repeat_pairing.conn_handle, &desc);
assert(rc == 0);
ble_store_util_delete_peer(&desc.peer_id_addr);
/* Return BLE_GAP_REPEAT_PAIRING_RETRY to indicate that the host should
* continue with the pairing operation.
*/
return BLE_GAP_REPEAT_PAIRING_RETRY;
default:
return 0;
}
}
static void
blecent_on_reset(int reason)
{
MODLOG_DFLT(ERROR, "Resetting state; reason=%d\n", reason);
}
static void
blecent_on_sync(void)
{
int rc;
/* Make sure we have proper identity address set (public preferred) */
rc = ble_hs_util_ensure_addr(0);
assert(rc == 0);
/* Begin scanning for a peripheral to connect to. */
blecent_scan();
}
/**
* main
*
* All application logic and NimBLE host work is performed in default task.
*
* @return int NOTE: this function should never return!
*/
int
main(void)
{
int rc;
/* Initialize OS */
sysinit();
/* Configure the host. */
ble_hs_cfg.reset_cb = blecent_on_reset;
ble_hs_cfg.sync_cb = blecent_on_sync;
ble_hs_cfg.store_status_cb = ble_store_util_status_rr;
/* Initialize data structures to track connected peers. */
rc = peer_init(MYNEWT_VAL(BLE_MAX_CONNECTIONS), 64, 64, 64);
assert(rc == 0);
/* Set the default device name. */
rc = ble_svc_gap_device_name_set("nimble-blecent");
assert(rc == 0);
/* os start should never return. If it does, this should be an error */
while (1) {
os_eventq_run(os_eventq_dflt_get());
}
return 0;
}

View File

@@ -0,0 +1,209 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
#include <assert.h>
#include <stdio.h>
#include <string.h>
#include "host/ble_hs.h"
#include "host/ble_uuid.h"
#include "blecent.h"
/**
* Utility function to log an array of bytes.
*/
void
print_bytes(const uint8_t *bytes, int len)
{
int i;
for (i = 0; i < len; i++) {
MODLOG_DFLT(DEBUG, "%s0x%02x", i != 0 ? ":" : "", bytes[i]);
}
}
void
print_mbuf(const struct os_mbuf *om)
{
int colon;
colon = 0;
while (om != NULL) {
if (colon) {
MODLOG_DFLT(DEBUG, ":");
} else {
colon = 1;
}
print_bytes(om->om_data, om->om_len);
om = SLIST_NEXT(om, om_next);
}
}
char *
addr_str(const void *addr)
{
static char buf[6 * 2 + 5 + 1];
const uint8_t *u8p;
u8p = addr;
sprintf(buf, "%02x:%02x:%02x:%02x:%02x:%02x",
u8p[5], u8p[4], u8p[3], u8p[2], u8p[1], u8p[0]);
return buf;
}
void
print_uuid(const ble_uuid_t *uuid)
{
char buf[BLE_UUID_STR_LEN];
MODLOG_DFLT(DEBUG, "%s", ble_uuid_to_str(uuid, buf));
}
/**
* Logs information about a connection to the console.
*/
void
print_conn_desc(const struct ble_gap_conn_desc *desc)
{
MODLOG_DFLT(DEBUG, "handle=%d our_ota_addr_type=%d our_ota_addr=%s ",
desc->conn_handle, desc->our_ota_addr.type,
addr_str(desc->our_ota_addr.val));
MODLOG_DFLT(DEBUG, "our_id_addr_type=%d our_id_addr=%s ",
desc->our_id_addr.type, addr_str(desc->our_id_addr.val));
MODLOG_DFLT(DEBUG, "peer_ota_addr_type=%d peer_ota_addr=%s ",
desc->peer_ota_addr.type, addr_str(desc->peer_ota_addr.val));
MODLOG_DFLT(DEBUG, "peer_id_addr_type=%d peer_id_addr=%s ",
desc->peer_id_addr.type, addr_str(desc->peer_id_addr.val));
MODLOG_DFLT(DEBUG, "conn_itvl=%d conn_latency=%d supervision_timeout=%d "
"encrypted=%d authenticated=%d bonded=%d",
desc->conn_itvl, desc->conn_latency,
desc->supervision_timeout,
desc->sec_state.encrypted,
desc->sec_state.authenticated,
desc->sec_state.bonded);
}
void
print_adv_fields(const struct ble_hs_adv_fields *fields)
{
char s[BLE_HS_ADV_MAX_SZ];
const uint8_t *u8p;
int i;
if (fields->flags != 0) {
MODLOG_DFLT(DEBUG, " flags=0x%02x\n", fields->flags);
}
if (fields->uuids16 != NULL) {
MODLOG_DFLT(DEBUG, " uuids16(%scomplete)=",
fields->uuids16_is_complete ? "" : "in");
for (i = 0; i < fields->num_uuids16; i++) {
print_uuid(&fields->uuids16[i].u);
MODLOG_DFLT(DEBUG, " ");
}
MODLOG_DFLT(DEBUG, "\n");
}
if (fields->uuids32 != NULL) {
MODLOG_DFLT(DEBUG, " uuids32(%scomplete)=",
fields->uuids32_is_complete ? "" : "in");
for (i = 0; i < fields->num_uuids32; i++) {
print_uuid(&fields->uuids32[i].u);
MODLOG_DFLT(DEBUG, " ");
}
MODLOG_DFLT(DEBUG, "\n");
}
if (fields->uuids128 != NULL) {
MODLOG_DFLT(DEBUG, " uuids128(%scomplete)=",
fields->uuids128_is_complete ? "" : "in");
for (i = 0; i < fields->num_uuids128; i++) {
print_uuid(&fields->uuids128[i].u);
MODLOG_DFLT(DEBUG, " ");
}
MODLOG_DFLT(DEBUG, "\n");
}
if (fields->name != NULL) {
assert(fields->name_len < sizeof s - 1);
memcpy(s, fields->name, fields->name_len);
s[fields->name_len] = '\0';
MODLOG_DFLT(DEBUG, " name(%scomplete)=%s\n",
fields->name_is_complete ? "" : "in", s);
}
if (fields->tx_pwr_lvl_is_present) {
MODLOG_DFLT(DEBUG, " tx_pwr_lvl=%d\n", fields->tx_pwr_lvl);
}
if (fields->slave_itvl_range != NULL) {
MODLOG_DFLT(DEBUG, " slave_itvl_range=");
print_bytes(fields->slave_itvl_range, BLE_HS_ADV_SLAVE_ITVL_RANGE_LEN);
MODLOG_DFLT(DEBUG, "\n");
}
if (fields->svc_data_uuid16 != NULL) {
MODLOG_DFLT(DEBUG, " svc_data_uuid16=");
print_bytes(fields->svc_data_uuid16, fields->svc_data_uuid16_len);
MODLOG_DFLT(DEBUG, "\n");
}
if (fields->public_tgt_addr != NULL) {
MODLOG_DFLT(DEBUG, " public_tgt_addr=");
u8p = fields->public_tgt_addr;
for (i = 0; i < fields->num_public_tgt_addrs; i++) {
MODLOG_DFLT(DEBUG, "public_tgt_addr=%s ", addr_str(u8p));
u8p += BLE_HS_ADV_PUBLIC_TGT_ADDR_ENTRY_LEN;
}
MODLOG_DFLT(DEBUG, "\n");
}
if (fields->appearance_is_present) {
MODLOG_DFLT(DEBUG, " appearance=0x%04x\n", fields->appearance);
}
if (fields->adv_itvl_is_present) {
MODLOG_DFLT(DEBUG, " adv_itvl=0x%04x\n", fields->adv_itvl);
}
if (fields->svc_data_uuid32 != NULL) {
MODLOG_DFLT(DEBUG, " svc_data_uuid32=");
print_bytes(fields->svc_data_uuid32, fields->svc_data_uuid32_len);
MODLOG_DFLT(DEBUG, "\n");
}
if (fields->svc_data_uuid128 != NULL) {
MODLOG_DFLT(DEBUG, " svc_data_uuid128=");
print_bytes(fields->svc_data_uuid128, fields->svc_data_uuid128_len);
MODLOG_DFLT(DEBUG, "\n");
}
if (fields->uri != NULL) {
MODLOG_DFLT(DEBUG, " uri=");
print_bytes(fields->uri, fields->uri_len);
MODLOG_DFLT(DEBUG, "\n");
}
if (fields->mfg_data != NULL) {
MODLOG_DFLT(DEBUG, " mfg_data=");
print_bytes(fields->mfg_data, fields->mfg_data_len);
MODLOG_DFLT(DEBUG, "\n");
}
}

View File

@@ -0,0 +1,807 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
#include <assert.h>
#include <string.h>
#include "host/ble_hs.h"
#include "blecent.h"
static void *peer_svc_mem;
static struct os_mempool peer_svc_pool;
static void *peer_chr_mem;
static struct os_mempool peer_chr_pool;
static void *peer_dsc_mem;
static struct os_mempool peer_dsc_pool;
static void *peer_mem;
static struct os_mempool peer_pool;
static SLIST_HEAD(, peer) peers;
static struct peer_svc *
peer_svc_find_range(struct peer *peer, uint16_t attr_handle);
static struct peer_svc *
peer_svc_find(struct peer *peer, uint16_t svc_start_handle,
struct peer_svc **out_prev);
int
peer_svc_is_empty(const struct peer_svc *svc);
uint16_t
chr_end_handle(const struct peer_svc *svc, const struct peer_chr *chr);
int
chr_is_empty(const struct peer_svc *svc, const struct peer_chr *chr);
static struct peer_chr *
peer_chr_find(const struct peer_svc *svc, uint16_t chr_def_handle,
struct peer_chr **out_prev);
static void
peer_disc_chrs(struct peer *peer);
static int
peer_dsc_disced(uint16_t conn_handle, const struct ble_gatt_error *error,
uint16_t chr_val_handle, const struct ble_gatt_dsc *dsc,
void *arg);
static struct peer *
peer_find(uint16_t conn_handle)
{
struct peer *peer;
SLIST_FOREACH(peer, &peers, next) {
if (peer->conn_handle == conn_handle) {
return peer;
}
}
return NULL;
}
static void
peer_disc_complete(struct peer *peer, int rc)
{
peer->disc_prev_chr_val = 0;
/* Notify caller that discovery has completed. */
if (peer->disc_cb != NULL) {
peer->disc_cb(peer, rc, peer->disc_cb_arg);
}
}
static struct peer_dsc *
peer_dsc_find_prev(const struct peer_chr *chr, uint16_t dsc_handle)
{
struct peer_dsc *prev;
struct peer_dsc *dsc;
prev = NULL;
SLIST_FOREACH(dsc, &chr->dscs, next) {
if (dsc->dsc.handle >= dsc_handle) {
break;
}
prev = dsc;
}
return prev;
}
static struct peer_dsc *
peer_dsc_find(const struct peer_chr *chr, uint16_t dsc_handle,
struct peer_dsc **out_prev)
{
struct peer_dsc *prev;
struct peer_dsc *dsc;
prev = peer_dsc_find_prev(chr, dsc_handle);
if (prev == NULL) {
dsc = SLIST_FIRST(&chr->dscs);
} else {
dsc = SLIST_NEXT(prev, next);
}
if (dsc != NULL && dsc->dsc.handle != dsc_handle) {
dsc = NULL;
}
if (out_prev != NULL) {
*out_prev = prev;
}
return dsc;
}
static int
peer_dsc_add(struct peer *peer, uint16_t chr_val_handle,
const struct ble_gatt_dsc *gatt_dsc)
{
struct peer_dsc *prev;
struct peer_dsc *dsc;
struct peer_svc *svc;
struct peer_chr *chr;
svc = peer_svc_find_range(peer, chr_val_handle);
if (svc == NULL) {
/* Can't find service for discovered descriptor; this shouldn't
* happen.
*/
assert(0);
return BLE_HS_EUNKNOWN;
}
chr = peer_chr_find(svc, chr_val_handle, NULL);
if (chr == NULL) {
/* Can't find characteristic for discovered descriptor; this shouldn't
* happen.
*/
assert(0);
return BLE_HS_EUNKNOWN;
}
dsc = peer_dsc_find(chr, gatt_dsc->handle, &prev);
if (dsc != NULL) {
/* Descriptor already discovered. */
return 0;
}
dsc = os_memblock_get(&peer_dsc_pool);
if (dsc == NULL) {
/* Out of memory. */
return BLE_HS_ENOMEM;
}
memset(dsc, 0, sizeof *dsc);
dsc->dsc = *gatt_dsc;
if (prev == NULL) {
SLIST_INSERT_HEAD(&chr->dscs, dsc, next);
} else {
SLIST_NEXT(prev, next) = dsc;
}
return 0;
}
static void
peer_disc_dscs(struct peer *peer)
{
struct peer_chr *chr;
struct peer_svc *svc;
int rc;
/* Search through the list of discovered characteristics for the first
* characteristic that contains undiscovered descriptors. Then, discover
* all descriptors belonging to that characteristic.
*/
SLIST_FOREACH(svc, &peer->svcs, next) {
SLIST_FOREACH(chr, &svc->chrs, next) {
if (!chr_is_empty(svc, chr) &&
SLIST_EMPTY(&chr->dscs) &&
peer->disc_prev_chr_val <= chr->chr.def_handle) {
rc = ble_gattc_disc_all_dscs(peer->conn_handle,
chr->chr.val_handle,
chr_end_handle(svc, chr),
peer_dsc_disced, peer);
if (rc != 0) {
peer_disc_complete(peer, rc);
}
peer->disc_prev_chr_val = chr->chr.val_handle;
return;
}
}
}
/* All descriptors discovered. */
peer_disc_complete(peer, 0);
}
static int
peer_dsc_disced(uint16_t conn_handle, const struct ble_gatt_error *error,
uint16_t chr_val_handle, const struct ble_gatt_dsc *dsc,
void *arg)
{
struct peer *peer;
int rc;
peer = arg;
assert(peer->conn_handle == conn_handle);
switch (error->status) {
case 0:
rc = peer_dsc_add(peer, chr_val_handle, dsc);
break;
case BLE_HS_EDONE:
/* All descriptors in this characteristic discovered; start discovering
* descriptors in the next characteristic.
*/
if (peer->disc_prev_chr_val > 0) {
peer_disc_dscs(peer);
}
rc = 0;
break;
default:
/* Error; abort discovery. */
rc = error->status;
break;
}
if (rc != 0) {
/* Error; abort discovery. */
peer_disc_complete(peer, rc);
}
return rc;
}
uint16_t
chr_end_handle(const struct peer_svc *svc, const struct peer_chr *chr)
{
const struct peer_chr *next_chr;
next_chr = SLIST_NEXT(chr, next);
if (next_chr != NULL) {
return next_chr->chr.def_handle - 1;
} else {
return svc->svc.end_handle;
}
}
int
chr_is_empty(const struct peer_svc *svc, const struct peer_chr *chr)
{
return chr_end_handle(svc, chr) <= chr->chr.val_handle;
}
static struct peer_chr *
peer_chr_find_prev(const struct peer_svc *svc, uint16_t chr_val_handle)
{
struct peer_chr *prev;
struct peer_chr *chr;
prev = NULL;
SLIST_FOREACH(chr, &svc->chrs, next) {
if (chr->chr.val_handle >= chr_val_handle) {
break;
}
prev = chr;
}
return prev;
}
static struct peer_chr *
peer_chr_find(const struct peer_svc *svc, uint16_t chr_val_handle,
struct peer_chr **out_prev)
{
struct peer_chr *prev;
struct peer_chr *chr;
prev = peer_chr_find_prev(svc, chr_val_handle);
if (prev == NULL) {
chr = SLIST_FIRST(&svc->chrs);
} else {
chr = SLIST_NEXT(prev, next);
}
if (chr != NULL && chr->chr.val_handle != chr_val_handle) {
chr = NULL;
}
if (out_prev != NULL) {
*out_prev = prev;
}
return chr;
}
static void
peer_chr_delete(struct peer_chr *chr)
{
struct peer_dsc *dsc;
while ((dsc = SLIST_FIRST(&chr->dscs)) != NULL) {
SLIST_REMOVE_HEAD(&chr->dscs, next);
os_memblock_put(&peer_dsc_pool, dsc);
}
os_memblock_put(&peer_chr_pool, chr);
}
static int
peer_chr_add(struct peer *peer, uint16_t svc_start_handle,
const struct ble_gatt_chr *gatt_chr)
{
struct peer_chr *prev;
struct peer_chr *chr;
struct peer_svc *svc;
svc = peer_svc_find(peer, svc_start_handle, NULL);
if (svc == NULL) {
/* Can't find service for discovered characteristic; this shouldn't
* happen.
*/
assert(0);
return BLE_HS_EUNKNOWN;
}
chr = peer_chr_find(svc, gatt_chr->def_handle, &prev);
if (chr != NULL) {
/* Characteristic already discovered. */
return 0;
}
chr = os_memblock_get(&peer_chr_pool);
if (chr == NULL) {
/* Out of memory. */
return BLE_HS_ENOMEM;
}
memset(chr, 0, sizeof *chr);
chr->chr = *gatt_chr;
if (prev == NULL) {
SLIST_INSERT_HEAD(&svc->chrs, chr, next);
} else {
SLIST_NEXT(prev, next) = chr;
}
return 0;
}
static int
peer_chr_disced(uint16_t conn_handle, const struct ble_gatt_error *error,
const struct ble_gatt_chr *chr, void *arg)
{
struct peer *peer;
int rc;
peer = arg;
assert(peer->conn_handle == conn_handle);
switch (error->status) {
case 0:
rc = peer_chr_add(peer, peer->cur_svc->svc.start_handle, chr);
break;
case BLE_HS_EDONE:
/* All characteristics in this service discovered; start discovering
* characteristics in the next service.
*/
if (peer->disc_prev_chr_val > 0) {
peer_disc_chrs(peer);
}
rc = 0;
break;
default:
rc = error->status;
break;
}
if (rc != 0) {
/* Error; abort discovery. */
peer_disc_complete(peer, rc);
}
return rc;
}
static void
peer_disc_chrs(struct peer *peer)
{
struct peer_svc *svc;
int rc;
/* Search through the list of discovered service for the first service that
* contains undiscovered characteristics. Then, discover all
* characteristics belonging to that service.
*/
SLIST_FOREACH(svc, &peer->svcs, next) {
if (!peer_svc_is_empty(svc) && SLIST_EMPTY(&svc->chrs)) {
peer->cur_svc = svc;
rc = ble_gattc_disc_all_chrs(peer->conn_handle,
svc->svc.start_handle,
svc->svc.end_handle,
peer_chr_disced, peer);
if (rc != 0) {
peer_disc_complete(peer, rc);
}
return;
}
}
/* All characteristics discovered. */
peer_disc_dscs(peer);
}
int
peer_svc_is_empty(const struct peer_svc *svc)
{
return svc->svc.end_handle <= svc->svc.start_handle;
}
static struct peer_svc *
peer_svc_find_prev(struct peer *peer, uint16_t svc_start_handle)
{
struct peer_svc *prev;
struct peer_svc *svc;
prev = NULL;
SLIST_FOREACH(svc, &peer->svcs, next) {
if (svc->svc.start_handle >= svc_start_handle) {
break;
}
prev = svc;
}
return prev;
}
static struct peer_svc *
peer_svc_find(struct peer *peer, uint16_t svc_start_handle,
struct peer_svc **out_prev)
{
struct peer_svc *prev;
struct peer_svc *svc;
prev = peer_svc_find_prev(peer, svc_start_handle);
if (prev == NULL) {
svc = SLIST_FIRST(&peer->svcs);
} else {
svc = SLIST_NEXT(prev, next);
}
if (svc != NULL && svc->svc.start_handle != svc_start_handle) {
svc = NULL;
}
if (out_prev != NULL) {
*out_prev = prev;
}
return svc;
}
static struct peer_svc *
peer_svc_find_range(struct peer *peer, uint16_t attr_handle)
{
struct peer_svc *svc;
SLIST_FOREACH(svc, &peer->svcs, next) {
if (svc->svc.start_handle <= attr_handle &&
svc->svc.end_handle >= attr_handle) {
return svc;
}
}
return NULL;
}
const struct peer_svc *
peer_svc_find_uuid(const struct peer *peer, const ble_uuid_t *uuid)
{
const struct peer_svc *svc;
SLIST_FOREACH(svc, &peer->svcs, next) {
if (ble_uuid_cmp(&svc->svc.uuid.u, uuid) == 0) {
return svc;
}
}
return NULL;
}
const struct peer_chr *
peer_chr_find_uuid(const struct peer *peer, const ble_uuid_t *svc_uuid,
const ble_uuid_t *chr_uuid)
{
const struct peer_svc *svc;
const struct peer_chr *chr;
svc = peer_svc_find_uuid(peer, svc_uuid);
if (svc == NULL) {
return NULL;
}
SLIST_FOREACH(chr, &svc->chrs, next) {
if (ble_uuid_cmp(&chr->chr.uuid.u, chr_uuid) == 0) {
return chr;
}
}
return NULL;
}
const struct peer_dsc *
peer_dsc_find_uuid(const struct peer *peer, const ble_uuid_t *svc_uuid,
const ble_uuid_t *chr_uuid, const ble_uuid_t *dsc_uuid)
{
const struct peer_chr *chr;
const struct peer_dsc *dsc;
chr = peer_chr_find_uuid(peer, svc_uuid, chr_uuid);
if (chr == NULL) {
return NULL;
}
SLIST_FOREACH(dsc, &chr->dscs, next) {
if (ble_uuid_cmp(&dsc->dsc.uuid.u, dsc_uuid) == 0) {
return dsc;
}
}
return NULL;
}
static int
peer_svc_add(struct peer *peer, const struct ble_gatt_svc *gatt_svc)
{
struct peer_svc *prev;
struct peer_svc *svc;
svc = peer_svc_find(peer, gatt_svc->start_handle, &prev);
if (svc != NULL) {
/* Service already discovered. */
return 0;
}
svc = os_memblock_get(&peer_svc_pool);
if (svc == NULL) {
/* Out of memory. */
return BLE_HS_ENOMEM;
}
memset(svc, 0, sizeof *svc);
svc->svc = *gatt_svc;
SLIST_INIT(&svc->chrs);
if (prev == NULL) {
SLIST_INSERT_HEAD(&peer->svcs, svc, next);
} else {
SLIST_INSERT_AFTER(prev, svc, next);
}
return 0;
}
static void
peer_svc_delete(struct peer_svc *svc)
{
struct peer_chr *chr;
while ((chr = SLIST_FIRST(&svc->chrs)) != NULL) {
SLIST_REMOVE_HEAD(&svc->chrs, next);
peer_chr_delete(chr);
}
os_memblock_put(&peer_svc_pool, svc);
}
static int
peer_svc_disced(uint16_t conn_handle, const struct ble_gatt_error *error,
const struct ble_gatt_svc *service, void *arg)
{
struct peer *peer;
int rc;
peer = arg;
assert(peer->conn_handle == conn_handle);
switch (error->status) {
case 0:
rc = peer_svc_add(peer, service);
break;
case BLE_HS_EDONE:
/* All services discovered; start discovering characteristics. */
if (peer->disc_prev_chr_val > 0) {
peer_disc_chrs(peer);
}
rc = 0;
break;
default:
rc = error->status;
break;
}
if (rc != 0) {
/* Error; abort discovery. */
peer_disc_complete(peer, rc);
}
return rc;
}
int
peer_disc_all(uint16_t conn_handle, peer_disc_fn *disc_cb, void *disc_cb_arg)
{
struct peer_svc *svc;
struct peer *peer;
int rc;
peer = peer_find(conn_handle);
if (peer == NULL) {
return BLE_HS_ENOTCONN;
}
/* Undiscover everything first. */
while ((svc = SLIST_FIRST(&peer->svcs)) != NULL) {
SLIST_REMOVE_HEAD(&peer->svcs, next);
peer_svc_delete(svc);
}
peer->disc_prev_chr_val = 1;
peer->disc_cb = disc_cb;
peer->disc_cb_arg = disc_cb_arg;
rc = ble_gattc_disc_all_svcs(conn_handle, peer_svc_disced, peer);
if (rc != 0) {
return rc;
}
return 0;
}
int
peer_delete(uint16_t conn_handle)
{
struct peer_svc *svc;
struct peer *peer;
int rc;
peer = peer_find(conn_handle);
if (peer == NULL) {
return BLE_HS_ENOTCONN;
}
SLIST_REMOVE(&peers, peer, peer, next);
while ((svc = SLIST_FIRST(&peer->svcs)) != NULL) {
SLIST_REMOVE_HEAD(&peer->svcs, next);
peer_svc_delete(svc);
}
rc = os_memblock_put(&peer_pool, peer);
if (rc != 0) {
return BLE_HS_EOS;
}
return 0;
}
int
peer_add(uint16_t conn_handle)
{
struct peer *peer;
/* Make sure the connection handle is unique. */
peer = peer_find(conn_handle);
if (peer != NULL) {
return BLE_HS_EALREADY;
}
peer = os_memblock_get(&peer_pool);
if (peer == NULL) {
/* Out of memory. */
return BLE_HS_ENOMEM;
}
memset(peer, 0, sizeof *peer);
peer->conn_handle = conn_handle;
SLIST_INSERT_HEAD(&peers, peer, next);
return 0;
}
static void
peer_free_mem(void)
{
free(peer_mem);
peer_mem = NULL;
free(peer_svc_mem);
peer_svc_mem = NULL;
free(peer_chr_mem);
peer_chr_mem = NULL;
free(peer_dsc_mem);
peer_dsc_mem = NULL;
}
int
peer_init(int max_peers, int max_svcs, int max_chrs, int max_dscs)
{
int rc;
/* Free memory first in case this function gets called more than once. */
peer_free_mem();
peer_mem = malloc(
OS_MEMPOOL_BYTES(max_peers, sizeof (struct peer)));
if (peer_mem == NULL) {
rc = BLE_HS_ENOMEM;
goto err;
}
rc = os_mempool_init(&peer_pool, max_peers,
sizeof (struct peer), peer_mem,
"peer_pool");
if (rc != 0) {
rc = BLE_HS_EOS;
goto err;
}
peer_svc_mem = malloc(
OS_MEMPOOL_BYTES(max_svcs, sizeof (struct peer_svc)));
if (peer_svc_mem == NULL) {
rc = BLE_HS_ENOMEM;
goto err;
}
rc = os_mempool_init(&peer_svc_pool, max_svcs,
sizeof (struct peer_svc), peer_svc_mem,
"peer_svc_pool");
if (rc != 0) {
rc = BLE_HS_EOS;
goto err;
}
peer_chr_mem = malloc(
OS_MEMPOOL_BYTES(max_chrs, sizeof (struct peer_chr)));
if (peer_chr_mem == NULL) {
rc = BLE_HS_ENOMEM;
goto err;
}
rc = os_mempool_init(&peer_chr_pool, max_chrs,
sizeof (struct peer_chr), peer_chr_mem,
"peer_chr_pool");
if (rc != 0) {
rc = BLE_HS_EOS;
goto err;
}
peer_dsc_mem = malloc(
OS_MEMPOOL_BYTES(max_dscs, sizeof (struct peer_dsc)));
if (peer_dsc_mem == NULL) {
rc = BLE_HS_ENOMEM;
goto err;
}
rc = os_mempool_init(&peer_dsc_pool, max_dscs,
sizeof (struct peer_dsc), peer_dsc_mem,
"peer_dsc_pool");
if (rc != 0) {
rc = BLE_HS_EOS;
goto err;
}
return 0;
err:
peer_free_mem();
return rc;
}

View File

@@ -0,0 +1,30 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you 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.
#
syscfg.vals:
# DEBUG logging is a bit noisy; use INFO.
LOG_LEVEL: 1
# Default task settings
OS_MAIN_STACK_SIZE: 336
# Disable peripheral and broadcaster roles.
BLE_ROLE_BROADCASTER: 0
BLE_ROLE_CENTRAL: 1
BLE_ROLE_OBSERVER: 1
BLE_ROLE_PERIPHERAL: 0

View File

@@ -0,0 +1,9 @@
# BLE Cycling Speed and Cadence peripheral app.
The source files are located in the src/ directory.
pkg.yml contains the base definition of the app.
syscfg.yml contains setting definitions and overrides.

View File

@@ -0,0 +1,40 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you 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.
#
pkg.name: apps/blecsc
pkg.type: app
pkg.description: BLE peripheral cycling speed and cadence sensor.
pkg.author: "Maciej Jurczak"
pkg.email: "mjurczak@gmail.com"
pkg.homepage: "http://mynewt.apache.org/"
pkg.keywords:
pkg.deps:
- "@apache-mynewt-core/kernel/os"
- "@apache-mynewt-core/sys/console/full"
- "@apache-mynewt-core/sys/log/full"
- "@apache-mynewt-core/sys/log/modlog"
- "@apache-mynewt-core/sys/stats/full"
- "@apache-mynewt-core/sys/sysinit"
- "@apache-mynewt-core/sys/id"
- nimble/controller
- nimble/host
- nimble/host/services/gap
- nimble/host/services/gatt
- nimble/host/store/config
- nimble/transport

View File

@@ -0,0 +1,105 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 H_BLECSC_SENSOR_
#define H_BLECSC_SENSOR_
#include "modlog/modlog.h"
#include "nimble/ble.h"
#ifdef __cplusplus
extern "C" {
#endif
/* Cycling Speed and Cadence configuration */
#define GATT_CSC_UUID 0x1816
#define GATT_CSC_MEASUREMENT_UUID 0x2A5B
#define GATT_CSC_FEATURE_UUID 0x2A5C
#define GATT_SENSOR_LOCATION_UUID 0x2A5D
#define GATT_SC_CONTROL_POINT_UUID 0x2A55
/* Device Information configuration */
#define GATT_DEVICE_INFO_UUID 0x180A
#define GATT_MANUFACTURER_NAME_UUID 0x2A29
#define GATT_MODEL_NUMBER_UUID 0x2A24
/*CSC Measurement flags*/
#define CSC_MEASUREMENT_WHEEL_REV_PRESENT 0x01
#define CSC_MEASUREMENT_CRANK_REV_PRESENT 0x02
/* CSC feature flags */
#define CSC_FEATURE_WHEEL_REV_DATA 0x01
#define CSC_FEATURE_CRANK_REV_DATA 0x02
#define CSC_FEATURE_MULTIPLE_SENSOR_LOC 0x04
/* Sensor location enum */
#define SENSOR_LOCATION_OTHER 0
#define SENSOR_LOCATION_TOP_OF_SHOE 1
#define SENSOR_LOCATION_IN_SHOE 2
#define SENSOR_LOCATION_HIP 3
#define SENSOR_LOCATION_FRONT_WHEEL 4
#define SENSOR_LOCATION_LEFT_CRANK 5
#define SENSOR_LOCATION_RIGHT_CRANK 6
#define SENSOR_LOCATION_LEFT_PEDAL 7
#define SENSOR_LOCATION_RIGHT_PEDAL 8
#define SENSOR_LOCATION_FROT_HUB 9
#define SENSOR_LOCATION_REAR_DROPOUT 10
#define SENSOR_LOCATION_CHAINSTAY 11
#define SENSOR_LOCATION_REAR_WHEEL 12
#define SENSOR_LOCATION_REAR_HUB 13
#define SENSOR_LOCATION_CHEST 14
#define SENSOR_LOCATION_SPIDER 15
#define SENSOR_LOCATION_CHAIN_RING 16
/* SC Control Point op codes */
#define SC_CP_OP_SET_CUMULATIVE_VALUE 1
#define SC_CP_OP_START_SENSOR_CALIBRATION 2
#define SC_CP_OP_UPDATE_SENSOR_LOCATION 3
#define SC_CP_OP_REQ_SUPPORTED_SENSOR_LOCATIONS 4
#define SC_CP_OP_RESPONSE 16
/*SC Control Point response values */
#define SC_CP_RESPONSE_SUCCESS 1
#define SC_CP_RESPONSE_OP_NOT_SUPPORTED 2
#define SC_CP_RESPONSE_INVALID_PARAM 3
#define SC_CP_RESPONSE_OP_FAILED 4
/* CSC simulation configuration */
#define CSC_FEATURES (CSC_FEATURE_WHEEL_REV_DATA | \
CSC_FEATURE_CRANK_REV_DATA |\
CSC_FEATURE_MULTIPLE_SENSOR_LOC)
struct ble_csc_measurement_state {
uint32_t cumulative_wheel_rev;
uint16_t last_wheel_evt_time;
uint16_t cumulative_crank_rev;
uint16_t last_crank_evt_time;
};
extern uint16_t csc_measurement_handle;
extern uint16_t csc_control_point_handle;
int gatt_svr_init(struct ble_csc_measurement_state * csc_measurement_state);
int gatt_svr_chr_notify_csc_measurement(uint16_t conn_handle);
void gatt_svr_set_cp_indicate(uint8_t indication_status);
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -0,0 +1,385 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
#include <assert.h>
#include <stdio.h>
#include <string.h>
#include "os/mynewt.h"
#include "host/ble_hs.h"
#include "host/ble_uuid.h"
#include "blecsc_sens.h"
#define CSC_ERR_CCC_DESC_IMPROPERLY_CONFIGURED 0x81
static const char *manuf_name = "Apache Mynewt";
static const char *model_num = "Mynewt CSC Sensor";
static const uint8_t csc_supported_sensor_locations[] = {
SENSOR_LOCATION_FRONT_WHEEL,
SENSOR_LOCATION_REAR_DROPOUT,
SENSOR_LOCATION_CHAINSTAY,
SENSOR_LOCATION_REAR_WHEEL
};
static uint8_t sensor_location = SENSOR_LOCATION_REAR_DROPOUT;
static struct ble_csc_measurement_state * measurement_state;
uint16_t csc_measurement_handle;
uint16_t csc_control_point_handle;
uint8_t csc_cp_indication_status;
static int
gatt_svr_chr_access_csc_measurement(uint16_t conn_handle,
uint16_t attr_handle,
struct ble_gatt_access_ctxt *ctxt,
void *arg);
static int
gatt_svr_chr_access_csc_feature(uint16_t conn_handle,
uint16_t attr_handle,
struct ble_gatt_access_ctxt *ctxt,
void *arg);
static int
gatt_svr_chr_access_sensor_location(uint16_t conn_handle,
uint16_t attr_handle,
struct ble_gatt_access_ctxt *ctxt,
void *arg);
static int
gatt_svr_chr_access_sc_control_point(uint16_t conn_handle,
uint16_t attr_handle,
struct ble_gatt_access_ctxt *ctxt,
void *arg);
static int
gatt_svr_chr_access_device_info(uint16_t conn_handle,
uint16_t attr_handle,
struct ble_gatt_access_ctxt *ctxt,
void *arg);
static const struct ble_gatt_svc_def gatt_svr_svcs[] = {
{
/* Service: Cycling Speed and Cadence */
.type = BLE_GATT_SVC_TYPE_PRIMARY,
.uuid = BLE_UUID16_DECLARE(GATT_CSC_UUID),
.characteristics = (struct ble_gatt_chr_def[]) { {
/* Characteristic: Cycling Speed and Cadence Measurement */
.uuid = BLE_UUID16_DECLARE(GATT_CSC_MEASUREMENT_UUID),
.access_cb = gatt_svr_chr_access_csc_measurement,
.val_handle = &csc_measurement_handle,
.flags = BLE_GATT_CHR_F_NOTIFY,
}, {
/* Characteristic: Cycling Speed and Cadence features */
.uuid = BLE_UUID16_DECLARE(GATT_CSC_FEATURE_UUID),
.access_cb = gatt_svr_chr_access_csc_feature,
.flags = BLE_GATT_CHR_F_READ,
}, {
/* Characteristic: Sensor Location */
.uuid = BLE_UUID16_DECLARE(GATT_SENSOR_LOCATION_UUID),
.access_cb = gatt_svr_chr_access_sensor_location,
.flags = BLE_GATT_CHR_F_READ,
}, {
/* Characteristic: SC Control Point*/
.uuid = BLE_UUID16_DECLARE(GATT_SC_CONTROL_POINT_UUID),
.access_cb = gatt_svr_chr_access_sc_control_point,
.val_handle = &csc_control_point_handle,
.flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_INDICATE,
}, {
0, /* No more characteristics in this service */
}, }
},
{
/* Service: Device Information */
.type = BLE_GATT_SVC_TYPE_PRIMARY,
.uuid = BLE_UUID16_DECLARE(GATT_DEVICE_INFO_UUID),
.characteristics = (struct ble_gatt_chr_def[]) { {
/* Characteristic: * Manufacturer name */
.uuid = BLE_UUID16_DECLARE(GATT_MANUFACTURER_NAME_UUID),
.access_cb = gatt_svr_chr_access_device_info,
.flags = BLE_GATT_CHR_F_READ,
}, {
/* Characteristic: Model number string */
.uuid = BLE_UUID16_DECLARE(GATT_MODEL_NUMBER_UUID),
.access_cb = gatt_svr_chr_access_device_info,
.flags = BLE_GATT_CHR_F_READ,
}, {
0, /* No more characteristics in this service */
}, }
},
{
0, /* No more services */
},
};
static int
gatt_svr_chr_access_csc_measurement(uint16_t conn_handle, uint16_t attr_handle,
struct ble_gatt_access_ctxt *ctxt, void *arg)
{
return BLE_ATT_ERR_READ_NOT_PERMITTED;
}
static int
gatt_svr_chr_access_csc_feature(uint16_t conn_handle, uint16_t attr_handle,
struct ble_gatt_access_ctxt *ctxt, void *arg)
{
static const uint16_t csc_feature = CSC_FEATURES;
int rc;
assert(ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR);
rc = os_mbuf_append(ctxt->om, &csc_feature, sizeof(csc_feature));
return (rc == 0) ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
}
static int
gatt_svr_chr_access_sensor_location(uint16_t conn_handle, uint16_t attr_handle,
struct ble_gatt_access_ctxt *ctxt, void *arg)
{
int rc;
assert(ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR);
rc = os_mbuf_append(ctxt->om, &sensor_location, sizeof(sensor_location));
return (rc == 0) ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
}
static int
gatt_svr_chr_access_sc_control_point(uint16_t conn_handle,
uint16_t attr_handle,
struct ble_gatt_access_ctxt *ctxt,
void *arg)
{
uint8_t op_code;
uint8_t new_sensor_location;
uint8_t new_cumulative_wheel_rev_arr[4];
struct os_mbuf *om_indication;
uint8_t response = SC_CP_RESPONSE_OP_NOT_SUPPORTED;
int ii;
int rc;
assert(ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR);
if (!csc_cp_indication_status) {
MODLOG_DFLT(INFO, "SC Control Point; CCC descriptor "
"improperly configured");
return CSC_ERR_CCC_DESC_IMPROPERLY_CONFIGURED;
}
/* Read control point op code*/
rc = os_mbuf_copydata(ctxt->om, 0, sizeof(op_code), &op_code);
if (rc != 0){
return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
}
MODLOG_DFLT(INFO, "SC Control Point; opcode=%d\n", op_code);
/* Allocate response buffer */
om_indication = ble_hs_mbuf_att_pkt();
switch(op_code){
#if (CSC_FEATURES & CSC_FEATURE_WHEEL_REV_DATA)
case SC_CP_OP_SET_CUMULATIVE_VALUE:
/* Read new cumulative wheel revolutions value*/
rc = os_mbuf_copydata(ctxt->om, 1,
sizeof(new_cumulative_wheel_rev_arr),
new_cumulative_wheel_rev_arr);
if (rc != 0){
return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
}
measurement_state->cumulative_wheel_rev =
get_le32(new_cumulative_wheel_rev_arr);
MODLOG_DFLT(INFO, "SC Control Point; Set cumulative value = %d\n",
measurement_state->cumulative_wheel_rev);
response = SC_CP_RESPONSE_SUCCESS;
break;
#endif
#if (CSC_FEATURES & CSC_FEATURE_MULTIPLE_SENSOR_LOC)
case SC_CP_OP_UPDATE_SENSOR_LOCATION:
/* Read new sensor location value*/
rc = os_mbuf_copydata(ctxt->om, 1, 1, &new_sensor_location);
if (rc != 0){
return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
}
MODLOG_DFLT(INFO, "SC Control Point; Sensor location update = %d\n",
new_sensor_location);
/* Verify if requested new location is on supported locations list */
response = SC_CP_RESPONSE_INVALID_PARAM;
for (ii = 0; ii < sizeof(csc_supported_sensor_locations); ii++){
if (new_sensor_location == csc_supported_sensor_locations[ii]){
sensor_location = new_sensor_location;
response = SC_CP_RESPONSE_SUCCESS;
break;
}
}
break;
case SC_CP_OP_REQ_SUPPORTED_SENSOR_LOCATIONS:
response = SC_CP_RESPONSE_SUCCESS;
break;
#endif
default:
break;
}
/* Append response value */
rc = os_mbuf_append(om_indication, &response, sizeof(response));
if (rc != 0){
return BLE_ATT_ERR_INSUFFICIENT_RES;
}
#if (CSC_FEATURES & CSC_FEATURE_MULTIPLE_SENSOR_LOC)
/* In case of supported locations request append locations list */
if (op_code == SC_CP_OP_REQ_SUPPORTED_SENSOR_LOCATIONS){
rc = os_mbuf_append(om_indication, &csc_supported_sensor_locations,
sizeof(csc_supported_sensor_locations));
}
if (rc != 0){
return BLE_ATT_ERR_INSUFFICIENT_RES;
}
#endif
rc = ble_gattc_indicate_custom(conn_handle, csc_control_point_handle,
om_indication);
return rc;
}
static int
gatt_svr_chr_access_device_info(uint16_t conn_handle, uint16_t attr_handle,
struct ble_gatt_access_ctxt *ctxt, void *arg)
{
uint16_t uuid;
int rc;
uuid = ble_uuid_u16(ctxt->chr->uuid);
if (uuid == GATT_MODEL_NUMBER_UUID) {
rc = os_mbuf_append(ctxt->om, model_num, strlen(model_num));
return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
}
if (uuid == GATT_MANUFACTURER_NAME_UUID) {
rc = os_mbuf_append(ctxt->om, manuf_name, strlen(manuf_name));
return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
}
assert(0);
return BLE_ATT_ERR_UNLIKELY;
}
int
gatt_svr_chr_notify_csc_measurement(uint16_t conn_handle)
{
int rc;
struct os_mbuf *om;
uint8_t data_buf[11];
uint8_t data_offset = 1;
memset(data_buf, 0, sizeof(data_buf));
#if (CSC_FEATURES & CSC_FEATURE_WHEEL_REV_DATA)
data_buf[0] |= CSC_MEASUREMENT_WHEEL_REV_PRESENT;
put_le16(&(data_buf[5]), measurement_state->last_wheel_evt_time);
put_le32(&(data_buf[1]), measurement_state->cumulative_wheel_rev);
data_offset += 6;
#endif
#if (CSC_FEATURES & CSC_FEATURE_CRANK_REV_DATA)
data_buf[0] |= CSC_MEASUREMENT_CRANK_REV_PRESENT;
put_le16(&(data_buf[data_offset]),
measurement_state->cumulative_crank_rev);
put_le16(&(data_buf[data_offset + 2]),
measurement_state->last_crank_evt_time);
data_offset += 4;
#endif
om = ble_hs_mbuf_from_flat(data_buf, data_offset);
rc = ble_gattc_notify_custom(conn_handle, csc_measurement_handle, om);
return rc;
}
void
gatt_svr_set_cp_indicate(uint8_t indication_status)
{
csc_cp_indication_status = indication_status;
}
void
gatt_svr_register_cb(struct ble_gatt_register_ctxt *ctxt, void *arg)
{
char buf[BLE_UUID_STR_LEN];
switch (ctxt->op) {
case BLE_GATT_REGISTER_OP_SVC:
MODLOG_DFLT(DEBUG, "registered service %s with handle=%d\n",
ble_uuid_to_str(ctxt->svc.svc_def->uuid, buf),
ctxt->svc.handle);
break;
case BLE_GATT_REGISTER_OP_CHR:
MODLOG_DFLT(DEBUG, "registering characteristic %s with "
"def_handle=%d val_handle=%d\n",
ble_uuid_to_str(ctxt->chr.chr_def->uuid, buf),
ctxt->chr.def_handle,
ctxt->chr.val_handle);
break;
case BLE_GATT_REGISTER_OP_DSC:
MODLOG_DFLT(DEBUG, "registering descriptor %s with handle=%d\n",
ble_uuid_to_str(ctxt->dsc.dsc_def->uuid, buf),
ctxt->dsc.handle);
break;
default:
assert(0);
break;
}
}
int
gatt_svr_init(struct ble_csc_measurement_state * csc_measurement_state)
{
int rc;
rc = ble_gatts_count_cfg(gatt_svr_svcs);
if (rc != 0) {
return rc;
}
rc = ble_gatts_add_svcs(gatt_svr_svcs);
if (rc != 0) {
return rc;
}
measurement_state = csc_measurement_state;
return 0;
}

View File

@@ -0,0 +1,310 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
#include <assert.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
#include "os/mynewt.h"
#include "console/console.h"
#include "config/config.h"
#include "nimble/ble.h"
#include "host/ble_hs.h"
#include "services/gap/ble_svc_gap.h"
#include "blecsc_sens.h"
/* Wheel size for simulation calculations */
#define CSC_SIM_WHEEL_CIRCUMFERENCE_MM 2000
/* Simulated cadence lower limit */
#define CSC_SIM_CRANK_RPM_MIN 20
/* Simulated cadence upper limit */
#define CSC_SIM_CRANK_RPM_MAX 100
/* Simulated speed lower limit */
#define CSC_SIM_SPEED_KPH_MIN 0
/* Simulated speed upper limit */
#define CSC_SIM_SPEED_KPH_MAX 35
/* Noticication status */
static bool notify_state = false;
/* Connection handle */
static uint16_t conn_handle;
static uint8_t blecsc_addr_type;
/* Advertised device name */
static const char *device_name = "blecsc_sensor";
/* Measurement and notification timer */
static struct os_callout blecsc_measure_timer;
/* Variable holds current CSC measurement state */
static struct ble_csc_measurement_state csc_measurement_state;
/* Variable holds simulted speed (kilometers per hour) */
static uint16_t csc_sim_speed_kph = CSC_SIM_SPEED_KPH_MIN;
/* Variable holds simulated cadence (RPM) */
static uint8_t csc_sim_crank_rpm = CSC_SIM_CRANK_RPM_MIN;
static int blecsc_gap_event(struct ble_gap_event *event, void *arg);
/*
* Enables advertising with parameters:
* o General discoverable mode
* o Undirected connectable mode
*/
static void
blecsc_advertise(void)
{
struct ble_gap_adv_params adv_params;
struct ble_hs_adv_fields fields;
int rc;
/*
* Set the advertisement data included in our advertisements:
* o Flags (indicates advertisement type and other general info)
* o Advertising tx power
* o Device name
*/
memset(&fields, 0, sizeof(fields));
/*
* Advertise two flags:
* o Discoverability in forthcoming advertisement (general)
* o BLE-only (BR/EDR unsupported)
*/
fields.flags = BLE_HS_ADV_F_DISC_GEN |
BLE_HS_ADV_F_BREDR_UNSUP;
/*
* Indicate that the TX power level field should be included; have the
* stack fill this value automatically. This is done by assigning the
* special value BLE_HS_ADV_TX_PWR_LVL_AUTO.
*/
fields.tx_pwr_lvl_is_present = 1;
fields.tx_pwr_lvl = BLE_HS_ADV_TX_PWR_LVL_AUTO;
fields.name = (uint8_t *)device_name;
fields.name_len = strlen(device_name);
fields.name_is_complete = 1;
/*
* Set appearance.
*/
fields.appearance = ble_svc_gap_device_appearance();
fields.appearance_is_present = 1;
rc = ble_gap_adv_set_fields(&fields);
if (rc != 0) {
MODLOG_DFLT(ERROR, "error setting advertisement data; rc=%d\n", rc);
return;
}
/* Begin advertising */
memset(&adv_params, 0, sizeof(adv_params));
adv_params.conn_mode = BLE_GAP_CONN_MODE_UND;
adv_params.disc_mode = BLE_GAP_DISC_MODE_GEN;
rc = ble_gap_adv_start(blecsc_addr_type, NULL, BLE_HS_FOREVER,
&adv_params, blecsc_gap_event, NULL);
if (rc != 0) {
MODLOG_DFLT(ERROR, "error enabling advertisement; rc=%d\n", rc);
return;
}
}
/* Update simulated CSC measurements.
* Each call increments wheel and crank revolution counters by one and
* computes last event time in order to match simulated candence and speed.
* Last event time is expressedd in 1/1024th of second units.
*
* 60 * 1024
* crank_dt = --------------
* cadence[RPM]
*
*
* circumference[mm] * 1024 * 60 * 60
* wheel_dt = -------------------------------------
* 10^6 * speed [kph]
*/
static void
blecsc_simulate_speed_and_cadence()
{
uint16_t wheel_rev_period;
uint16_t crank_rev_period;
/* Update simulated crank and wheel rotation speed */
csc_sim_speed_kph++;
if (csc_sim_speed_kph >= CSC_SIM_SPEED_KPH_MAX) {
csc_sim_speed_kph = CSC_SIM_SPEED_KPH_MIN;
}
csc_sim_crank_rpm++;
if (csc_sim_crank_rpm >= CSC_SIM_CRANK_RPM_MAX) {
csc_sim_crank_rpm = CSC_SIM_CRANK_RPM_MIN;
}
/* Calculate simulated measurement values */
if (csc_sim_speed_kph > 0){
wheel_rev_period = (36*64*CSC_SIM_WHEEL_CIRCUMFERENCE_MM) /
(625*csc_sim_speed_kph);
csc_measurement_state.cumulative_wheel_rev++;
csc_measurement_state.last_wheel_evt_time += wheel_rev_period;
}
if (csc_sim_crank_rpm > 0){
crank_rev_period = (60*1024) / csc_sim_crank_rpm;
csc_measurement_state.cumulative_crank_rev++;
csc_measurement_state.last_crank_evt_time += crank_rev_period;
}
MODLOG_DFLT(INFO, "CSC simulated values: speed = %d kph, cadence = %d \n",
csc_sim_speed_kph, csc_sim_crank_rpm);
}
/* Run CSC measurement simulation and notify it to the client */
static void
blecsc_measurement(struct os_event *ev)
{
int rc;
rc = os_callout_reset(&blecsc_measure_timer, OS_TICKS_PER_SEC);
assert(rc == 0);
blecsc_simulate_speed_and_cadence();
if (notify_state) {
rc = gatt_svr_chr_notify_csc_measurement(conn_handle);
assert(rc == 0);
}
}
static int
blecsc_gap_event(struct ble_gap_event *event, void *arg)
{
switch (event->type) {
case BLE_GAP_EVENT_CONNECT:
/* A new connection was established or a connection attempt failed */
MODLOG_DFLT(INFO, "connection %s; status=%d\n",
event->connect.status == 0 ? "established" : "failed",
event->connect.status);
if (event->connect.status != 0) {
/* Connection failed; resume advertising */
blecsc_advertise();
conn_handle = 0;
}
else {
conn_handle = event->connect.conn_handle;
}
break;
case BLE_GAP_EVENT_DISCONNECT:
MODLOG_DFLT(INFO, "disconnect; reason=%d\n", event->disconnect.reason);
conn_handle = 0;
/* Connection terminated; resume advertising */
blecsc_advertise();
break;
case BLE_GAP_EVENT_ADV_COMPLETE:
MODLOG_DFLT(INFO, "adv complete\n");
break;
case BLE_GAP_EVENT_SUBSCRIBE:
MODLOG_DFLT(INFO, "subscribe event attr_handle=%d\n",
event->subscribe.attr_handle);
if (event->subscribe.attr_handle == csc_measurement_handle) {
notify_state = event->subscribe.cur_notify;
MODLOG_DFLT(INFO, "csc measurement notify state = %d\n",
notify_state);
}
else if (event->subscribe.attr_handle == csc_control_point_handle) {
gatt_svr_set_cp_indicate(event->subscribe.cur_indicate);
MODLOG_DFLT(INFO, "csc control point indicate state = %d\n",
event->subscribe.cur_indicate);
}
break;
case BLE_GAP_EVENT_MTU:
MODLOG_DFLT(INFO, "mtu update event; conn_handle=%d mtu=%d\n",
event->mtu.conn_handle,
event->mtu.value);
break;
}
return 0;
}
static void
blecsc_on_sync(void)
{
int rc;
/* Figure out address to use while advertising (no privacy) */
rc = ble_hs_id_infer_auto(0, &blecsc_addr_type);
assert(rc == 0);
/* Begin advertising */
blecsc_advertise();
}
/*
* main
*
* The main task for the project. This function initializes the packages,
* then starts serving events from default event queue.
*
* @return int NOTE: this function should never return!
*/
int
main(void)
{
int rc;
/* Initialize OS */
sysinit();
/* Initialize the NimBLE host configuration */
ble_hs_cfg.sync_cb = blecsc_on_sync;
/* Initialize measurement and notification timer */
os_callout_init(&blecsc_measure_timer, os_eventq_dflt_get(),
blecsc_measurement, NULL);
rc = os_callout_reset(&blecsc_measure_timer, OS_TICKS_PER_SEC);
assert(rc == 0);
rc = gatt_svr_init(&csc_measurement_state);
assert(rc == 0);
/* Set the default device name */
rc = ble_svc_gap_device_name_set(device_name);
assert(rc == 0);
/* As the last thing, process events from default event queue */
while (1) {
os_eventq_run(os_eventq_dflt_get());
}
return 0;
}

View File

@@ -0,0 +1,38 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you 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.
syscfg.vals:
# Disable central and observer roles.
BLE_ROLE_BROADCASTER: 1
BLE_ROLE_CENTRAL: 0
BLE_ROLE_OBSERVER: 0
BLE_ROLE_PERIPHERAL: 1
# Disable unused eddystone feature.
BLE_EDDYSTONE: 0
# Log reboot messages to a flash circular buffer.
REBOOT_LOG_FCB: 1
LOG_FCB: 1
CONFIG_FCB: 1
# Set public device address.
BLE_PUBLIC_DEV_ADDR: ((uint8_t[6]){0xcc, 0xbb, 0xaa, 0x33, 0x22, 0x11})
# Set device appearance to Cycling Speed and Cadence Sensor
BLE_SVC_GAP_APPEARANCE: BLE_SVC_GAP_APPEARANCE_CYC_SPEED_AND_CADENCE_SENSOR

View File

@@ -0,0 +1,34 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you 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.
#
pkg.name: apps/blehci
pkg.type: app
pkg.description: BLE controller application exposing HCI over external interface
pkg.author: "Johan Hedberg <johan.hedberg@intel.com>"
pkg.homepage: "http://mynewt.apache.org/"
pkg.keywords:
pkg.deps:
- "@apache-mynewt-core/sys/console/stub"
- "@apache-mynewt-core/sys/log/stub"
- "@apache-mynewt-core/sys/stats/full"
- "@apache-mynewt-core/kernel/os"
- nimble/controller
- nimble/transport
pkg.req_apis:
- ble_transport

View File

@@ -0,0 +1,33 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
#include <assert.h>
#include "os/mynewt.h"
int
main(void)
{
/* Initialize OS */
sysinit();
while (1) {
os_eventq_run(os_eventq_dflt_get());
}
return 0;
}

View File

@@ -0,0 +1,23 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you 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.
#
syscfg.vals:
# Default task settings
OS_MAIN_STACK_SIZE: 64
# Use UART transport by default
BLE_HCI_TRANSPORT: uart

View File

@@ -0,0 +1,9 @@
# BLE Heart Rate peripheral app.
The source files are located in the src/ directory.
pkg.yml contains the base definition of the app.
syscfg.yml contains setting definitions and overrides.

View File

@@ -0,0 +1,40 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you 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.
#
pkg.name: apps/blehr
pkg.type: app
pkg.description: BLE peripheral heartrate sensor.
pkg.author: "Szymon Czapracki"
pkg.email: "szymon.czapracki@codecoup.pl"
pkg.homepage: "http://mynewt.apache.org/"
pkg.keywords:
pkg.deps:
- "@apache-mynewt-core/kernel/os"
- "@apache-mynewt-core/sys/console/full"
- "@apache-mynewt-core/sys/log/full"
- "@apache-mynewt-core/sys/log/modlog"
- "@apache-mynewt-core/sys/stats/full"
- "@apache-mynewt-core/sys/sysinit"
- "@apache-mynewt-core/sys/id"
- nimble/controller
- nimble/host
- nimble/host/services/gap
- nimble/host/services/gatt
- nimble/host/store/config
- nimble/transport/ram

View File

@@ -0,0 +1,46 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 H_BLEHR_SENSOR_
#define H_BLEHR_SENSOR_
#include "nimble/ble.h"
#include "modlog/modlog.h"
#ifdef __cplusplus
extern "C" {
#endif
/* Heart-rate configuration */
#define GATT_HRS_UUID 0x180D
#define GATT_HRS_MEASUREMENT_UUID 0x2A37
#define GATT_HRS_BODY_SENSOR_LOC_UUID 0x2A38
#define GATT_DEVICE_INFO_UUID 0x180A
#define GATT_MANUFACTURER_NAME_UUID 0x2A29
#define GATT_MODEL_NUMBER_UUID 0x2A24
extern uint16_t hrs_hrm_handle;
int gatt_svr_init(void);
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -0,0 +1,177 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
#include <assert.h>
#include <stdio.h>
#include <string.h>
#include "host/ble_hs.h"
#include "host/ble_uuid.h"
#include "blehr_sens.h"
static const char *manuf_name = "Apache Mynewt";
static const char *model_num = "Mynewt HR Sensor";
uint16_t hrs_hrm_handle;
static int
gatt_svr_chr_access_heart_rate(uint16_t conn_handle, uint16_t attr_handle,
struct ble_gatt_access_ctxt *ctxt, void *arg);
static int
gatt_svr_chr_access_device_info(uint16_t conn_handle, uint16_t attr_handle,
struct ble_gatt_access_ctxt *ctxt, void *arg);
static const struct ble_gatt_svc_def gatt_svr_svcs[] = {
{
/* Service: Heart-rate */
.type = BLE_GATT_SVC_TYPE_PRIMARY,
.uuid = BLE_UUID16_DECLARE(GATT_HRS_UUID),
.characteristics = (struct ble_gatt_chr_def[]) { {
/* Characteristic: Heart-rate measurement */
.uuid = BLE_UUID16_DECLARE(GATT_HRS_MEASUREMENT_UUID),
.access_cb = gatt_svr_chr_access_heart_rate,
.val_handle = &hrs_hrm_handle,
.flags = BLE_GATT_CHR_F_NOTIFY,
}, {
/* Characteristic: Body sensor location */
.uuid = BLE_UUID16_DECLARE(GATT_HRS_BODY_SENSOR_LOC_UUID),
.access_cb = gatt_svr_chr_access_heart_rate,
.flags = BLE_GATT_CHR_F_READ,
}, {
0, /* No more characteristics in this service */
}, }
},
{
/* Service: Device Information */
.type = BLE_GATT_SVC_TYPE_PRIMARY,
.uuid = BLE_UUID16_DECLARE(GATT_DEVICE_INFO_UUID),
.characteristics = (struct ble_gatt_chr_def[]) { {
/* Characteristic: * Manufacturer name */
.uuid = BLE_UUID16_DECLARE(GATT_MANUFACTURER_NAME_UUID),
.access_cb = gatt_svr_chr_access_device_info,
.flags = BLE_GATT_CHR_F_READ,
}, {
/* Characteristic: Model number string */
.uuid = BLE_UUID16_DECLARE(GATT_MODEL_NUMBER_UUID),
.access_cb = gatt_svr_chr_access_device_info,
.flags = BLE_GATT_CHR_F_READ,
}, {
0, /* No more characteristics in this service */
}, }
},
{
0, /* No more services */
},
};
static int
gatt_svr_chr_access_heart_rate(uint16_t conn_handle, uint16_t attr_handle,
struct ble_gatt_access_ctxt *ctxt, void *arg)
{
/* Sensor location, set to "Chest" */
static uint8_t body_sens_loc = 0x01;
uint16_t uuid;
int rc;
uuid = ble_uuid_u16(ctxt->chr->uuid);
if (uuid == GATT_HRS_BODY_SENSOR_LOC_UUID) {
rc = os_mbuf_append(ctxt->om, &body_sens_loc, sizeof(body_sens_loc));
return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
}
assert(0);
return BLE_ATT_ERR_UNLIKELY;
}
static int
gatt_svr_chr_access_device_info(uint16_t conn_handle, uint16_t attr_handle,
struct ble_gatt_access_ctxt *ctxt, void *arg)
{
uint16_t uuid;
int rc;
uuid = ble_uuid_u16(ctxt->chr->uuid);
if (uuid == GATT_MODEL_NUMBER_UUID) {
rc = os_mbuf_append(ctxt->om, model_num, strlen(model_num));
return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
}
if (uuid == GATT_MANUFACTURER_NAME_UUID) {
rc = os_mbuf_append(ctxt->om, manuf_name, strlen(manuf_name));
return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
}
assert(0);
return BLE_ATT_ERR_UNLIKELY;
}
void
gatt_svr_register_cb(struct ble_gatt_register_ctxt *ctxt, void *arg)
{
char buf[BLE_UUID_STR_LEN];
switch (ctxt->op) {
case BLE_GATT_REGISTER_OP_SVC:
MODLOG_DFLT(DEBUG, "registered service %s with handle=%d\n",
ble_uuid_to_str(ctxt->svc.svc_def->uuid, buf),
ctxt->svc.handle);
break;
case BLE_GATT_REGISTER_OP_CHR:
MODLOG_DFLT(DEBUG, "registering characteristic %s with "
"def_handle=%d val_handle=%d\n",
ble_uuid_to_str(ctxt->chr.chr_def->uuid, buf),
ctxt->chr.def_handle,
ctxt->chr.val_handle);
break;
case BLE_GATT_REGISTER_OP_DSC:
MODLOG_DFLT(DEBUG, "registering descriptor %s with handle=%d\n",
ble_uuid_to_str(ctxt->dsc.dsc_def->uuid, buf),
ctxt->dsc.handle);
break;
default:
assert(0);
break;
}
}
int
gatt_svr_init(void)
{
int rc;
rc = ble_gatts_count_cfg(gatt_svr_svcs);
if (rc != 0) {
return rc;
}
rc = ble_gatts_add_svcs(gatt_svr_svcs);
if (rc != 0) {
return rc;
}
return 0;
}

View File

@@ -0,0 +1,261 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
#include <assert.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
#include "os/mynewt.h"
#include "console/console.h"
#include "config/config.h"
#include "nimble/ble.h"
#include "host/ble_hs.h"
#include "services/gap/ble_svc_gap.h"
#include "blehr_sens.h"
static bool notify_state;
/* Connection handle */
static uint16_t conn_handle;
static const char *device_name = "blehr_sensor";
static int blehr_gap_event(struct ble_gap_event *event, void *arg);
static uint8_t blehr_addr_type;
/* Sending notify data timer */
static struct os_callout blehr_tx_timer;
/* Variable to simulate heart beats */
static uint8_t heartrate = 90;
/*
* Enables advertising with parameters:
* o General discoverable mode
* o Undirected connectable mode
*/
static void
blehr_advertise(void)
{
struct ble_gap_adv_params adv_params;
struct ble_hs_adv_fields fields;
int rc;
/*
* Set the advertisement data included in our advertisements:
* o Flags (indicates advertisement type and other general info)
* o Advertising tx power
* o Device name
*/
memset(&fields, 0, sizeof(fields));
/*
* Advertise two flags:
* o Discoverability in forthcoming advertisement (general)
* o BLE-only (BR/EDR unsupported)
*/
fields.flags = BLE_HS_ADV_F_DISC_GEN |
BLE_HS_ADV_F_BREDR_UNSUP;
/*
* Indicate that the TX power level field should be included; have the
* stack fill this value automatically. This is done by assigning the
* special value BLE_HS_ADV_TX_PWR_LVL_AUTO.
*/
fields.tx_pwr_lvl_is_present = 1;
fields.tx_pwr_lvl = BLE_HS_ADV_TX_PWR_LVL_AUTO;
fields.name = (uint8_t *)device_name;
fields.name_len = strlen(device_name);
fields.name_is_complete = 1;
rc = ble_gap_adv_set_fields(&fields);
if (rc != 0) {
MODLOG_DFLT(ERROR, "error setting advertisement data; rc=%d\n", rc);
return;
}
/* Begin advertising */
memset(&adv_params, 0, sizeof(adv_params));
adv_params.conn_mode = BLE_GAP_CONN_MODE_UND;
adv_params.disc_mode = BLE_GAP_DISC_MODE_GEN;
rc = ble_gap_adv_start(blehr_addr_type, NULL, BLE_HS_FOREVER,
&adv_params, blehr_gap_event, NULL);
if (rc != 0) {
MODLOG_DFLT(ERROR, "error enabling advertisement; rc=%d\n", rc);
return;
}
}
static void
blehr_tx_hrate_stop(void)
{
os_callout_stop(&blehr_tx_timer);
}
/* Reset heartrate measurment */
static void
blehr_tx_hrate_reset(void)
{
int rc;
rc = os_callout_reset(&blehr_tx_timer, OS_TICKS_PER_SEC);
assert(rc == 0);
}
/* This functions simulates heart beat and notifies it to the client */
static void
blehr_tx_hrate(struct os_event *ev)
{
static uint8_t hrm[2];
int rc;
struct os_mbuf *om;
if (!notify_state) {
blehr_tx_hrate_stop();
heartrate = 90;
return;
}
hrm[0] = 0x06; /* contact of a sensor */
hrm[1] = heartrate; /* storing dummy data */
/* Simulation of heart beats */
heartrate++;
if (heartrate == 160) {
heartrate = 90;
}
om = ble_hs_mbuf_from_flat(hrm, sizeof(hrm));
rc = ble_gattc_notify_custom(conn_handle, hrs_hrm_handle, om);
assert(rc == 0);
blehr_tx_hrate_reset();
}
static int
blehr_gap_event(struct ble_gap_event *event, void *arg)
{
switch (event->type) {
case BLE_GAP_EVENT_CONNECT:
/* A new connection was established or a connection attempt failed */
MODLOG_DFLT(INFO, "connection %s; status=%d\n",
event->connect.status == 0 ? "established" : "failed",
event->connect.status);
if (event->connect.status != 0) {
/* Connection failed; resume advertising */
blehr_advertise();
conn_handle = 0;
}
else {
conn_handle = event->connect.conn_handle;
}
break;
case BLE_GAP_EVENT_DISCONNECT:
MODLOG_DFLT(INFO, "disconnect; reason=%d\n", event->disconnect.reason);
conn_handle = BLE_HS_CONN_HANDLE_NONE; /* reset conn_handle */
/* Connection terminated; resume advertising */
blehr_advertise();
break;
case BLE_GAP_EVENT_ADV_COMPLETE:
MODLOG_DFLT(INFO, "adv complete\n");
blehr_advertise();
break;
case BLE_GAP_EVENT_SUBSCRIBE:
MODLOG_DFLT(INFO, "subscribe event; cur_notify=%d\n value handle; "
"val_handle=%d\n",
event->subscribe.cur_notify, hrs_hrm_handle);
if (event->subscribe.attr_handle == hrs_hrm_handle) {
notify_state = event->subscribe.cur_notify;
blehr_tx_hrate_reset();
} else if (event->subscribe.attr_handle != hrs_hrm_handle) {
notify_state = event->subscribe.cur_notify;
blehr_tx_hrate_stop();
}
break;
case BLE_GAP_EVENT_MTU:
MODLOG_DFLT(INFO, "mtu update event; conn_handle=%d mtu=%d\n",
event->mtu.conn_handle,
event->mtu.value);
break;
}
return 0;
}
static void
blehr_on_sync(void)
{
int rc;
/* Use privacy */
rc = ble_hs_id_infer_auto(0, &blehr_addr_type);
assert(rc == 0);
/* Begin advertising */
blehr_advertise();
}
/*
* main
*
* The main task for the project. This function initializes the packages,
* then starts serving events from default event queue.
*
* @return int NOTE: this function should never return!
*/
int
main(void)
{
int rc;
/* Initialize OS */
sysinit();
/* Initialize the NimBLE host configuration */
ble_hs_cfg.sync_cb = blehr_on_sync;
os_callout_init(&blehr_tx_timer, os_eventq_dflt_get(),
blehr_tx_hrate, NULL);
rc = gatt_svr_init();
assert(rc == 0);
/* Set the default device name */
rc = ble_svc_gap_device_name_set(device_name);
assert(rc == 0);
/* As the last thing, process events from default event queue */
while (1) {
os_eventq_run(os_eventq_dflt_get());
}
return 0;
}

View File

@@ -0,0 +1,35 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you 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.
syscfg.vals:
# Disable central and observer roles.
BLE_ROLE_BROADCASTER: 1
BLE_ROLE_CENTRAL: 0
BLE_ROLE_OBSERVER: 0
BLE_ROLE_PERIPHERAL: 1
# Disable unused eddystone feature.
BLE_EDDYSTONE: 0
# Log reboot messages to a flash circular buffer.
REBOOT_LOG_FCB: 1
LOG_FCB: 1
CONFIG_FCB: 1
# Set public device address.
BLE_PUBLIC_DEV_ADDR: ((uint8_t[6]){0xcc, 0xbb, 0xaa, 0x33, 0x22, 0x11})

View File

@@ -0,0 +1,37 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you 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.
#
pkg.name: apps/blemesh
pkg.type: app
pkg.description: Sample application for BLE Mesh node with on/off model
pkg.author: "Łukasz Rymanowski <lukasz.rymanowski@codecoup.pl>"
pkg.homepage: "http://mynewt.apache.org/"
pkg.keywords:
pkg.deps:
- "@apache-mynewt-core/kernel/os"
- "@apache-mynewt-core/sys/console/full"
- "@apache-mynewt-core/sys/log/full"
- "@apache-mynewt-core/sys/log/modlog"
- "@apache-mynewt-core/sys/stats/full"
- "@apache-mynewt-core/sys/shell"
- nimble/controller
- nimble/host
- nimble/host/services/gap
- nimble/host/services/gatt
- nimble/host/store/ram
- nimble/transport/ram

View File

@@ -0,0 +1,470 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
#include <assert.h>
#include "os/mynewt.h"
#include "mesh/mesh.h"
#include "console/console.h"
#include "hal/hal_system.h"
#include "hal/hal_gpio.h"
#include "bsp/bsp.h"
#include "shell/shell.h"
/* BLE */
#include "nimble/ble.h"
#include "host/ble_hs.h"
#include "services/gap/ble_svc_gap.h"
#include "mesh/glue.h"
#define BT_DBG_ENABLED (MYNEWT_VAL(BLE_MESH_DEBUG))
/* Company ID */
#define CID_VENDOR 0x05C3
#define STANDARD_TEST_ID 0x00
#define TEST_ID 0x01
static int recent_test_id = STANDARD_TEST_ID;
#define FAULT_ARR_SIZE 2
static bool has_reg_fault = true;
static struct bt_mesh_cfg_srv cfg_srv = {
.relay = BT_MESH_RELAY_DISABLED,
.beacon = BT_MESH_BEACON_ENABLED,
#if MYNEWT_VAL(BLE_MESH_FRIEND)
.frnd = BT_MESH_FRIEND_ENABLED,
#else
.gatt_proxy = BT_MESH_GATT_PROXY_NOT_SUPPORTED,
#endif
#if MYNEWT_VAL(BLE_MESH_GATT_PROXY)
.gatt_proxy = BT_MESH_GATT_PROXY_ENABLED,
#else
.gatt_proxy = BT_MESH_GATT_PROXY_NOT_SUPPORTED,
#endif
.default_ttl = 7,
/* 3 transmissions with 20ms interval */
.net_transmit = BT_MESH_TRANSMIT(2, 20),
.relay_retransmit = BT_MESH_TRANSMIT(2, 20),
};
static int
fault_get_cur(struct bt_mesh_model *model,
uint8_t *test_id,
uint16_t *company_id,
uint8_t *faults,
uint8_t *fault_count)
{
uint8_t reg_faults[FAULT_ARR_SIZE] = { [0 ... FAULT_ARR_SIZE-1] = 0xff };
console_printf("fault_get_cur() has_reg_fault %u\n", has_reg_fault);
*test_id = recent_test_id;
*company_id = CID_VENDOR;
*fault_count = min(*fault_count, sizeof(reg_faults));
memcpy(faults, reg_faults, *fault_count);
return 0;
}
static int
fault_get_reg(struct bt_mesh_model *model,
uint16_t company_id,
uint8_t *test_id,
uint8_t *faults,
uint8_t *fault_count)
{
if (company_id != CID_VENDOR) {
return -BLE_HS_EINVAL;
}
console_printf("fault_get_reg() has_reg_fault %u\n", has_reg_fault);
*test_id = recent_test_id;
if (has_reg_fault) {
uint8_t reg_faults[FAULT_ARR_SIZE] = { [0 ... FAULT_ARR_SIZE-1] = 0xff };
*fault_count = min(*fault_count, sizeof(reg_faults));
memcpy(faults, reg_faults, *fault_count);
} else {
*fault_count = 0;
}
return 0;
}
static int
fault_clear(struct bt_mesh_model *model, uint16_t company_id)
{
if (company_id != CID_VENDOR) {
return -BLE_HS_EINVAL;
}
has_reg_fault = false;
return 0;
}
static int
fault_test(struct bt_mesh_model *model, uint8_t test_id, uint16_t company_id)
{
if (company_id != CID_VENDOR) {
return -BLE_HS_EINVAL;
}
if (test_id != STANDARD_TEST_ID && test_id != TEST_ID) {
return -BLE_HS_EINVAL;
}
recent_test_id = test_id;
has_reg_fault = true;
bt_mesh_fault_update(bt_mesh_model_elem(model));
return 0;
}
static const struct bt_mesh_health_srv_cb health_srv_cb = {
.fault_get_cur = &fault_get_cur,
.fault_get_reg = &fault_get_reg,
.fault_clear = &fault_clear,
.fault_test = &fault_test,
};
static struct bt_mesh_health_srv health_srv = {
.cb = &health_srv_cb,
};
static struct bt_mesh_model_pub health_pub;
static void
health_pub_init(void)
{
health_pub.msg = BT_MESH_HEALTH_FAULT_MSG(0);
}
static struct bt_mesh_model_pub gen_level_pub;
static struct bt_mesh_model_pub gen_onoff_pub;
static uint8_t gen_on_off_state;
static int16_t gen_level_state;
static void gen_onoff_status(struct bt_mesh_model *model,
struct bt_mesh_msg_ctx *ctx)
{
struct os_mbuf *msg = NET_BUF_SIMPLE(3);
uint8_t *status;
console_printf("#mesh-onoff STATUS\n");
bt_mesh_model_msg_init(msg, BT_MESH_MODEL_OP_2(0x82, 0x04));
status = net_buf_simple_add(msg, 1);
*status = gen_on_off_state;
if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) {
console_printf("#mesh-onoff STATUS: send status failed\n");
}
os_mbuf_free_chain(msg);
}
static void gen_onoff_get(struct bt_mesh_model *model,
struct bt_mesh_msg_ctx *ctx,
struct os_mbuf *buf)
{
console_printf("#mesh-onoff GET\n");
gen_onoff_status(model, ctx);
}
static void gen_onoff_set(struct bt_mesh_model *model,
struct bt_mesh_msg_ctx *ctx,
struct os_mbuf *buf)
{
console_printf("#mesh-onoff SET\n");
gen_on_off_state = buf->om_data[0];
hal_gpio_write(LED_2, !gen_on_off_state);
gen_onoff_status(model, ctx);
}
static void gen_onoff_set_unack(struct bt_mesh_model *model,
struct bt_mesh_msg_ctx *ctx,
struct os_mbuf *buf)
{
console_printf("#mesh-onoff SET-UNACK\n");
gen_on_off_state = buf->om_data[0];
hal_gpio_write(LED_2, !gen_on_off_state);
}
static const struct bt_mesh_model_op gen_onoff_op[] = {
{ BT_MESH_MODEL_OP_2(0x82, 0x01), 0, gen_onoff_get },
{ BT_MESH_MODEL_OP_2(0x82, 0x02), 2, gen_onoff_set },
{ BT_MESH_MODEL_OP_2(0x82, 0x03), 2, gen_onoff_set_unack },
BT_MESH_MODEL_OP_END,
};
static void gen_level_status(struct bt_mesh_model *model,
struct bt_mesh_msg_ctx *ctx)
{
struct os_mbuf *msg = NET_BUF_SIMPLE(4);
console_printf("#mesh-level STATUS\n");
bt_mesh_model_msg_init(msg, BT_MESH_MODEL_OP_2(0x82, 0x08));
net_buf_simple_add_le16(msg, gen_level_state);
if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) {
console_printf("#mesh-level STATUS: send status failed\n");
}
os_mbuf_free_chain(msg);
}
static void gen_level_get(struct bt_mesh_model *model,
struct bt_mesh_msg_ctx *ctx,
struct os_mbuf *buf)
{
console_printf("#mesh-level GET\n");
gen_level_status(model, ctx);
}
static void gen_level_set(struct bt_mesh_model *model,
struct bt_mesh_msg_ctx *ctx,
struct os_mbuf *buf)
{
int16_t level;
level = (int16_t) net_buf_simple_pull_le16(buf);
console_printf("#mesh-level SET: level=%d\n", level);
gen_level_status(model, ctx);
gen_level_state = level;
console_printf("#mesh-level: level=%d\n", gen_level_state);
}
static void gen_level_set_unack(struct bt_mesh_model *model,
struct bt_mesh_msg_ctx *ctx,
struct os_mbuf *buf)
{
int16_t level;
level = (int16_t) net_buf_simple_pull_le16(buf);
console_printf("#mesh-level SET-UNACK: level=%d\n", level);
gen_level_state = level;
console_printf("#mesh-level: level=%d\n", gen_level_state);
}
static void gen_delta_set(struct bt_mesh_model *model,
struct bt_mesh_msg_ctx *ctx,
struct os_mbuf *buf)
{
int16_t delta_level;
delta_level = (int16_t) net_buf_simple_pull_le16(buf);
console_printf("#mesh-level DELTA-SET: delta_level=%d\n", delta_level);
gen_level_status(model, ctx);
gen_level_state += delta_level;
console_printf("#mesh-level: level=%d\n", gen_level_state);
}
static void gen_delta_set_unack(struct bt_mesh_model *model,
struct bt_mesh_msg_ctx *ctx,
struct os_mbuf *buf)
{
int16_t delta_level;
delta_level = (int16_t) net_buf_simple_pull_le16(buf);
console_printf("#mesh-level DELTA-SET: delta_level=%d\n", delta_level);
gen_level_state += delta_level;
console_printf("#mesh-level: level=%d\n", gen_level_state);
}
static void gen_move_set(struct bt_mesh_model *model,
struct bt_mesh_msg_ctx *ctx,
struct os_mbuf *buf)
{
}
static void gen_move_set_unack(struct bt_mesh_model *model,
struct bt_mesh_msg_ctx *ctx,
struct os_mbuf *buf)
{
}
static const struct bt_mesh_model_op gen_level_op[] = {
{ BT_MESH_MODEL_OP_2(0x82, 0x05), 0, gen_level_get },
{ BT_MESH_MODEL_OP_2(0x82, 0x06), 3, gen_level_set },
{ BT_MESH_MODEL_OP_2(0x82, 0x07), 3, gen_level_set_unack },
{ BT_MESH_MODEL_OP_2(0x82, 0x09), 5, gen_delta_set },
{ BT_MESH_MODEL_OP_2(0x82, 0x0a), 5, gen_delta_set_unack },
{ BT_MESH_MODEL_OP_2(0x82, 0x0b), 3, gen_move_set },
{ BT_MESH_MODEL_OP_2(0x82, 0x0c), 3, gen_move_set_unack },
BT_MESH_MODEL_OP_END,
};
static struct bt_mesh_model root_models[] = {
BT_MESH_MODEL_CFG_SRV(&cfg_srv),
BT_MESH_MODEL_HEALTH_SRV(&health_srv, &health_pub),
BT_MESH_MODEL(BT_MESH_MODEL_ID_GEN_ONOFF_SRV, gen_onoff_op,
&gen_onoff_pub, NULL),
BT_MESH_MODEL(BT_MESH_MODEL_ID_GEN_LEVEL_SRV, gen_level_op,
&gen_level_pub, NULL),
};
static struct bt_mesh_model_pub vnd_model_pub;
static void vnd_model_recv(struct bt_mesh_model *model,
struct bt_mesh_msg_ctx *ctx,
struct os_mbuf *buf)
{
struct os_mbuf *msg = NET_BUF_SIMPLE(3);
console_printf("#vendor-model-recv\n");
console_printf("data:%s len:%d\n", bt_hex(buf->om_data, buf->om_len),
buf->om_len);
bt_mesh_model_msg_init(msg, BT_MESH_MODEL_OP_3(0x01, CID_VENDOR));
os_mbuf_append(msg, buf->om_data, buf->om_len);
if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) {
console_printf("#vendor-model-recv: send rsp failed\n");
}
os_mbuf_free_chain(msg);
}
static const struct bt_mesh_model_op vnd_model_op[] = {
{ BT_MESH_MODEL_OP_3(0x01, CID_VENDOR), 0, vnd_model_recv },
BT_MESH_MODEL_OP_END,
};
static struct bt_mesh_model vnd_models[] = {
BT_MESH_MODEL_VND(CID_VENDOR, BT_MESH_MODEL_ID_GEN_ONOFF_SRV, vnd_model_op,
&vnd_model_pub, NULL),
};
static struct bt_mesh_elem elements[] = {
BT_MESH_ELEM(0, root_models, vnd_models),
};
static const struct bt_mesh_comp comp = {
.cid = CID_VENDOR,
.elem = elements,
.elem_count = ARRAY_SIZE(elements),
};
static int output_number(bt_mesh_output_action_t action, uint32_t number)
{
console_printf("OOB Number: %lu\n", number);
return 0;
}
static void prov_complete(u16_t net_idx, u16_t addr)
{
console_printf("Local node provisioned, primary address 0x%04x\n", addr);
}
static const uint8_t dev_uuid[16] = MYNEWT_VAL(BLE_MESH_DEV_UUID);
static const struct bt_mesh_prov prov = {
.uuid = dev_uuid,
.output_size = 4,
.output_actions = BT_MESH_DISPLAY_NUMBER | BT_MESH_BEEP | BT_MESH_VIBRATE | BT_MESH_BLINK,
.output_number = output_number,
.complete = prov_complete,
};
static void
blemesh_on_reset(int reason)
{
BLE_HS_LOG(ERROR, "Resetting state; reason=%d\n", reason);
}
static void
blemesh_on_sync(void)
{
int err;
ble_addr_t addr;
console_printf("Bluetooth initialized\n");
/* Use NRPA */
err = ble_hs_id_gen_rnd(1, &addr);
assert(err == 0);
err = ble_hs_id_set_rnd(addr.val);
assert(err == 0);
err = bt_mesh_init(addr.type, &prov, &comp);
if (err) {
console_printf("Initializing mesh failed (err %d)\n", err);
return;
}
#if (MYNEWT_VAL(BLE_MESH_SHELL))
shell_register_default_module("mesh");
#endif
console_printf("Mesh initialized\n");
if (IS_ENABLED(CONFIG_SETTINGS)) {
settings_load();
}
if (bt_mesh_is_provisioned()) {
printk("Mesh network restored from flash\n");
}
}
int
main(int argc, char **argv)
{
#ifdef ARCH_sim
mcu_sim_parse_args(argc, argv);
#endif
/* Initialize OS */
sysinit();
/* Initialize the NimBLE host configuration. */
ble_hs_cfg.reset_cb = blemesh_on_reset;
ble_hs_cfg.sync_cb = blemesh_on_sync;
ble_hs_cfg.store_status_cb = ble_store_util_status_rr;
hal_gpio_init_out(LED_2, 0);
health_pub_init();
while (1) {
os_eventq_run(os_eventq_dflt_get());
}
return 0;
}

View File

@@ -0,0 +1,52 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you 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.
#
syscfg.vals:
# Enable the shell task.
SHELL_TASK: 1
# Set log level to info (disable debug logging).
LOG_LEVEL: 1
# Default task settings
OS_MAIN_STACK_SIZE: 768
# Newtmgr is not supported in this app, so disable newtmgr-over-shell.
SHELL_NEWTMGR: 0
BLE_MESH: 1
MSYS_1_BLOCK_COUNT: 48
BLE_MESH_ADV_BUF_COUNT: 20
BLE_MESH_TX_SEG_MAX: 6
BLE_MESH_DEBUG: 1
BLE_MESH_DEBUG_NET: 1
BLE_MESH_DEBUG_TRANS: 1
BLE_MESH_DEBUG_BEACON: 1
BLE_MESH_DEBUG_CRYPTO: 1
BLE_MESH_DEBUG_PROV: 1
BLE_MESH_DEBUG_ACCESS: 1
BLE_MESH_DEBUG_MODEL: 1
BLE_MESH_DEBUG_ADV: 1
BLE_MESH_DEBUG_LOW_POWER: 1
BLE_MESH_DEBUG_FRIEND: 1
BLE_MESH_DEBUG_PROXY: 1
BLE_MESH_SETTINGS: 0
CONFIG_NFFS: 0

View File

@@ -0,0 +1,37 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you 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.
#
pkg.name: apps/blemesh_light
pkg.type: app
pkg.description: Sample application for BLE Mesh node with Light model
pkg.author: "Michał Narajowski <michal.narajowski@codecoup.pl>"
pkg.homepage: "http://mynewt.apache.org/"
pkg.keywords:
pkg.deps:
- "@apache-mynewt-core/kernel/os"
- "@apache-mynewt-core/sys/console/full"
- "@apache-mynewt-core/sys/log/full"
- "@apache-mynewt-core/sys/log/modlog"
- "@apache-mynewt-core/sys/stats/full"
- "@apache-mynewt-core/sys/shell"
- nimble/controller
- nimble/host
- nimble/host/services/gap
- nimble/host/services/gatt
- nimble/host/store/ram
- nimble/transport/ram

View File

@@ -0,0 +1,242 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
#include "syscfg/syscfg.h"
#if MYNEWT_VAL(BLE_MESH_SHELL_MODELS)
#include "mesh/mesh.h"
#include "bsp.h"
#include "pwm/pwm.h"
#include "light_model.h"
#include "ws2812.h"
#if (!MYNEWT_VAL(USE_NEOPIXEL))
#if MYNEWT_VAL(PWM_0)
struct pwm_dev *pwm0;
#endif
#if MYNEWT_VAL(PWM_1)
struct pwm_dev *pwm1;
#endif
#if MYNEWT_VAL(PWM_2)
struct pwm_dev *pwm2;
#endif
#if MYNEWT_VAL(PWM_3)
struct pwm_dev *pwm3;
#endif
static uint16_t top_val;
#endif
#if (MYNEWT_VAL(USE_NEOPIXEL))
static uint32_t neopixel[WS2812_NUM_LED];
#endif
static u8_t gen_onoff_state;
static s16_t gen_level_state;
static void light_set_lightness(u8_t percentage)
{
#if (!MYNEWT_VAL(USE_NEOPIXEL))
int rc;
uint16_t pwm_val = (uint16_t) (percentage * top_val / 100);
#if MYNEWT_VAL(PWM_0)
rc = pwm_set_duty_cycle(pwm0, 0, pwm_val);
assert(rc == 0);
#endif
#if MYNEWT_VAL(PWM_1)
rc = pwm_set_duty_cycle(pwm1, 0, pwm_val);
assert(rc == 0);
#endif
#if MYNEWT_VAL(PWM_2)
rc = pwm_set_duty_cycle(pwm2, 0, pwm_val);
assert(rc == 0);
#endif
#if MYNEWT_VAL(PWM_3)
rc = pwm_set_duty_cycle(pwm3, 0, pwm_val);
assert(rc == 0);
#endif
#else
int i;
u32_t lightness;
u8_t max_lightness = 0x1f;
lightness = (u8_t) (percentage * max_lightness / 100);
for (i = 0; i < WS2812_NUM_LED; i++) {
neopixel[i] = (lightness | lightness << 8 | lightness << 16);
}
ws2812_write(neopixel);
#endif
}
static void update_light_state(void)
{
u16_t level = (u16_t)gen_level_state;
int percent = 100 * level / 0xffff;
if (gen_onoff_state == 0) {
percent = 0;
}
light_set_lightness((uint8_t) percent);
}
int light_model_gen_onoff_get(struct bt_mesh_model *model, u8_t *state)
{
*state = gen_onoff_state;
return 0;
}
int light_model_gen_onoff_set(struct bt_mesh_model *model, u8_t state)
{
gen_onoff_state = state;
update_light_state();
return 0;
}
int light_model_gen_level_get(struct bt_mesh_model *model, s16_t *level)
{
*level = gen_level_state;
return 0;
}
int light_model_gen_level_set(struct bt_mesh_model *model, s16_t level)
{
gen_level_state = level;
if ((u16_t)gen_level_state > 0x0000) {
gen_onoff_state = 1;
}
if ((u16_t)gen_level_state == 0x0000) {
gen_onoff_state = 0;
}
update_light_state();
return 0;
}
int light_model_light_lightness_get(struct bt_mesh_model *model, s16_t *lightness)
{
return light_model_gen_level_get(model, lightness);
}
int light_model_light_lightness_set(struct bt_mesh_model *model, s16_t lightness)
{
return light_model_gen_level_set(model, lightness);
}
#if (!MYNEWT_VAL(USE_NEOPIXEL))
struct pwm_dev_cfg dev_conf = {
.n_cycles = 0,
.int_prio = 3,
};
#if MYNEWT_VAL(PWM_0)
static struct pwm_chan_cfg led1_conf = {
.pin = LED_1,
.inverted = true,
};
#endif
#if MYNEWT_VAL(PWM_1)
static struct pwm_chan_cfg led2_conf = {
.pin = LED_2,
.inverted = true,
};
#endif
#if MYNEWT_VAL(PWM_2)
static struct pwm_chan_cfg led3_conf = {
.pin = LED_3,
.inverted = true,
};
#endif
#endif
#if MYNEWT_VAL(PWM_3)
static struct pwm_chan_cfg led4_conf = {
.pin = LED_4,
.inverted = true,
};
#endif
#if (!MYNEWT_VAL(USE_NEOPIXEL))
void init_pwm_dev(struct pwm_dev **pwm, char *dev_name, struct pwm_chan_cfg *chan_cfg)
{
int rc = 0;
*pwm = (struct pwm_dev *) os_dev_open(dev_name, 0, NULL);
assert(pwm);
rc = pwm_configure_device(*pwm, &dev_conf);
assert(rc == 0);
rc = pwm_configure_channel(*pwm, 0, chan_cfg);
assert(rc == 0);
rc = pwm_enable(*pwm);
assert(rc == 0);
}
int pwm_init(void)
{
#if MYNEWT_VAL(PWM_0)
init_pwm_dev(&pwm0, "pwm0", &led1_conf);
#endif
#if MYNEWT_VAL(PWM_1)
init_pwm_dev(&pwm1, "pwm1", &led2_conf);
#endif
#if MYNEWT_VAL(PWM_2)
init_pwm_dev(&pwm2, "pwm2", &led3_conf);
#endif
#if MYNEWT_VAL(PWM_3)
init_pwm_dev(&pwm3, "pwm3", &led4_conf);
#endif
if (!pwm0) {
return 0;
}
top_val = (uint16_t) pwm_get_top_value(pwm0);
update_light_state();
return 0;
}
#endif
#endif
int light_model_init(void)
{
#if MYNEWT_VAL(BLE_MESH_SHELL_MODELS)
int rc;
#if (!MYNEWT_VAL(USE_NEOPIXEL))
rc = pwm_init();
assert(rc == 0);
#else
rc = ws2812_init();
assert(rc == 0);
update_light_state();
#endif
return rc;
#else
return 0;
#endif
}

View File

@@ -0,0 +1,37 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*
* Copyright (c) 2017 Intel Corporation
*
*/
#ifndef __BT_MESH_LIGHT_MODEL_H
#define __BT_MESH_LIGHT_MODEL_H
#include "syscfg/syscfg.h"
#include "mesh/mesh.h"
int light_model_gen_onoff_get(struct bt_mesh_model *model, u8_t *state);
int light_model_gen_onoff_set(struct bt_mesh_model *model, u8_t state);
int light_model_gen_level_get(struct bt_mesh_model *model, s16_t *level);
int light_model_gen_level_set(struct bt_mesh_model *model, s16_t level);
int light_model_light_lightness_get(struct bt_mesh_model *model, s16_t *lightness);
int light_model_light_lightness_set(struct bt_mesh_model *model, s16_t lightness);
int light_model_init(void);
#endif

View File

@@ -0,0 +1,128 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
#include <assert.h>
#include "sysinit/sysinit.h"
#include "os/os.h"
#include "mesh/mesh.h"
#include "console/console.h"
#include "hal/hal_system.h"
#include "hal/hal_gpio.h"
#include "bsp/bsp.h"
#include "shell/shell.h"
/* BLE */
#include "nimble/ble.h"
#include "host/ble_hs.h"
#include "services/gap/ble_svc_gap.h"
#include "mesh/glue.h"
#include "mesh/testing.h"
#include "mesh/model_srv.h"
#include "light_model.h"
static void model_bound_cb(u16_t addr, struct bt_mesh_model *model,
u16_t key_idx)
{
int rc;
console_printf("Model bound: remote addr 0x%04x key_idx 0x%04x model %p\n",
addr, key_idx, model);
if (model->id != BT_MESH_MODEL_ID_GEN_LEVEL_SRV) {
return;
}
/* Hack for demo purposes */
rc = bt_test_bind_app_key_to_model(model, key_idx,
BT_MESH_MODEL_ID_LIGHT_LIGHTNESS_SRV);
if (rc) {
console_printf("Failed to bind light lightness srv model to app_key");
} else {
console_printf("Successfuly bound light lightness srv model to app_key");
}
}
static struct bt_test_cb bt_test_cb = {
.mesh_model_bound = model_bound_cb,
};
static void
blemesh_on_reset(int reason)
{
BLE_HS_LOG(ERROR, "Resetting state; reason=%d\n", reason);
}
static struct bt_mesh_gen_onoff_srv_cb gen_onoff_srv_cb = {
.get = light_model_gen_onoff_get,
.set = light_model_gen_onoff_set,
};
static struct bt_mesh_gen_level_srv_cb gen_level_srv_cb = {
.get = light_model_gen_level_get,
.set = light_model_gen_level_set,
};
static struct bt_mesh_light_lightness_srv_cb light_lightness_srv_cb = {
.get = light_model_light_lightness_get,
.set = light_model_light_lightness_set,
};
static void
blemesh_on_sync(void)
{
console_printf("Bluetooth initialized\n");
shell_register_default_module("mesh");
bt_test_cb_register(&bt_test_cb);
light_model_init();
bt_mesh_set_gen_onoff_srv_cb(&gen_onoff_srv_cb);
bt_mesh_set_gen_level_srv_cb(&gen_level_srv_cb);
bt_mesh_set_light_lightness_srv_cb(&light_lightness_srv_cb);
console_printf("Mesh initialized\n");
if (IS_ENABLED(CONFIG_SETTINGS)) {
settings_load();
}
if (bt_mesh_is_provisioned()) {
printk("Mesh network restored from flash\n");
}
/* Hack for demo purposes */
bt_test_shell_init();
}
int
main(void)
{
/* Initialize OS */
sysinit();
/* Initialize the NimBLE host configuration. */
ble_hs_cfg.reset_cb = blemesh_on_reset;
ble_hs_cfg.sync_cb = blemesh_on_sync;
ble_hs_cfg.store_status_cb = ble_store_util_status_rr;
while (1) {
os_eventq_run(os_eventq_dflt_get());
}
return 0;
}

View File

@@ -0,0 +1,138 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
#include "syscfg/syscfg.h"
#if (MYNEWT_VAL(USE_NEOPIXEL))
#include <assert.h>
#include <string.h>
#include <stdio.h>
#include "sysinit/sysinit.h"
#include "os/os.h"
#include "bsp/bsp.h"
#include "pwm/pwm.h"
#include "nrfx.h"
#include "nrfx_pwm.h"
#include "ws2812.h"
#define BITS_PER_SEQ (24)
#define BIT0 (0x8000 | 6)
#define BIT1 (0x8000 | 11)
static const nrfx_pwm_t pwm = NRFX_PWM_INSTANCE(WS2812_PWM);
static const nrfx_pwm_config_t pwm_config = {
.output_pins = { WS2812_GPIO, NRFX_PWM_PIN_NOT_USED, NRFX_PWM_PIN_NOT_USED, NRFX_PWM_PIN_NOT_USED },
.irq_priority = 3,
.base_clock = NRF_PWM_CLK_16MHz,
.count_mode = NRF_PWM_MODE_UP,
.top_value = 20,
.load_mode = NRF_PWM_LOAD_COMMON,
.step_mode = NRF_PWM_STEP_AUTO,
};
static uint16_t pwm_seq_values[2][BITS_PER_SEQ];
static const nrf_pwm_sequence_t pwm_seq[2] = {
{
.values.p_raw = pwm_seq_values[0],
.length = BITS_PER_SEQ,
.repeats = 0,
.end_delay = 0,
}, {
.values.p_raw = pwm_seq_values[1],
.length = BITS_PER_SEQ,
.repeats = 0,
.end_delay = 0,
},
};
static uint32_t led_color[WS2812_NUM_LED];
static int led_idx;
static void
load_pixel(void)
{
uint16_t *seq_values;
uint32_t grb;
int i;
seq_values = pwm_seq_values[led_idx & 1];
grb = led_color[led_idx];
for (i = 0; i < BITS_PER_SEQ; i++) {
*seq_values = grb & 0x800000 ? BIT1 : BIT0;
grb <<= 1;
seq_values++;
}
led_idx++;
}
static void
pwm_handler_func(nrfx_pwm_evt_type_t event_type)
{
switch (event_type) {
case NRFX_PWM_EVT_END_SEQ0:
case NRFX_PWM_EVT_END_SEQ1:
load_pixel();
break;
default:
break;
}
}
int
ws2812_init(void)
{
nrfx_err_t err;
err = nrfx_pwm_init(&pwm, &pwm_config, pwm_handler_func);
return err != NRFX_SUCCESS;
}
int
ws2812_write(const uint32_t *rgb)
{
uint32_t grb;
int i;
for (i = 0; i < WS2812_NUM_LED; i++) {
grb = 0;
grb |= (rgb[i] & 0x00FF00) << 8;
grb |= (rgb[i] & 0xFF0000) >> 8;
grb |= (rgb[i] & 0x0000FF);
led_color[i] = grb;
}
led_idx = 0;
load_pixel();
load_pixel();
nrfx_pwm_complex_playback(&pwm, &pwm_seq[0], &pwm_seq[1], WS2812_NUM_LED,
NRFX_PWM_FLAG_SIGNAL_END_SEQ0 |
NRFX_PWM_FLAG_SIGNAL_END_SEQ1 |
NRFX_PWM_FLAG_STOP);
return 0;
}
#endif

View File

@@ -0,0 +1,42 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 __WS2812_H__
#define __WS2812_H__
#include <stddef.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
#define WS2812_PWM 0
#define WS2812_GPIO 30
#define WS2812_NUM_LED 32
int ws2812_init(void);
int ws2812_write(const uint32_t *rgb);
#ifdef __cplusplus
}
#endif
#endif /* __WS2812_H__ */

View File

@@ -0,0 +1,63 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you 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.
#
syscfg.defs:
USE_NEOPIXEL:
value: 0
syscfg.vals:
# Enable the shell task.
SHELL_TASK: 1
# Set log level to info (disable debug logging).
LOG_LEVEL: 1
# Default task settings
OS_MAIN_STACK_SIZE: 768
# Newtmgr is not supported in this app, so disable newtmgr-over-shell.
SHELL_NEWTMGR: 0
MSYS_1_BLOCK_COUNT: 80
BLE_MESH_ADV_BUF_COUNT: 20
BLE_MESH_TX_SEG_MAX: 6
BLE_MESH: 1
BLE_MESH_SHELL: 1
BLE_MESH_PROV: 1
BLE_MESH_PB_ADV: 1
BLE_MESH_PB_GATT: 1
BLE_MESH_GATT_PROXY: 1
BLE_MESH_TESTING: 1
BLE_MESH_FRIEND: 0
BLE_MESH_CFG_CLI: 1
BLE_MESH_HEALTH_CLI: 0
BLE_MESH_SHELL_MODELS: 1
BLE_MESH_OOB_OUTPUT_ACTIONS: 0
BLE_MESH_SETTINGS: 0
CONFIG_NFFS: 0
USE_NEOPIXEL: 0
syscfg.vals.BLE_MESH_SHELL_MODELS:
PWM_0: 1
PWM_1: 1
PWM_2: 1
PWM_3: 1

View File

@@ -0,0 +1,79 @@
### Bluetooth: Mesh OnOff Model
#### Overview
This is a simple application demonstrating a Bluetooth mesh multi-element node.
Each element has a mesh onoff client and server
model which controls one of the 4 sets of buttons and LEDs .
Prior to provisioning, an unprovisioned beacon is broadcast that contains
a UUID. Each button controls the state of its
corresponding LED and does not initiate any mesh activity.
The models for button 1 and LED 1 are in the node's root element.
The 3 remaining button/LED pairs are in elements 1 through 3.
Assuming the provisioner assigns 0x100 to the root element,
the secondary elements will appear at 0x101, 0x102 and 0x103.
After provisioning, the button clients must
be configured to publish and the LED servers to subscribe.
If a LED server is provided with a publish address, it will
also publish its status on an onoff state change.
If a button is pressed only once within a 1 second interval, it sends an
"on" message. If it is pressed more than once, it
sends an "off" message. The buttons are quite noisy and are debounced in
the button_pressed() interrupt handler. An interrupt within 250ms of the
previous is discarded. This might seem a little clumsy, but the alternative of
using one button for "on" and another for "off" would reduce the number
of onoff clients from 4 to 2.
#### Requirements
************
This sample has been tested on the Nordic nRF52840-PDK board, but would
likely also run on the nrf52_pca10040 board.
#### Building and Running
********************
Prior to provisioning, each button controls its corresponding LED as one
would expect with an actual switch.
Provisioning is done using the BlueZ meshctl utility. Below is an example that
binds button 2 and LED 1 to application key 1. It then configures button 2
to publish to group 0xc000 and LED 1 to subscribe to that group.
```
discover-unprovisioned on
provision <discovered UUID>
menu config
target 0100
appkey-add 1
bind 0 1 1000 # bind appkey 1 to LED server on element 0 (unicast 0100)
sub-add 0100 c000 1000 # add subscription to group address c000 to the LED server
bind 1 1 1001 # bind appkey 1 to button 2 on element 1 (unicast 0101)
pub-set 0101 c000 1 0 0 1001 # publish button 2 to group address c000
```
The meshctl utility maintains a persistent JSON database containing
the mesh configuration. As additional nodes (boards) are provisioned, it
assigns sequential unicast addresses based on the number of elements
supported by the node. This example supports 4 elements per node.
The first or root element of the node contains models for configuration,
health, and onoff. The secondary elements only
have models for onoff. The meshctl target for configuration must be the
root element's unicast address as it is the only one that has a
configuration server model.
If meshctl is gracefully exited, it can be restarted and reconnected to
network 0x0.
The meshctl utility also supports a onoff model client that can be used to
change the state of any LED that is bound to application key 0x1.
This is done by setting the target to the unicast address of the element
that has that LED's model and issuing the onoff command.
Group addresses are not supported.

View File

@@ -0,0 +1,34 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you 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.
#
pkg.name: apps/blemesh_models_example_1
pkg.type: app
pkg.description: Sample application for BLE Mesh node with on/off model on nRF52840pdk
pkg.author: "Michał Narajowski <michal.narajowski@codecoup.pl>"
pkg.homepage: "http://mynewt.apache.org/"
pkg.keywords:
pkg.deps:
- "@apache-mynewt-core/kernel/os"
- "@apache-mynewt-core/sys/console/full"
- "@apache-mynewt-core/sys/log/full"
- "@apache-mynewt-core/sys/stats/full"
- nimble/controller
- nimble/host
- nimble/host/services/gap
- nimble/host/services/gatt
- nimble/transport/ram

View File

@@ -0,0 +1,709 @@
/* main.c - Application main entry point */
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
/*
* Copyright (c) 2017 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
/*
* This application is specific to the Nordic nRF52840-PDK board.
*
* It supports the 4 buttons and 4 LEDs as mesh clients and servers.
*
* Prior to provisioning, a button inverts the state of the
* corresponding LED.
*
* Button and LED 1 are in the root node.
* The 3 remaining button/LED pairs are in element 1 through 3.
* Assuming the provisioner assigns 0x100 to the root node,
* the secondary elements will appear at 0x101, 0x102 and 0x103.
*
* It's anticipated that after provisioning, the button clients would
* be configured to publish and the LED servers to subscribe.
*
* If a LED server is provided with a publish address, it will
* also publish its status on a state change.
*
* Messages from a button to its corresponding LED are ignored as
* the LED's state has already been changed locally by the button client.
*
* The buttons are debounced at a nominal 250ms. That value can be
* changed as needed.
*
*/
#include "os/mynewt.h"
#include "bsp/bsp.h"
#include "console/console.h"
#include "hal/hal_gpio.h"
#include "host/ble_hs.h"
#include "mesh/glue.h"
#include "mesh/mesh.h"
#define CID_RUNTIME 0x05C3
/* Model Operation Codes */
#define BT_MESH_MODEL_OP_GEN_ONOFF_GET BT_MESH_MODEL_OP_2(0x82, 0x01)
#define BT_MESH_MODEL_OP_GEN_ONOFF_SET BT_MESH_MODEL_OP_2(0x82, 0x02)
#define BT_MESH_MODEL_OP_GEN_ONOFF_SET_UNACK BT_MESH_MODEL_OP_2(0x82, 0x03)
#define BT_MESH_MODEL_OP_GEN_ONOFF_STATUS BT_MESH_MODEL_OP_2(0x82, 0x04)
static void gen_onoff_set(struct bt_mesh_model *model,
struct bt_mesh_msg_ctx *ctx,
struct os_mbuf *buf);
static void gen_onoff_set_unack(struct bt_mesh_model *model,
struct bt_mesh_msg_ctx *ctx,
struct os_mbuf *buf);
static void gen_onoff_get(struct bt_mesh_model *model,
struct bt_mesh_msg_ctx *ctx,
struct os_mbuf *buf);
static void gen_onoff_status(struct bt_mesh_model *model,
struct bt_mesh_msg_ctx *ctx,
struct os_mbuf *buf);
/*
* Server Configuration Declaration
*/
static struct bt_mesh_cfg_srv cfg_srv = {
.relay = BT_MESH_RELAY_DISABLED,
.beacon = BT_MESH_BEACON_ENABLED,
#if defined(CONFIG_BT_MESH_FRIEND)
.frnd = BT_MESH_FRIEND_ENABLED,
#else
.frnd = BT_MESH_FRIEND_NOT_SUPPORTED,
#endif
#if defined(CONFIG_BT_MESH_GATT_PROXY)
.gatt_proxy = BT_MESH_GATT_PROXY_ENABLED,
#else
.gatt_proxy = BT_MESH_GATT_PROXY_NOT_SUPPORTED,
#endif
.default_ttl = 7,
/* 3 transmissions with 20ms interval */
.net_transmit = BT_MESH_TRANSMIT(2, 20),
.relay_retransmit = BT_MESH_TRANSMIT(2, 20),
};
/*
* Client Configuration Declaration
*/
static struct bt_mesh_cfg_cli cfg_cli = {
};
/*
* Health Server Declaration
*/
static struct bt_mesh_health_srv health_srv = {
};
/*
* Publication Declarations
*
* The publication messages are initialized to the
* the size of the opcode + content
*
* For publication, the message must be in static or global as
* it is re-transmitted several times. This occurs
* after the function that called bt_mesh_model_publish() has
* exited and the stack is no longer valid.
*
* Note that the additional 4 bytes for the AppMIC is not needed
* because it is added to a stack variable at the time a
* transmission occurs.
*
*/
static struct bt_mesh_model_pub health_pub;
static struct bt_mesh_model_pub gen_onoff_pub_srv;
static struct bt_mesh_model_pub gen_onoff_pub_cli;
static struct bt_mesh_model_pub gen_onoff_pub_srv_s_0;
static struct bt_mesh_model_pub gen_onoff_pub_cli_s_0;
static struct bt_mesh_model_pub gen_onoff_pub_srv_s_1;
static struct bt_mesh_model_pub gen_onoff_pub_cli_s_1;
static struct bt_mesh_model_pub gen_onoff_pub_srv_s_2;
static struct bt_mesh_model_pub gen_onoff_pub_cli_s_2;
static struct os_mbuf *bt_mesh_pub_msg_health_pub;
static struct os_mbuf *bt_mesh_pub_msg_gen_onoff_pub_srv;
static struct os_mbuf *bt_mesh_pub_msg_gen_onoff_pub_cli;
static struct os_mbuf *bt_mesh_pub_msg_gen_onoff_pub_srv_s_0;
static struct os_mbuf *bt_mesh_pub_msg_gen_onoff_pub_cli_s_0;
static struct os_mbuf *bt_mesh_pub_msg_gen_onoff_pub_srv_s_1;
static struct os_mbuf *bt_mesh_pub_msg_gen_onoff_pub_cli_s_1;
static struct os_mbuf *bt_mesh_pub_msg_gen_onoff_pub_srv_s_2;
static struct os_mbuf *bt_mesh_pub_msg_gen_onoff_pub_cli_s_2;
void init_pub(void)
{
bt_mesh_pub_msg_health_pub = NET_BUF_SIMPLE(1 + 3 + 0);
bt_mesh_pub_msg_gen_onoff_pub_srv = NET_BUF_SIMPLE(2 + 2);
bt_mesh_pub_msg_gen_onoff_pub_cli = NET_BUF_SIMPLE(2 + 2);
bt_mesh_pub_msg_gen_onoff_pub_srv_s_0 = NET_BUF_SIMPLE(2 + 2);
bt_mesh_pub_msg_gen_onoff_pub_cli_s_0 = NET_BUF_SIMPLE(2 + 2);
bt_mesh_pub_msg_gen_onoff_pub_srv_s_1 = NET_BUF_SIMPLE(2 + 2);
bt_mesh_pub_msg_gen_onoff_pub_cli_s_1 = NET_BUF_SIMPLE(2 + 2);
bt_mesh_pub_msg_gen_onoff_pub_srv_s_2 = NET_BUF_SIMPLE(2 + 2);
bt_mesh_pub_msg_gen_onoff_pub_cli_s_2 = NET_BUF_SIMPLE(2 + 2);
health_pub.msg = bt_mesh_pub_msg_health_pub;
gen_onoff_pub_srv.msg = bt_mesh_pub_msg_gen_onoff_pub_srv;
gen_onoff_pub_cli.msg = bt_mesh_pub_msg_gen_onoff_pub_cli;
gen_onoff_pub_srv_s_0.msg = bt_mesh_pub_msg_gen_onoff_pub_srv_s_0;
gen_onoff_pub_cli_s_0.msg = bt_mesh_pub_msg_gen_onoff_pub_cli_s_0;
gen_onoff_pub_srv_s_1.msg = bt_mesh_pub_msg_gen_onoff_pub_srv_s_1;
gen_onoff_pub_cli_s_1.msg = bt_mesh_pub_msg_gen_onoff_pub_cli_s_1;
gen_onoff_pub_srv_s_2.msg = bt_mesh_pub_msg_gen_onoff_pub_srv_s_2;
gen_onoff_pub_cli_s_2.msg = bt_mesh_pub_msg_gen_onoff_pub_cli_s_2;
}
/*
* Models in an element must have unique op codes.
*
* The mesh stack dispatches a message to the first model in an element
* that is also bound to an app key and supports the op code in the
* received message.
*
*/
/*
* OnOff Model Server Op Dispatch Table
*
*/
static const struct bt_mesh_model_op gen_onoff_srv_op[] = {
{ BT_MESH_MODEL_OP_GEN_ONOFF_GET, 0, gen_onoff_get },
{ BT_MESH_MODEL_OP_GEN_ONOFF_SET, 2, gen_onoff_set },
{ BT_MESH_MODEL_OP_GEN_ONOFF_SET_UNACK, 2, gen_onoff_set_unack },
BT_MESH_MODEL_OP_END,
};
/*
* OnOff Model Client Op Dispatch Table
*/
static const struct bt_mesh_model_op gen_onoff_cli_op[] = {
{ BT_MESH_MODEL_OP_GEN_ONOFF_STATUS, 1, gen_onoff_status },
BT_MESH_MODEL_OP_END,
};
struct onoff_state {
u8_t current;
u8_t previous;
u8_t led_gpio_pin;
};
/*
* Declare and Initialize Element Contexts
* Change to select different GPIO output pins
*/
static struct onoff_state onoff_state_arr[] = {
{ .led_gpio_pin = LED_1 },
{ .led_gpio_pin = LED_2 },
{ .led_gpio_pin = LED_3 },
{ .led_gpio_pin = LED_4 },
};
/*
*
* Element Model Declarations
*
* Element 0 Root Models
*/
static struct bt_mesh_model root_models[] = {
BT_MESH_MODEL_CFG_SRV(&cfg_srv),
BT_MESH_MODEL_CFG_CLI(&cfg_cli),
BT_MESH_MODEL_HEALTH_SRV(&health_srv, &health_pub),
BT_MESH_MODEL(BT_MESH_MODEL_ID_GEN_ONOFF_SRV, gen_onoff_srv_op,
&gen_onoff_pub_srv, &onoff_state_arr[0]),
BT_MESH_MODEL(BT_MESH_MODEL_ID_GEN_ONOFF_CLI, gen_onoff_cli_op,
&gen_onoff_pub_cli, &onoff_state_arr[0]),
};
/*
* Element 1 Models
*/
static struct bt_mesh_model secondary_0_models[] = {
BT_MESH_MODEL(BT_MESH_MODEL_ID_GEN_ONOFF_SRV, gen_onoff_srv_op,
&gen_onoff_pub_srv_s_0, &onoff_state_arr[1]),
BT_MESH_MODEL(BT_MESH_MODEL_ID_GEN_ONOFF_CLI, gen_onoff_cli_op,
&gen_onoff_pub_cli_s_0, &onoff_state_arr[1]),
};
/*
* Element 2 Models
*/
static struct bt_mesh_model secondary_1_models[] = {
BT_MESH_MODEL(BT_MESH_MODEL_ID_GEN_ONOFF_SRV, gen_onoff_srv_op,
&gen_onoff_pub_srv_s_1, &onoff_state_arr[2]),
BT_MESH_MODEL(BT_MESH_MODEL_ID_GEN_ONOFF_CLI, gen_onoff_cli_op,
&gen_onoff_pub_cli_s_1, &onoff_state_arr[2]),
};
/*
* Element 3 Models
*/
static struct bt_mesh_model secondary_2_models[] = {
BT_MESH_MODEL(BT_MESH_MODEL_ID_GEN_ONOFF_SRV, gen_onoff_srv_op,
&gen_onoff_pub_srv_s_2, &onoff_state_arr[3]),
BT_MESH_MODEL(BT_MESH_MODEL_ID_GEN_ONOFF_CLI, gen_onoff_cli_op,
&gen_onoff_pub_cli_s_2, &onoff_state_arr[3]),
};
/*
* Button to Client Model Assignments
*/
struct bt_mesh_model *mod_cli_sw[] = {
&root_models[4],
&secondary_0_models[1],
&secondary_1_models[1],
&secondary_2_models[1],
};
/*
* LED to Server Model Assigmnents
*/
struct bt_mesh_model *mod_srv_sw[] = {
&root_models[3],
&secondary_0_models[0],
&secondary_1_models[0],
&secondary_2_models[0],
};
/*
* Root and Secondary Element Declarations
*/
static struct bt_mesh_elem elements[] = {
BT_MESH_ELEM(0, root_models, BT_MESH_MODEL_NONE),
BT_MESH_ELEM(0, secondary_0_models, BT_MESH_MODEL_NONE),
BT_MESH_ELEM(0, secondary_1_models, BT_MESH_MODEL_NONE),
BT_MESH_ELEM(0, secondary_2_models, BT_MESH_MODEL_NONE),
};
static const struct bt_mesh_comp comp = {
.cid = CID_RUNTIME,
.elem = elements,
.elem_count = ARRAY_SIZE(elements),
};
struct sw {
u8_t sw_num;
u8_t onoff_state;
struct os_callout button_work;
struct os_callout button_timer;
};
static u8_t button_press_cnt;
static struct sw sw;
static u8_t trans_id;
static u32_t time, last_time;
static u16_t primary_addr;
static u16_t primary_net_idx;
/*
* Generic OnOff Model Server Message Handlers
*
* Mesh Model Specification 3.1.1
*
*/
static void gen_onoff_get(struct bt_mesh_model *model,
struct bt_mesh_msg_ctx *ctx,
struct os_mbuf *buf)
{
struct os_mbuf *msg = NET_BUF_SIMPLE(2 + 1 + 4);
struct onoff_state *state = model->user_data;
BT_INFO("addr 0x%04x onoff 0x%02x",
bt_mesh_model_elem(model)->addr, state->current);
bt_mesh_model_msg_init(msg, BT_MESH_MODEL_OP_GEN_ONOFF_STATUS);
net_buf_simple_add_u8(msg, state->current);
if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) {
BT_ERR("Unable to send On Off Status response");
}
os_mbuf_free_chain(msg);
}
static void gen_onoff_set_unack(struct bt_mesh_model *model,
struct bt_mesh_msg_ctx *ctx,
struct os_mbuf *buf)
{
struct os_mbuf *msg = model->pub->msg;
struct onoff_state *state = model->user_data;
int err;
state->current = net_buf_simple_pull_u8(buf);
BT_INFO("addr 0x%02x state 0x%02x",
bt_mesh_model_elem(model)->addr, state->current);
/* Pin set low turns on LED's on the nrf52840-pca10056 board */
hal_gpio_write(state->led_gpio_pin,
state->current ? 0 : 1);
/*
* If a server has a publish address, it is required to
* publish status on a state change
*
* See Mesh Profile Specification 3.7.6.1.2
*
* Only publish if there is an assigned address
*/
if (state->previous != state->current &&
model->pub->addr != BT_MESH_ADDR_UNASSIGNED) {
BT_INFO("publish last 0x%02x cur 0x%02x",
state->previous,
state->current);
state->previous = state->current;
bt_mesh_model_msg_init(msg,
BT_MESH_MODEL_OP_GEN_ONOFF_STATUS);
net_buf_simple_add_u8(msg, state->current);
err = bt_mesh_model_publish(model);
if (err) {
BT_ERR("bt_mesh_model_publish err %d", err);
}
}
}
static void gen_onoff_set(struct bt_mesh_model *model,
struct bt_mesh_msg_ctx *ctx,
struct os_mbuf *buf)
{
BT_INFO("");
gen_onoff_set_unack(model, ctx, buf);
gen_onoff_get(model, ctx, buf);
}
static void gen_onoff_status(struct bt_mesh_model *model,
struct bt_mesh_msg_ctx *ctx,
struct os_mbuf *buf)
{
u8_t state;
state = net_buf_simple_pull_u8(buf);
BT_INFO("Node 0x%04x OnOff status from 0x%04x with state 0x%02x",
bt_mesh_model_elem(model)->addr, ctx->addr, state);
}
static int output_number(bt_mesh_output_action_t action, u32_t number)
{
BT_INFO("OOB Number %u", number);
return 0;
}
static int output_string(const char *str)
{
BT_INFO("OOB String %s", str);
return 0;
}
static void prov_complete(u16_t net_idx, u16_t addr)
{
BT_INFO("provisioning complete for net_idx 0x%04x addr 0x%04x",
net_idx, addr);
primary_addr = addr;
primary_net_idx = net_idx;
}
static void prov_reset(void)
{
bt_mesh_prov_enable(BT_MESH_PROV_ADV | BT_MESH_PROV_GATT);
}
static u8_t dev_uuid[16] = MYNEWT_VAL(BLE_MESH_DEV_UUID);
#define BUTTON_DEBOUNCE_DELAY_MS 250
/*
* Map GPIO pins to button number
* Change to select different GPIO input pins
*/
static uint8_t pin_to_sw(int pin_pos)
{
switch (pin_pos) {
case BUTTON_1: return 0;
case BUTTON_2: return 1;
case BUTTON_3: return 2;
case BUTTON_4: return 3;
default:break;
}
BT_ERR("No match for GPIO pin 0x%08x", pin_pos);
return 0;
}
static void button_pressed(struct os_event *ev)
{
int pin_pos = (int ) ev->ev_arg;
/*
* One button press within a 1 second interval sends an on message
* More than one button press sends an off message
*/
time = k_uptime_get_32();
/* debounce the switch */
if (time < last_time + BUTTON_DEBOUNCE_DELAY_MS) {
last_time = time;
return;
}
if (button_press_cnt == 0) {
os_callout_reset(&sw.button_timer, os_time_ms_to_ticks32(K_SECONDS(1)));
}
BT_INFO("button_press_cnt 0x%02x", button_press_cnt);
button_press_cnt++;
/* The variable pin_pos is the pin position in the GPIO register,
* not the pin number. It's assumed that only one bit is set.
*/
sw.sw_num = pin_to_sw(pin_pos);
last_time = time;
}
/*
* Button Count Timer Worker
*/
static void button_cnt_timer(struct os_event *work)
{
struct sw *button_sw = work->ev_arg;
button_sw->onoff_state = button_press_cnt == 1 ? 1 : 0;
BT_INFO("button_press_cnt 0x%02x onoff_state 0x%02x",
button_press_cnt, button_sw->onoff_state);
button_press_cnt = 0;
os_callout_reset(&sw.button_work, 0);
}
/*
* Button Pressed Worker Task
*/
static void button_pressed_worker(struct os_event *work)
{
struct os_mbuf *msg = NET_BUF_SIMPLE(1);
struct bt_mesh_model *mod_cli, *mod_srv;
struct bt_mesh_model_pub *pub_cli, *pub_srv;
struct sw *sw = work->ev_arg;
u8_t sw_idx = sw->sw_num;
int err;
mod_cli = mod_cli_sw[sw_idx];
pub_cli = mod_cli->pub;
mod_srv = mod_srv_sw[sw_idx];
pub_srv = mod_srv->pub;
(void)pub_srv;
/* If unprovisioned, just call the set function.
* The intent is to have switch-like behavior
* prior to provisioning. Once provisioned,
* the button and its corresponding led are no longer
* associated and act independently. So, if a button is to
* control its associated led after provisioning, the button
* must be configured to either publish to the led's unicast
* address or a group to which the led is subscribed.
*/
if (primary_addr == BT_MESH_ADDR_UNASSIGNED) {
struct bt_mesh_msg_ctx ctx = {
.addr = sw_idx + primary_addr,
};
/* This is a dummy message sufficient
* for the led server
*/
net_buf_simple_add_u8(msg, sw->onoff_state);
gen_onoff_set_unack(mod_srv, &ctx, msg);
goto done;
}
if (pub_cli->addr == BT_MESH_ADDR_UNASSIGNED) {
goto done;
}
BT_INFO("publish to 0x%04x onoff 0x%04x sw_idx 0x%04x",
pub_cli->addr, sw->onoff_state, sw_idx);
bt_mesh_model_msg_init(pub_cli->msg,
BT_MESH_MODEL_OP_GEN_ONOFF_SET);
net_buf_simple_add_u8(pub_cli->msg, sw->onoff_state);
net_buf_simple_add_u8(pub_cli->msg, trans_id++);
err = bt_mesh_model_publish(mod_cli);
if (err) {
BT_ERR("bt_mesh_model_publish err %d", err);
}
done:
os_mbuf_free_chain(msg);
}
/* Disable OOB security for SILabs Android app */
static const struct bt_mesh_prov prov = {
.uuid = dev_uuid,
#if 1
.output_size = 6,
.output_actions = (BT_MESH_DISPLAY_NUMBER | BT_MESH_DISPLAY_STRING),
.output_number = output_number,
.output_string = output_string,
#else
.output_size = 0,
.output_actions = 0,
.output_number = 0,
#endif
.complete = prov_complete,
.reset = prov_reset,
};
void init_led(u8_t dev)
{
hal_gpio_init_out(onoff_state_arr[dev].led_gpio_pin, 1);
}
static struct os_event button_event;
static void
gpio_irq_handler(void *arg)
{
button_event.ev_arg = arg;
os_eventq_put(os_eventq_dflt_get(), &button_event);
}
void init_button(int button)
{
button_event.ev_cb = button_pressed;
hal_gpio_irq_init(button, gpio_irq_handler, (void *)button,
HAL_GPIO_TRIG_FALLING, HAL_GPIO_PULL_UP);
hal_gpio_irq_enable(button);
}
static void
blemesh_on_reset(int reason)
{
BLE_HS_LOG(ERROR, "Resetting state; reason=%d\n", reason);
}
static void
blemesh_on_sync(void)
{
int err;
ble_addr_t addr;
console_printf("Bluetooth initialized\n");
/* Use NRPA */
err = ble_hs_id_gen_rnd(1, &addr);
assert(err == 0);
err = ble_hs_id_set_rnd(addr.val);
assert(err == 0);
err = bt_mesh_init(addr.type, &prov, &comp);
if (err) {
console_printf("Initializing mesh failed (err %d)\n", err);
return;
}
if (IS_ENABLED(CONFIG_SETTINGS)) {
settings_load();
}
if (bt_mesh_is_provisioned()) {
console_printf("Mesh network restored from flash\n");
}
bt_mesh_prov_enable(BT_MESH_PROV_GATT | BT_MESH_PROV_ADV);
console_printf("Mesh initialized\n");
}
int main(void)
{
#ifdef ARCH_sim
mcu_sim_parse_args(argc, argv);
#endif
/* Initialize OS */
sysinit();
BT_INFO("Initializing...");
/* Initialize the button debouncer */
last_time = k_uptime_get_32();
/* Initialize button worker task*/
os_callout_init(&sw.button_work, os_eventq_dflt_get(),
button_pressed_worker, &sw);
/* Initialize button count timer */
os_callout_init(&sw.button_timer, os_eventq_dflt_get(),
button_cnt_timer, &sw);
/* Initialize LED's */
init_led(0);
init_led(1);
init_led(2);
init_led(3);
init_button(BUTTON_1);
init_button(BUTTON_2);
init_button(BUTTON_3);
init_button(BUTTON_4);
init_pub();
/* Initialize the NimBLE host configuration. */
ble_hs_cfg.reset_cb = blemesh_on_reset;
ble_hs_cfg.sync_cb = blemesh_on_sync;
ble_hs_cfg.store_status_cb = ble_store_util_status_rr;
while (1) {
os_eventq_run(os_eventq_dflt_get());
}
return 0;
}

View File

@@ -0,0 +1,51 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you 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.
#
syscfg.vals:
# Set log level to info (disable debug logging).
LOG_LEVEL: 1
# Default task settings
OS_MAIN_STACK_SIZE: 768
# Newtmgr is not supported in this app, so disable newtmgr-over-shell.
SHELL_NEWTMGR: 0
MSYS_1_BLOCK_COUNT: 48
BLE_MESH: 1
BLE_MESH_CFG_CLI: 1
BLE_MESH_DEV_UUID: "((uint8_t[16]){0xdd, 0xdd, 0})"
BLE_MESH_SETTINGS: 0
CONFIG_NFFS: 0
BLE_MESH_ADV_BUF_COUNT: 20
BLE_MESH_TX_SEG_MAX: 6
BLE_MESH_DEBUG: 0
BLE_MESH_DEBUG_NET: 0
BLE_MESH_DEBUG_TRANS: 0
BLE_MESH_DEBUG_BEACON: 0
BLE_MESH_DEBUG_CRYPTO: 0
BLE_MESH_DEBUG_PROV: 0
BLE_MESH_DEBUG_ACCESS: 0
BLE_MESH_DEBUG_MODEL: 0
BLE_MESH_DEBUG_ADV: 0
BLE_MESH_DEBUG_LOW_POWER: 0
BLE_MESH_DEBUG_FRIEND: 0
BLE_MESH_DEBUG_PROXY: 0

View File

@@ -0,0 +1,101 @@
#### Bluetooth: Mesh Generic OnOff, Generic Level, Lighting & Vendor Models
##### Overview
********
This is a application demonstrating a Bluetooth mesh node in
which Root element has following models
- Generic OnOff Server
- Generic OnOff Client
- Generic Level Server
- Generic Level Client
- Generic Default Transition Time Server
- Generic Default Transition Time Client
- Generic Power OnOff Server
- Generic Power OnOff Setup Server
- Generic Power OnOff Client
- Light Lightness Server
- Light Lightness Setup Server
- Light Lightness Client
- Light CTL Server
- Light CTL Setup Server
- Light CTL Client
- Vendor Model
And Secondary element has following models
- Generic Level Server
- Generic Level Client
- Light CTL Temperature Server
Prior to provisioning, an unprovisioned beacon is broadcast that contains
a unique UUID. Each button controls the state of its
corresponding LED and does not initiate any mesh activity
##### Associations of Models with hardware
************************************
For the nRF52840-PDK board, these are the model associations:
* LED1 is associated with generic OnOff Server's state which is part of Root element
* LED2 is associated with Vendor Model which is part of Root element
* LED3 is associated with generic Level (ROOT) / Light Lightness Actual value
* LED4 is associated with generic Level (Secondary) / Light CTL Temperature value
* Button1 and Button2 are associated with gen. OnOff Client or Vendor Model which is part of Root element
* Button3 and Button4 are associated with gen. Level Client / Light Lightness Client / Light CTL Client which is part of Root element
States of Servers are bounded as per Bluetooth SIG Mesh Model Specification v1.0
After provisioning, the button clients must
be configured to publish and the LED servers to subscribe.
If a server is provided with a publish address, it will
also publish its relevant status.
##### Requirements
************
This sample has been tested on the Nordic nRF52840-PDK board, but would
likely also run on the nrf52_pca10040 board.
##### Running
************
Provisioning is done using the BlueZ meshctl utility. In this example, we'll use meshctl commands to bind:
- Button1, Button2, and LED1 to application key 1. It then configures Button1 and Button2
to publish to group 0xC000 and LED1 to subscribe to that group.
- Button3, Button4, and LED3 to application key 1. It then configures Button3 and Button4
to publish to group 0xC000 and LED3 to subscribe to that group.
```
discover-unprovisioned on
provision <discovered UUID>
menu config
target 0100
appkey-add 1
bind 0 1 1000
bind 0 1 1001
bind 0 1 1002
bind 0 1 1003
sub-add 0100 c000 1000
sub-add 0100 c000 1002
pub-set 0100 c000 1 0 5 1001
pub-set 0100 c000 1 0 5 1003
```
The meshctl utility maintains a persistent JSON database containing
the mesh configuration. As additional nodes (boards) are provisioned, it
assigns sequential unicast addresses based on the number of elements
supported by the node. This example supports 2 elements per node.
The meshctl target for configuration must be the root element's unicast
address as it is the only one that has a configuration server model. If
meshctl is gracefully exited, it can be restarted and reconnected to
network 0x0.
The meshctl utility also supports a onoff model client that can be used to
change the state of any LED that is bound to application key 0x1.
This is done by setting the target to the unicast address of the element
that has that LED's model and issuing the onoff command.
Group addresses are not supported.

View File

@@ -0,0 +1,40 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you 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.
#
pkg.name: apps/blemesh_models_example_2
pkg.type: app
pkg.description: Sample application for BLE Mesh node with on/off, level, light and vendor models on nRF52840pdk
pkg.author: "Michał Narajowski <michal.narajowski@codecoup.pl>"
pkg.homepage: "http://mynewt.apache.org/"
pkg.keywords:
pkg.deps:
- "@apache-mynewt-core/kernel/os"
- "@apache-mynewt-core/sys/console/full"
- "@apache-mynewt-core/sys/log/full"
- "@apache-mynewt-core/sys/stats/full"
- "@apache-mynewt-core/encoding/base64"
- "@apache-mynewt-core/sys/config"
- nimble/controller
- nimble/host
- nimble/host/services/gap
- nimble/host/services/gatt
- nimble/transport/ram
pkg.lflags:
- -DFLOAT_SUPPORT
- -lm

View File

@@ -0,0 +1,95 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
/* Bluetooth: Mesh Generic OnOff, Generic Level, Lighting & Vendor Models
*
* Copyright (c) 2018 Vikrant More
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "bsp/bsp.h"
#include "console/console.h"
#include "hal/hal_gpio.h"
#include "mesh/mesh.h"
#include "app_gpio.h"
#include "publisher.h"
int button_device[] = {
BUTTON_1,
BUTTON_2,
BUTTON_3,
BUTTON_4,
};
int led_device[] = {
LED_1,
LED_2,
LED_3,
LED_4,
};
static struct os_callout button_work;
static void button_pressed(struct os_event *ev)
{
os_callout_reset(&button_work, 0);
}
static struct os_event button_event;
static void gpio_irq_handler(void *arg)
{
button_event.ev_arg = arg;
os_eventq_put(os_eventq_dflt_get(), &button_event);
}
void app_gpio_init(void)
{
/* LEDs configiuratin & setting */
hal_gpio_init_out(led_device[0], 1);
hal_gpio_init_out(led_device[1], 1);
hal_gpio_init_out(led_device[2], 1);
hal_gpio_init_out(led_device[3], 1);
/* Buttons configiuratin & setting */
os_callout_init(&button_work, os_eventq_dflt_get(), publish, NULL);
button_event.ev_cb = button_pressed;
hal_gpio_irq_init(button_device[0], gpio_irq_handler, NULL,
HAL_GPIO_TRIG_FALLING, HAL_GPIO_PULL_UP);
hal_gpio_irq_enable(button_device[0]);
hal_gpio_irq_init(button_device[1], gpio_irq_handler, NULL,
HAL_GPIO_TRIG_FALLING, HAL_GPIO_PULL_UP);
hal_gpio_irq_enable(button_device[1]);
hal_gpio_irq_init(button_device[2], gpio_irq_handler, NULL,
HAL_GPIO_TRIG_FALLING, HAL_GPIO_PULL_UP);
hal_gpio_irq_enable(button_device[2]);
hal_gpio_irq_init(button_device[3], gpio_irq_handler, NULL,
HAL_GPIO_TRIG_FALLING, HAL_GPIO_PULL_UP);
hal_gpio_irq_enable(button_device[3]);
}

View File

@@ -0,0 +1,36 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
/* Bluetooth: Mesh Generic OnOff, Generic Level, Lighting & Vendor Models
*
* Copyright (c) 2018 Vikrant More
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef _APP_GPIO_H
#define _APP_GPIO_H
/* GPIO */
extern int button_device[];
extern int led_device[];
void app_gpio_init(void);
#endif

View File

@@ -0,0 +1,116 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
/* Bluetooth: Mesh Generic OnOff, Generic Level, Lighting & Vendor Models
*
* Copyright (c) 2018 Vikrant More
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "console/console.h"
#include "common.h"
#include "ble_mesh.h"
#include "device_composition.h"
#define OOB_AUTH_ENABLE 1
#ifdef OOB_AUTH_ENABLE
static int output_number(bt_mesh_output_action_t action, u32_t number)
{
printk("OOB Number: %lu\n", number);
return 0;
}
static int output_string(const char *str)
{
printk("OOB String: %s\n", str);
return 0;
}
#endif
static void prov_complete(u16_t net_idx, u16_t addr)
{
printk("Local node provisioned, primary address 0x%04x\n", addr);
}
static void prov_reset(void)
{
bt_mesh_prov_enable(BT_MESH_PROV_ADV | BT_MESH_PROV_GATT);
}
static u8_t dev_uuid[16] = MYNEWT_VAL(BLE_MESH_DEV_UUID);
static const struct bt_mesh_prov prov = {
.uuid = dev_uuid,
#ifdef OOB_AUTH_ENABLE
.output_size = 6,
.output_actions = BT_MESH_DISPLAY_NUMBER | BT_MESH_DISPLAY_STRING,
.output_number = output_number,
.output_string = output_string,
#endif
.complete = prov_complete,
.reset = prov_reset,
};
void blemesh_on_reset(int reason)
{
BLE_HS_LOG(ERROR, "Resetting state; reason=%d\n", reason);
}
void blemesh_on_sync(void)
{
int err;
ble_addr_t addr;
console_printf("Bluetooth initialized\n");
/* Use NRPA */
err = ble_hs_id_gen_rnd(1, &addr);
assert(err == 0);
err = ble_hs_id_set_rnd(addr.val);
assert(err == 0);
err = bt_mesh_init(addr.type, &prov, &comp);
if (err) {
console_printf("Initializing mesh failed (err %d)\n", err);
return;
}
if (IS_ENABLED(CONFIG_SETTINGS)) {
settings_load();
}
if (bt_mesh_is_provisioned()) {
console_printf("Mesh network restored from flash\n");
}
bt_mesh_prov_enable(BT_MESH_PROV_GATT | BT_MESH_PROV_ADV);
console_printf("Mesh initialized\n");
bt_initialized();
}

View File

@@ -0,0 +1,73 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
/* Bluetooth: Mesh Generic OnOff, Generic Level, Lighting & Vendor Models
*
* Copyright (c) 2018 Vikrant More
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef _BLE_MESH_H
#define _BLE_MESH_H
#include "mesh/mesh.h"
#include "mesh/glue.h"
/* Model Operation Codes */
#define BT_MESH_MODEL_OP_GEN_ONOFF_GET BT_MESH_MODEL_OP_2(0x82, 0x01)
#define BT_MESH_MODEL_OP_GEN_ONOFF_SET BT_MESH_MODEL_OP_2(0x82, 0x02)
#define BT_MESH_MODEL_OP_GEN_ONOFF_SET_UNACK BT_MESH_MODEL_OP_2(0x82, 0x03)
#define BT_MESH_MODEL_OP_GEN_ONOFF_STATUS BT_MESH_MODEL_OP_2(0x82, 0x04)
#define BT_MESH_MODEL_OP_GEN_LEVEL_GET BT_MESH_MODEL_OP_2(0x82, 0x05)
#define BT_MESH_MODEL_OP_GEN_LEVEL_SET BT_MESH_MODEL_OP_2(0x82, 0x06)
#define BT_MESH_MODEL_OP_GEN_LEVEL_SET_UNACK BT_MESH_MODEL_OP_2(0x82, 0x07)
#define BT_MESH_MODEL_OP_GEN_LEVEL_STATUS BT_MESH_MODEL_OP_2(0x82, 0x08)
#define BT_MESH_MODEL_OP_GEN_DELTA_SET BT_MESH_MODEL_OP_2(0x82, 0x09)
#define BT_MESH_MODEL_OP_GEN_DELTA_SET_UNACK BT_MESH_MODEL_OP_2(0x82, 0x0A)
#define BT_MESH_MODEL_OP_GEN_MOVE_SET BT_MESH_MODEL_OP_2(0x82, 0x0B)
#define BT_MESH_MODEL_OP_GEN_MOVE_SET_UNACK BT_MESH_MODEL_OP_2(0x82, 0x0C)
#define BT_MESH_MODEL_GEN_DEF_TRANS_TIME_STATUS BT_MESH_MODEL_OP_2(0x82, 0x10)
#define BT_MESH_MODEL_GEN_ONPOWERUP_STATUS BT_MESH_MODEL_OP_2(0x82, 0x12)
#define BT_MESH_MODEL_LIGHT_LIGHTNESS_STATUS BT_MESH_MODEL_OP_2(0x82, 0x4E)
#define BT_MESH_MODEL_LIGHT_LIGHTNESS_LINEAR_STATUS \
BT_MESH_MODEL_OP_2(0x82, 0x52)
#define BT_MESH_MODEL_LIGHT_LIGHTNESS_LAST_STATUS \
BT_MESH_MODEL_OP_2(0x82, 0x54)
#define BT_MESH_MODEL_LIGHT_LIGHTNESS_DEFAULT_STATUS \
BT_MESH_MODEL_OP_2(0x82, 0x56)
#define BT_MESH_MODEL_LIGHT_LIGHTNESS_RANGE_STATUS \
BT_MESH_MODEL_OP_2(0x82, 0x58)
#define BT_MESH_MODEL_LIGHT_CTL_STATUS BT_MESH_MODEL_OP_2(0x82, 0x60)
#define BT_MESH_MODEL_LIGHT_CTL_TEMP_RANGE_STATUS \
BT_MESH_MODEL_OP_2(0x82, 0x63)
#define BT_MESH_MODEL_LIGHT_CTL_TEMP_STATUS BT_MESH_MODEL_OP_2(0x82, 0x66)
#define BT_MESH_MODEL_LIGHT_CTL_DEFAULT_STATUS BT_MESH_MODEL_OP_2(0x82, 0x68)
void blemesh_on_reset(int reason);
void blemesh_on_sync(void);
void init_pub(void);
#endif

View File

@@ -0,0 +1,33 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
/* Bluetooth: Mesh Generic OnOff, Generic Level, Lighting & Vendor Models
*
* Copyright (c) 2018 Vikrant More
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef _COMMON_H
#define _COMMON_H
void update_light_state(void);
void bt_initialized(void);
#endif

View File

@@ -0,0 +1,177 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
/* Bluetooth: Mesh Generic OnOff, Generic Level, Lighting & Vendor Models
*
* Copyright (c) 2018 Vikrant More
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef _DEVICE_COMPOSITION_H
#define _DEVICE_COMPOSITION_H
#define CID_RUNTIME 0x05C3
#define STATE_OFF 0x00
#define STATE_ON 0x01
#define STATE_DEFAULT 0x01
#define STATE_RESTORE 0x02
/* Following 4 values are as per Mesh Model specification */
#define LIGHTNESS_MIN 0x0001
#define LIGHTNESS_MAX 0xFFFF
#define TEMP_MIN 0x0320
#define TEMP_MAX 0x4E20
/* Refer 7.2 of Mesh Model Specification */
#define RANGE_SUCCESSFULLY_UPDATED 0x00
#define CANNOT_SET_RANGE_MIN 0x01
#define CANNOT_SET_RANGE_MAX 0x02
struct generic_onoff_state {
u8_t onoff;
u8_t target_onoff;
u8_t last_tid;
u16_t last_src_addr;
u16_t last_dst_addr;
s64_t last_msg_timestamp;
s32_t tt_delta;
struct transition *transition;
};
struct generic_level_state {
s16_t level;
s16_t target_level;
s16_t last_level;
s32_t last_delta;
u8_t last_tid;
u16_t last_src_addr;
u16_t last_dst_addr;
s64_t last_msg_timestamp;
s32_t tt_delta;
struct transition *transition;
};
struct generic_onpowerup_state {
u8_t onpowerup;
};
struct gen_def_trans_time_state {
u8_t tt;
};
struct vendor_state {
int current;
u32_t response;
u8_t last_tid;
u16_t last_src_addr;
u16_t last_dst_addr;
s64_t last_msg_timestamp;
};
struct light_lightness_state {
u16_t linear;
u16_t target_linear;
u16_t actual;
u16_t target_actual;
u16_t last;
u16_t def;
u8_t status_code;
u16_t light_range_min;
u16_t light_range_max;
u32_t lightness_range;
u8_t last_tid;
u16_t last_src_addr;
u16_t last_dst_addr;
s64_t last_msg_timestamp;
s32_t tt_delta_actual;
s32_t tt_delta_linear;
struct transition *transition;
};
struct light_ctl_state {
u16_t lightness;
u16_t target_lightness;
u16_t temp;
u16_t target_temp;
s16_t delta_uv;
s16_t target_delta_uv;
u8_t status_code;
u16_t temp_range_min;
u16_t temp_range_max;
u32_t temperature_range;
u16_t lightness_def;
u16_t temp_def;
u32_t lightness_temp_def;
s16_t delta_uv_def;
u32_t lightness_temp_last;
u8_t last_tid;
u16_t last_src_addr;
u16_t last_dst_addr;
s64_t last_msg_timestamp;
s32_t tt_delta_lightness;
s32_t tt_delta_temp;
s32_t tt_delta_duv;
struct transition *transition;
};
extern struct generic_onoff_state gen_onoff_srv_root_user_data;
extern struct generic_level_state gen_level_srv_root_user_data;
extern struct gen_def_trans_time_state gen_def_trans_time_srv_user_data;
extern struct generic_onpowerup_state gen_power_onoff_srv_user_data;
extern struct light_lightness_state light_lightness_srv_user_data;
extern struct light_ctl_state light_ctl_srv_user_data;
extern struct generic_level_state gen_level_srv_s0_user_data;
extern struct bt_mesh_model root_models[];
extern struct bt_mesh_model vnd_models[];
extern struct bt_mesh_model s0_models[];
extern const struct bt_mesh_comp comp;
void gen_onoff_publish(struct bt_mesh_model *model);
void gen_level_publish(struct bt_mesh_model *model);
void light_lightness_publish(struct bt_mesh_model *model);
void light_lightness_linear_publish(struct bt_mesh_model *model);
void light_ctl_publish(struct bt_mesh_model *model);
void light_ctl_temp_publish(struct bt_mesh_model *model);
#endif

View File

@@ -0,0 +1,250 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
/* Bluetooth: Mesh Generic OnOff, Generic Level, Lighting & Vendor Models
*
* Copyright (c) 2018 Vikrant More
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "console/console.h"
#include "hal/hal_gpio.h"
#include "mesh/mesh.h"
#include "app_gpio.h"
#include "storage.h"
#include "ble_mesh.h"
#include "device_composition.h"
#include "no_transition_work_handler.h"
#include "publisher.h"
#include "state_binding.h"
#include "transition.h"
static bool reset;
static void light_default_var_init(void)
{
gen_def_trans_time_srv_user_data.tt = 0x00;
gen_power_onoff_srv_user_data.onpowerup = STATE_DEFAULT;
light_lightness_srv_user_data.light_range_min = LIGHTNESS_MIN;
light_lightness_srv_user_data.light_range_max = LIGHTNESS_MAX;
light_lightness_srv_user_data.last = LIGHTNESS_MAX;
light_lightness_srv_user_data.def = LIGHTNESS_MAX;
/* Following 2 values are as per specification */
light_ctl_srv_user_data.temp_range_min = TEMP_MIN;
light_ctl_srv_user_data.temp_range_max = TEMP_MAX;
light_ctl_srv_user_data.temp_def = TEMP_MIN;
light_ctl_srv_user_data.lightness_temp_last =
(u32_t) ((LIGHTNESS_MAX << 16) | TEMP_MIN);
}
static void light_default_status_init(void)
{
u16_t lightness;
lightness = (u16_t) (light_ctl_srv_user_data.lightness_temp_last >> 16);
if (lightness) {
gen_onoff_srv_root_user_data.onoff = STATE_ON;
} else {
gen_onoff_srv_root_user_data.onoff = STATE_OFF;
}
/* Retrieve Default Lightness & Temperature Values */
if (light_ctl_srv_user_data.lightness_temp_def) {
light_ctl_srv_user_data.lightness_def = (u16_t)
(light_ctl_srv_user_data.lightness_temp_def >> 16);
light_ctl_srv_user_data.temp_def = (u16_t)
(light_ctl_srv_user_data.lightness_temp_def);
}
light_lightness_srv_user_data.def =
light_ctl_srv_user_data.lightness_def;
light_ctl_srv_user_data.temp = light_ctl_srv_user_data.temp_def;
/* Retrieve Range of Lightness & Temperature */
if (light_lightness_srv_user_data.lightness_range) {
light_lightness_srv_user_data.light_range_max = (u16_t)
(light_lightness_srv_user_data.lightness_range >> 16);
light_lightness_srv_user_data.light_range_min = (u16_t)
(light_lightness_srv_user_data.lightness_range);
}
if (light_ctl_srv_user_data.temperature_range) {
light_ctl_srv_user_data.temp_range_max = (u16_t)
(light_ctl_srv_user_data.temperature_range >> 16);
light_ctl_srv_user_data.temp_range_min = (u16_t)
(light_ctl_srv_user_data.temperature_range);
}
switch (gen_power_onoff_srv_user_data.onpowerup) {
case STATE_OFF:
gen_onoff_srv_root_user_data.onoff = STATE_OFF;
state_binding(ONOFF, ONOFF_TEMP);
break;
case STATE_DEFAULT:
gen_onoff_srv_root_user_data.onoff = STATE_ON;
state_binding(ONOFF, ONOFF_TEMP);
break;
case STATE_RESTORE:
light_lightness_srv_user_data.last = (u16_t)
(light_ctl_srv_user_data.lightness_temp_last >> 16);
light_ctl_srv_user_data.temp =
(u16_t) (light_ctl_srv_user_data.lightness_temp_last);
state_binding(ONPOWERUP, ONOFF_TEMP);
break;
}
default_tt = gen_def_trans_time_srv_user_data.tt;
}
void update_light_state(void)
{
u8_t power, color;
power = 100 * ((float) lightness / 65535);
color = 100 * ((float) (temperature + 32768) / 65535);
printk("power-> %d, color-> %d\n", power, color);
if (lightness) {
/* LED1 On */
hal_gpio_write(led_device[0], 0);
} else {
/* LED1 Off */
hal_gpio_write(led_device[0], 1);
}
if (power < 50) {
/* LED3 On */
hal_gpio_write(led_device[2], 0);
} else {
/* LED3 Off */
hal_gpio_write(led_device[2], 1);
}
if (color < 50) {
/* LED4 On */
hal_gpio_write(led_device[3], 0);
} else {
/* LED4 Off */
hal_gpio_write(led_device[3], 1);
}
if (*ptr_counter == 0 || reset == false) {
reset = true;
os_callout_reset(&no_transition_work, 0);
}
}
static void short_time_multireset_bt_mesh_unprovisioning(void)
{
if (reset_counter >= 4) {
reset_counter = 0;
printk("BT Mesh reset\n");
bt_mesh_reset();
} else {
printk("Reset Counter -> %d\n", reset_counter);
reset_counter++;
}
save_on_flash(RESET_COUNTER);
}
static void reset_counter_timer_handler(struct os_event *dummy)
{
reset_counter = 0;
save_on_flash(RESET_COUNTER);
printk("Reset Counter set to Zero\n");
}
struct os_callout reset_counter_timer;
static void init_timers(void)
{
os_callout_init(&reset_counter_timer, os_eventq_dflt_get(),
reset_counter_timer_handler, NULL);
os_callout_reset(&reset_counter_timer,
os_time_ms_to_ticks32(K_MSEC(7000)));
no_transition_work_init();
}
void bt_initialized(void)
{
light_default_status_init();
update_light_state();
randomize_publishers_TID();
short_time_multireset_bt_mesh_unprovisioning();
}
int main(void)
{
#ifdef ARCH_sim
mcu_sim_parse_args(argc, argv);
#endif
/* Initialize OS */
sysinit();
light_default_var_init();
app_gpio_init();
init_timers();
transition_timers_init();
init_pub();
ps_settings_init();
printk("Initializing...\n");
/* Initialize the NimBLE host configuration. */
ble_hs_cfg.reset_cb = blemesh_on_reset;
ble_hs_cfg.sync_cb = blemesh_on_sync;
ble_hs_cfg.store_status_cb = ble_store_util_status_rr;
while (1) {
os_eventq_run(os_eventq_dflt_get());
}
return 0;
}

View File

@@ -0,0 +1,89 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
/* Bluetooth: Mesh Generic OnOff, Generic Level, Lighting & Vendor Models
*
* Copyright (c) 2018 Vikrant More
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "ble_mesh.h"
#include "device_composition.h"
#include "storage.h"
static void unsolicitedly_publish_states_work_handler(struct os_event *work)
{
gen_onoff_publish(&root_models[2]);
gen_level_publish(&root_models[4]);
light_lightness_publish(&root_models[11]);
light_lightness_linear_publish(&root_models[11]);
light_ctl_publish(&root_models[14]);
gen_level_publish(&s0_models[0]);
light_ctl_temp_publish(&s0_models[2]);
}
struct os_callout unsolicitedly_publish_states_work;
static void unsolicitedly_publish_states_timer_handler(struct os_event *dummy)
{
os_callout_reset(&unsolicitedly_publish_states_work, 0);
}
struct os_callout unsolicitedly_publish_states_timer;
static void save_lightness_temp_last_values_timer_handler(struct os_event *dummy)
{
save_on_flash(LIGHTNESS_TEMP_LAST_STATE);
}
struct os_callout save_lightness_temp_last_values_timer;
static void no_transition_work_handler(struct os_event *work)
{
os_callout_reset(&unsolicitedly_publish_states_timer,
os_time_ms_to_ticks32(K_MSEC(5000)));
/* If Lightness & Temperature values remains stable for
* 10 Seconds then & then only get stored on SoC flash.
*/
if (gen_power_onoff_srv_user_data.onpowerup == STATE_RESTORE) {
os_callout_reset(&save_lightness_temp_last_values_timer,
os_time_ms_to_ticks32(
K_MSEC(10000)));
}
}
struct os_callout no_transition_work;
void no_transition_work_init(void)
{
os_callout_init(&no_transition_work, os_eventq_dflt_get(),
no_transition_work_handler, NULL);
os_callout_init(&save_lightness_temp_last_values_timer,
os_eventq_dflt_get(),
save_lightness_temp_last_values_timer_handler,
NULL);
os_callout_init(&unsolicitedly_publish_states_work, os_eventq_dflt_get(),
unsolicitedly_publish_states_work_handler, NULL);
os_callout_init(&unsolicitedly_publish_states_timer, os_eventq_dflt_get(),
unsolicitedly_publish_states_timer_handler, NULL);
}

View File

@@ -0,0 +1,34 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
/* Bluetooth: Mesh Generic OnOff, Generic Level, Lighting & Vendor Models
*
* Copyright (c) 2018 Vikrant More
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef _NO_TRANSITION_WORK_HANDLER_H
#define _NO_TRANSITION_WORK_HANDLER_H
extern struct os_callout no_transition_work;
void no_transition_work_init(void);
#endif

View File

@@ -0,0 +1,266 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
/* Bluetooth: Mesh Generic OnOff, Generic Level, Lighting & Vendor Models
*
* Copyright (c) 2018 Vikrant More
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "console/console.h"
#include "hal/hal_gpio.h"
#include "app_gpio.h"
#include "ble_mesh.h"
#include "device_composition.h"
#include "publisher.h"
#define ONOFF
#define GENERIC_LEVEL
/* #define LIGHT_CTL */
/* #define LIGHT_CTL_TEMP */
static bool is_randomization_of_TIDs_done;
#if (defined(ONOFF) || defined(ONOFF_TT))
static u8_t tid_onoff;
#elif defined(VND_MODEL_TEST)
static u8_t tid_vnd;
#endif
static u8_t tid_level;
void randomize_publishers_TID(void)
{
#if (defined(ONOFF) || defined(ONOFF_TT))
bt_rand(&tid_onoff, sizeof(tid_onoff));
#elif defined(VND_MODEL_TEST)
bt_rand(&tid_vnd, sizeof(tid_vnd));
#endif
bt_rand(&tid_level, sizeof(tid_level));
is_randomization_of_TIDs_done = true;
}
static u32_t button_read(int button)
{
return (uint32_t) hal_gpio_read(button);
}
void publish(struct os_event *work)
{
int err = 0;
if (is_randomization_of_TIDs_done == false) {
return;
}
if (button_read(button_device[0]) == 0) {
#if defined(ONOFF)
bt_mesh_model_msg_init(root_models[3].pub->msg,
BT_MESH_MODEL_OP_GEN_ONOFF_SET_UNACK);
net_buf_simple_add_u8(root_models[3].pub->msg, 0x01);
net_buf_simple_add_u8(root_models[3].pub->msg, tid_onoff++);
err = bt_mesh_model_publish(&root_models[3]);
#elif defined(ONOFF_TT)
bt_mesh_model_msg_init(root_models[3].pub->msg,
BT_MESH_MODEL_OP_GEN_ONOFF_SET_UNACK);
net_buf_simple_add_u8(root_models[3].pub->msg, 0x01);
net_buf_simple_add_u8(root_models[3].pub->msg, tid_onoff++);
net_buf_simple_add_u8(root_models[3].pub->msg, 0x45);
net_buf_simple_add_u8(root_models[3].pub->msg, 0x28);
err = bt_mesh_model_publish(&root_models[3]);
#elif defined(VND_MODEL_TEST)
bt_mesh_model_msg_init(vnd_models[0].pub->msg,
BT_MESH_MODEL_OP_3(0x03, CID_RUNTIME));
net_buf_simple_add_le16(vnd_models[0].pub->msg, 0x0001);
net_buf_simple_add_u8(vnd_models[0].pub->msg, tid_vnd++);
err = bt_mesh_model_publish(&vnd_models[0]);
#endif
} else if (button_read(button_device[1]) == 0) {
#if defined(ONOFF)
bt_mesh_model_msg_init(root_models[3].pub->msg,
BT_MESH_MODEL_OP_GEN_ONOFF_SET_UNACK);
net_buf_simple_add_u8(root_models[3].pub->msg, 0x00);
net_buf_simple_add_u8(root_models[3].pub->msg, tid_onoff++);
err = bt_mesh_model_publish(&root_models[3]);
#elif defined(ONOFF_TT)
bt_mesh_model_msg_init(root_models[3].pub->msg,
BT_MESH_MODEL_OP_GEN_ONOFF_SET_UNACK);
net_buf_simple_add_u8(root_models[3].pub->msg, 0x00);
net_buf_simple_add_u8(root_models[3].pub->msg, tid_onoff++);
net_buf_simple_add_u8(root_models[3].pub->msg, 0x45);
net_buf_simple_add_u8(root_models[3].pub->msg, 0x28);
err = bt_mesh_model_publish(&root_models[3]);
#elif defined(VND_MODEL_TEST)
bt_mesh_model_msg_init(vnd_models[0].pub->msg,
BT_MESH_MODEL_OP_3(0x03, CID_RUNTIME));
net_buf_simple_add_le16(vnd_models[0].pub->msg, 0x0000);
net_buf_simple_add_u8(vnd_models[0].pub->msg, tid_vnd++);
err = bt_mesh_model_publish(&vnd_models[0]);
#endif
} else if (button_read(button_device[2]) == 0) {
#if defined(GENERIC_LEVEL)
bt_mesh_model_msg_init(root_models[5].pub->msg,
BT_MESH_MODEL_OP_GEN_LEVEL_SET_UNACK);
net_buf_simple_add_le16(root_models[5].pub->msg, LEVEL_S25);
net_buf_simple_add_u8(root_models[5].pub->msg, tid_level++);
err = bt_mesh_model_publish(&root_models[5]);
#elif defined(ONOFF_GET)
bt_mesh_model_msg_init(root_models[3].pub->msg,
BT_MESH_MODEL_OP_GEN_ONOFF_GET);
err = bt_mesh_model_publish(&root_models[3]);
#elif defined(GENERIC_DELTA_LEVEL)
bt_mesh_model_msg_init(root_models[5].pub->msg,
BT_MESH_MODEL_OP_GEN_DELTA_SET_UNACK);
net_buf_simple_add_le32(root_models[5].pub->msg, 100);
net_buf_simple_add_u8(root_models[5].pub->msg, tid_level++);
err = bt_mesh_model_publish(&root_models[5]);
#elif defined(GENERIC_MOVE_LEVEL_TT)
bt_mesh_model_msg_init(root_models[5].pub->msg,
BT_MESH_MODEL_OP_GEN_MOVE_SET_UNACK);
net_buf_simple_add_le16(root_models[5].pub->msg, 13100);
net_buf_simple_add_u8(root_models[5].pub->msg, tid_level++);
net_buf_simple_add_u8(root_models[5].pub->msg, 0x45);
net_buf_simple_add_u8(root_models[5].pub->msg, 0x00);
err = bt_mesh_model_publish(&root_models[5]);
#elif defined(LIGHT_LIGHTNESS_TT)
bt_mesh_model_msg_init(root_models[13].pub->msg,
BT_MESH_MODEL_OP_2(0x82, 0x4D));
net_buf_simple_add_le16(root_models[13].pub->msg, LEVEL_U25);
net_buf_simple_add_u8(root_models[13].pub->msg, tid_level++);
net_buf_simple_add_u8(root_models[13].pub->msg, 0x45);
net_buf_simple_add_u8(root_models[13].pub->msg, 0x28);
err = bt_mesh_model_publish(&root_models[13]);
#elif defined(LIGHT_CTL)
bt_mesh_model_msg_init(root_models[16].pub->msg,
BT_MESH_MODEL_OP_2(0x82, 0x5F));
/* Lightness */
net_buf_simple_add_le16(root_models[16].pub->msg, LEVEL_U25);
/* Temperature (value should be from 0x0320 to 0x4E20 */
/* This is as per 6.1.3.1 in Mesh Model Specification */
net_buf_simple_add_le16(root_models[16].pub->msg, 0x0320);
/* Delta UV */
net_buf_simple_add_le16(root_models[16].pub->msg, 0x0000);
net_buf_simple_add_u8(root_models[16].pub->msg, tid_level++);
err = bt_mesh_model_publish(&root_models[16]);
#elif defined(LIGHT_CTL_TT)
bt_mesh_model_msg_init(root_models[16].pub->msg,
BT_MESH_MODEL_OP_2(0x82, 0x5F));
/* Lightness */
net_buf_simple_add_le16(root_models[16].pub->msg, LEVEL_U25);
/* Temperature (value should be from 0x0320 to 0x4E20 */
/* This is as per 6.1.3.1 in Mesh Model Specification */
net_buf_simple_add_le16(root_models[16].pub->msg, 0x0320);
/* Delta UV */
net_buf_simple_add_le16(root_models[16].pub->msg, 0x0000);
net_buf_simple_add_u8(root_models[16].pub->msg, tid_level++);
net_buf_simple_add_u8(root_models[16].pub->msg, 0x45);
net_buf_simple_add_u8(root_models[16].pub->msg, 0x00);
err = bt_mesh_model_publish(&root_models[16]);
#elif defined(LIGHT_CTL_TEMP)
bt_mesh_model_msg_init(root_models[16].pub->msg,
BT_MESH_MODEL_OP_2(0x82, 0x65));
/* Temperature (value should be from 0x0320 to 0x4E20 */
/* This is as per 6.1.3.1 in Mesh Model Specification */
net_buf_simple_add_le16(root_models[16].pub->msg, 0x0320);
/* Delta UV */
net_buf_simple_add_le16(root_models[16].pub->msg, 0x0000);
net_buf_simple_add_u8(root_models[16].pub->msg, tid_level++);
err = bt_mesh_model_publish(&root_models[16]);
#endif
} else if (button_read(button_device[3]) == 0) {
#if defined(GENERIC_LEVEL)
bt_mesh_model_msg_init(root_models[5].pub->msg,
BT_MESH_MODEL_OP_GEN_LEVEL_SET_UNACK);
net_buf_simple_add_le16(root_models[5].pub->msg, LEVEL_S100);
net_buf_simple_add_u8(root_models[5].pub->msg, tid_level++);
err = bt_mesh_model_publish(&root_models[5]);
#elif defined(GENERIC_DELTA_LEVEL)
bt_mesh_model_msg_init(root_models[5].pub->msg,
BT_MESH_MODEL_OP_GEN_DELTA_SET_UNACK);
net_buf_simple_add_le32(root_models[5].pub->msg, -100);
net_buf_simple_add_u8(root_models[5].pub->msg, tid_level++);
err = bt_mesh_model_publish(&root_models[5]);
#elif defined(GENERIC_MOVE_LEVEL_TT)
bt_mesh_model_msg_init(root_models[5].pub->msg,
BT_MESH_MODEL_OP_GEN_MOVE_SET_UNACK);
net_buf_simple_add_le16(root_models[5].pub->msg, -13100);
net_buf_simple_add_u8(root_models[5].pub->msg, tid_level++);
net_buf_simple_add_u8(root_models[5].pub->msg, 0x45);
net_buf_simple_add_u8(root_models[5].pub->msg, 0x00);
err = bt_mesh_model_publish(&root_models[5]);
#elif defined(LIGHT_LIGHTNESS_TT)
bt_mesh_model_msg_init(root_models[13].pub->msg,
BT_MESH_MODEL_OP_2(0x82, 0x4D));
net_buf_simple_add_le16(root_models[13].pub->msg, LEVEL_U100);
net_buf_simple_add_u8(root_models[13].pub->msg, tid_level++);
net_buf_simple_add_u8(root_models[13].pub->msg, 0x45);
net_buf_simple_add_u8(root_models[13].pub->msg, 0x28);
err = bt_mesh_model_publish(&root_models[13]);
#elif defined(LIGHT_CTL)
bt_mesh_model_msg_init(root_models[16].pub->msg,
BT_MESH_MODEL_OP_2(0x82, 0x5F));
/* Lightness */
net_buf_simple_add_le16(root_models[16].pub->msg, LEVEL_U100);
/* Temperature (value should be from 0x0320 to 0x4E20 */
/* This is as per 6.1.3.1 in Mesh Model Specification */
net_buf_simple_add_le16(root_models[16].pub->msg, 0x4E20);
/* Delta UV */
net_buf_simple_add_le16(root_models[16].pub->msg, 0x0000);
net_buf_simple_add_u8(root_models[16].pub->msg, tid_level++);
err = bt_mesh_model_publish(&root_models[16]);
#elif defined(LIGHT_CTL_TT)
bt_mesh_model_msg_init(root_models[16].pub->msg,
BT_MESH_MODEL_OP_2(0x82, 0x5F));
/* Lightness */
net_buf_simple_add_le16(root_models[16].pub->msg, LEVEL_U100);
/* Temperature (value should be from 0x0320 to 0x4E20 */
/* This is as per 6.1.3.1 in Mesh Model Specification */
net_buf_simple_add_le16(root_models[16].pub->msg, 0x4E20);
/* Delta UV */
net_buf_simple_add_le16(root_models[16].pub->msg, 0x0000);
net_buf_simple_add_u8(root_models[16].pub->msg, tid_level++);
net_buf_simple_add_u8(root_models[16].pub->msg, 0x45);
net_buf_simple_add_u8(root_models[16].pub->msg, 0x00);
err = bt_mesh_model_publish(&root_models[16]);
#elif defined(LIGHT_CTL_TEMP)
bt_mesh_model_msg_init(root_models[16].pub->msg,
BT_MESH_MODEL_OP_2(0x82, 0x65));
/* Temperature (value should be from 0x0320 to 0x4E20 */
/* This is as per 6.1.3.1 in Mesh Model Specification */
net_buf_simple_add_le16(root_models[16].pub->msg, 0x4E20);
/* Delta UV */
net_buf_simple_add_le16(root_models[16].pub->msg, 0x0000);
net_buf_simple_add_u8(root_models[16].pub->msg, tid_level++);
err = bt_mesh_model_publish(&root_models[16]);
#endif
}
if (err) {
printk("bt_mesh_model_publish: err: %d\n", err);
}
}

View File

@@ -0,0 +1,46 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
/* Bluetooth: Mesh Generic OnOff, Generic Level, Lighting & Vendor Models
*
* Copyright (c) 2018 Vikrant More
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef _PUBLISHER_H
#define _PUBLISHER_H
/* Others */
#define LEVEL_S0 -32768
#define LEVEL_S25 -16384
#define LEVEL_S50 0
#define LEVEL_S75 16384
#define LEVEL_S100 32767
#define LEVEL_U0 0
#define LEVEL_U25 16384
#define LEVEL_U50 32768
#define LEVEL_U75 49152
#define LEVEL_U100 65535
void randomize_publishers_TID(void);
void publish(struct os_event *work);
#endif

View File

@@ -0,0 +1,308 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
/* Bluetooth: Mesh Generic OnOff, Generic Level, Lighting & Vendor Models
*
* Copyright (c) 2018 Vikrant More
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <math.h>
#include "ble_mesh.h"
#include "device_composition.h"
#include "state_binding.h"
#include "transition.h"
u16_t lightness, target_lightness;
s16_t temperature, target_temperature;
static s32_t ceiling(float num)
{
s32_t inum;
inum = (s32_t) num;
if (num == (float) inum) {
return inum;
}
return inum + 1;
}
u16_t actual_to_linear(u16_t val)
{
float tmp;
tmp = ((float) val / 65535);
return (u16_t) ceiling(65535 * tmp * tmp);
}
u16_t linear_to_actual(u16_t val)
{
return (u16_t) (65535 * sqrt(((float) val / 65535)));
}
static void constrain_lightness(u16_t var)
{
if (var > 0 && var < light_lightness_srv_user_data.light_range_min) {
var = light_lightness_srv_user_data.light_range_min;
} else if (var > light_lightness_srv_user_data.light_range_max) {
var = light_lightness_srv_user_data.light_range_max;
}
lightness = var;
}
static void constrain_lightness2(u16_t var)
{
/* This is as per Mesh Model Specification 3.3.2.2.3 */
if (var > 0 && var < light_lightness_srv_user_data.light_range_min) {
if (gen_level_srv_root_user_data.last_delta < 0) {
var = 0U;
} else {
var = light_lightness_srv_user_data.light_range_min;
}
} else if (var > light_lightness_srv_user_data.light_range_max) {
var = light_lightness_srv_user_data.light_range_max;
}
lightness = var;
}
static void constrain_target_lightness(u16_t var)
{
if (var > 0 &&
var < light_lightness_srv_user_data.light_range_min) {
var = light_lightness_srv_user_data.light_range_min;
} else if (var > light_lightness_srv_user_data.light_range_max) {
var = light_lightness_srv_user_data.light_range_max;
}
target_lightness = var;
}
static s16_t light_ctl_temp_to_level(u16_t temp)
{
float tmp;
/* Mesh Model Specification 6.1.3.1.1 2nd formula start */
tmp = (temp - light_ctl_srv_user_data.temp_range_min) * 65535;
tmp = tmp / (light_ctl_srv_user_data.temp_range_max -
light_ctl_srv_user_data.temp_range_min);
return (s16_t) (tmp - 32768);
/* 6.1.3.1.1 2nd formula end */
}
static u16_t level_to_light_ctl_temp(s16_t level)
{
u16_t tmp;
float diff;
/* Mesh Model Specification 6.1.3.1.1 1st formula start */
diff = (float) (light_ctl_srv_user_data.temp_range_max -
light_ctl_srv_user_data.temp_range_min) / 65535;
tmp = (u16_t) ((level + 32768) * diff);
return (light_ctl_srv_user_data.temp_range_min + tmp);
/* 6.1.3.1.1 1st formula end */
}
void state_binding(u8_t light, u8_t temp)
{
switch (temp) {
case ONOFF_TEMP:
case CTL_TEMP:
temperature =
light_ctl_temp_to_level(light_ctl_srv_user_data.temp);
gen_level_srv_s0_user_data.level = temperature;
break;
case LEVEL_TEMP:
temperature = gen_level_srv_s0_user_data.level;
light_ctl_srv_user_data.temp =
level_to_light_ctl_temp(temperature);
break;
default:
break;
}
switch (light) {
case ONPOWERUP:
if (gen_onoff_srv_root_user_data.onoff == STATE_OFF) {
lightness = 0U;
} else if (gen_onoff_srv_root_user_data.onoff == STATE_ON) {
lightness = light_lightness_srv_user_data.last;
}
break;
case ONOFF:
if (gen_onoff_srv_root_user_data.onoff == STATE_OFF) {
lightness = 0U;
} else if (gen_onoff_srv_root_user_data.onoff == STATE_ON) {
if (light_lightness_srv_user_data.def == 0) {
lightness = light_lightness_srv_user_data.last;
} else {
lightness = light_lightness_srv_user_data.def;
}
}
break;
case LEVEL:
lightness = gen_level_srv_root_user_data.level + 32768;
break;
case DELTA_LEVEL:
lightness = gen_level_srv_root_user_data.level + 32768;
constrain_lightness2(lightness);
goto jump;
case ACTUAL:
lightness = light_lightness_srv_user_data.actual;
break;
case LINEAR:
lightness =
linear_to_actual(light_lightness_srv_user_data.linear);
break;
case CTL:
lightness = light_ctl_srv_user_data.lightness;
break;
default:
break;
}
constrain_lightness(lightness);
jump:
if (lightness != 0) {
light_lightness_srv_user_data.last = lightness;
}
if (lightness) {
gen_onoff_srv_root_user_data.onoff = STATE_ON;
} else {
gen_onoff_srv_root_user_data.onoff = STATE_OFF;
}
gen_level_srv_root_user_data.level = lightness - 32768;
light_lightness_srv_user_data.actual = lightness;
light_lightness_srv_user_data.linear = actual_to_linear(lightness);
light_ctl_srv_user_data.lightness = lightness;
}
void calculate_lightness_target_values(u8_t type)
{
bool set_light_ctl_temp_target_value;
u16_t tmp;
set_light_ctl_temp_target_value = true;
switch (type) {
case ONOFF:
if (gen_onoff_srv_root_user_data.target_onoff == 0) {
tmp = 0U;
} else {
if (light_lightness_srv_user_data.def == 0) {
tmp = light_lightness_srv_user_data.last;
} else {
tmp = light_lightness_srv_user_data.def;
}
}
break;
case LEVEL:
tmp = gen_level_srv_root_user_data.target_level + 32768;
break;
case ACTUAL:
tmp = light_lightness_srv_user_data.target_actual;
break;
case LINEAR:
tmp = linear_to_actual(light_lightness_srv_user_data.target_linear);
break;
case CTL:
set_light_ctl_temp_target_value = false;
tmp = light_ctl_srv_user_data.target_lightness;
target_temperature = light_ctl_temp_to_level(light_ctl_srv_user_data.target_temp);
gen_level_srv_s0_user_data.target_level = target_temperature;
break;
default:
return;
}
constrain_target_lightness(tmp);
if (target_lightness) {
gen_onoff_srv_root_user_data.target_onoff = STATE_ON;
} else {
gen_onoff_srv_root_user_data.target_onoff = STATE_OFF;
}
gen_level_srv_root_user_data.target_level = target_lightness - 32768;
light_lightness_srv_user_data.target_actual = target_lightness;
light_lightness_srv_user_data.target_linear =
actual_to_linear(target_lightness);
light_ctl_srv_user_data.target_lightness = target_lightness;
if (set_light_ctl_temp_target_value) {
target_temperature = light_ctl_srv_user_data.temp;
light_ctl_srv_user_data.target_temp = target_temperature;
}
}
void calculate_temp_target_values(u8_t type)
{
bool set_light_ctl_delta_uv_target_value;
set_light_ctl_delta_uv_target_value = true;
switch (type) {
case LEVEL_TEMP:
target_temperature = gen_level_srv_s0_user_data.target_level;
light_ctl_srv_user_data.target_temp =
level_to_light_ctl_temp(target_temperature);
break;
case CTL_TEMP:
set_light_ctl_delta_uv_target_value = false;
target_temperature = light_ctl_temp_to_level(light_ctl_srv_user_data.target_temp);
gen_level_srv_s0_user_data.target_level = target_temperature;
break;
default:
return;
}
target_lightness = light_ctl_srv_user_data.lightness;
light_ctl_srv_user_data.target_lightness = target_lightness;
if (set_light_ctl_delta_uv_target_value) {
light_ctl_srv_user_data.target_delta_uv =
light_ctl_srv_user_data.delta_uv;
}
}

View File

@@ -0,0 +1,53 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
/* Bluetooth: Mesh Generic OnOff, Generic Level, Lighting & Vendor Models
*
* Copyright (c) 2018 Vikrant More
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef _STATE_BINDING_H
#define _STATE_BINDING_H
enum state_binding {
ONPOWERUP = 0x01,
ONOFF,
LEVEL,
DELTA_LEVEL,
ACTUAL,
LINEAR,
CTL,
IGNORE,
ONOFF_TEMP,
LEVEL_TEMP,
CTL_TEMP,
IGNORE_TEMP
};
extern u16_t lightness, target_lightness;
extern s16_t temperature, target_temperature;
void state_binding(u8_t lightness, u8_t temperature);
void calculate_lightness_target_values(u8_t type);
void calculate_temp_target_values(u8_t type);
#endif

View File

@@ -0,0 +1,255 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
/* Bluetooth: Mesh Generic OnOff, Generic Level, Lighting & Vendor Models
*
* Copyright (c) 2018 Vikrant More
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "base64/base64.h"
#include "console/console.h"
#include "mesh/mesh.h"
#include "ble_mesh.h"
#include "device_composition.h"
#include "storage.h"
static u8_t storage_id;
u8_t reset_counter;
static void save_reset_counter(void)
{
char buf[5];
settings_str_from_bytes(&reset_counter, sizeof(reset_counter), buf,
sizeof(buf));
settings_save_one("ps/rc", buf);
}
static void save_gen_def_trans_time_state(void)
{
char buf[5];
settings_str_from_bytes(&gen_def_trans_time_srv_user_data.tt,
sizeof(gen_def_trans_time_srv_user_data.tt),
buf, sizeof(buf));
settings_save_one("ps/gdtt", buf);
}
static void save_gen_onpowerup_state(void)
{
char buf[5];
settings_str_from_bytes(&gen_power_onoff_srv_user_data.onpowerup,
sizeof(gen_power_onoff_srv_user_data.onpowerup),
buf, sizeof(buf));
settings_save_one("ps/gpo", buf);
if (gen_power_onoff_srv_user_data.onpowerup == 0x02) {
save_on_flash(LIGHTNESS_TEMP_LAST_STATE);
}
}
static void save_lightness_temp_def_state(void)
{
char buf[12];
light_ctl_srv_user_data.lightness_temp_def =
(u32_t) ((light_ctl_srv_user_data.lightness_def << 16) |
light_ctl_srv_user_data.temp_def);
settings_str_from_bytes(&light_ctl_srv_user_data.lightness_temp_def,
sizeof(light_ctl_srv_user_data.lightness_temp_def),
buf, sizeof(buf));
settings_save_one("ps/ltd", buf);
}
static void save_lightness_temp_last_state(void)
{
char buf[12];
light_ctl_srv_user_data.lightness_temp_last =
(u32_t) ((light_ctl_srv_user_data.lightness << 16) |
light_ctl_srv_user_data.temp);
settings_str_from_bytes(&light_ctl_srv_user_data.lightness_temp_last,
sizeof(light_ctl_srv_user_data.lightness_temp_last),
buf, sizeof(buf));
settings_save_one("ps/ltl", buf);
printk("Light CTL Last values have beed saved !!\n");
}
static void save_lightness_range(void)
{
char buf[12];
light_lightness_srv_user_data.lightness_range =
(u32_t) ((light_lightness_srv_user_data.light_range_max << 16) |
light_lightness_srv_user_data.light_range_min);
settings_str_from_bytes(&light_lightness_srv_user_data.lightness_range,
sizeof(light_lightness_srv_user_data.lightness_range),
buf, sizeof(buf));
settings_save_one("ps/lr", buf);
}
static void save_temperature_range(void)
{
char buf[12];
light_ctl_srv_user_data.temperature_range =
(u32_t) ((light_ctl_srv_user_data.temp_range_max << 16) |
light_ctl_srv_user_data.temp_range_min);
settings_str_from_bytes(&light_ctl_srv_user_data.temperature_range,
sizeof(light_ctl_srv_user_data.temperature_range),
buf, sizeof(buf));
settings_save_one("ps/tr", buf);
}
static void storage_work_handler(struct os_event *work)
{
switch (storage_id) {
case RESET_COUNTER:
save_reset_counter();
break;
case GEN_DEF_TRANS_TIME_STATE:
save_gen_def_trans_time_state();
break;
case GEN_ONPOWERUP_STATE:
save_gen_onpowerup_state();
break;
case LIGHTNESS_TEMP_DEF_STATE:
save_lightness_temp_def_state();
break;
case LIGHTNESS_TEMP_LAST_STATE:
save_lightness_temp_last_state();
break;
case LIGHTNESS_RANGE:
save_lightness_range();
break;
case TEMPERATURE_RANGE:
save_temperature_range();
break;
}
}
struct os_callout storage_work;
void save_on_flash(u8_t id)
{
storage_id = id;
os_callout_reset(&storage_work, 0);
}
static int ps_set(int argc, char **argv, char *val)
{
int len;
if (argc == 1) {
if (!strcmp(argv[0], "rc")) {
len = sizeof(reset_counter);
return settings_bytes_from_str(val, &reset_counter,
&len);
}
if (!strcmp(argv[0], "gdtt")) {
len = sizeof(gen_def_trans_time_srv_user_data.tt);
return settings_bytes_from_str(val,
&gen_def_trans_time_srv_user_data.tt, &len);
}
if (!strcmp(argv[0], "gpo")) {
len = sizeof(gen_power_onoff_srv_user_data.onpowerup);
return settings_bytes_from_str(val,
&gen_power_onoff_srv_user_data.onpowerup, &len);
}
if (!strcmp(argv[0], "ltd")) {
len = sizeof(light_ctl_srv_user_data.lightness_temp_def);
return settings_bytes_from_str(val,
&light_ctl_srv_user_data.lightness_temp_def,
&len);
}
if (!strcmp(argv[0], "ltl")) {
len = sizeof(light_ctl_srv_user_data.
lightness_temp_last);
return settings_bytes_from_str(val,
&light_ctl_srv_user_data.lightness_temp_last,
&len);
}
if (!strcmp(argv[0], "lr")) {
len = sizeof(light_lightness_srv_user_data.
lightness_range);
return settings_bytes_from_str(val,
&light_lightness_srv_user_data.lightness_range,
&len);
}
if (!strcmp(argv[0], "tr")) {
len = sizeof(light_ctl_srv_user_data.
temperature_range);
return settings_bytes_from_str(val,
&light_ctl_srv_user_data. temperature_range,
&len);
}
}
return -ENOENT;
}
static struct conf_handler ps_settings = {
.ch_name = "ps",
.ch_set = ps_set,
};
int ps_settings_init(void)
{
int err;
os_callout_init(&storage_work, os_eventq_dflt_get(),
storage_work_handler, NULL);
err = conf_register(&ps_settings);
if (err) {
printk("ps_settings_register failed (err %d)", err);
return err;
}
return 0;
}

View File

@@ -0,0 +1,47 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
/* Bluetooth: Mesh Generic OnOff, Generic Level, Lighting & Vendor Models
*
* Copyright (c) 2018 Vikrant More
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef _STORAGE_H
#define _STORAGE_H
enum ps_variables_id {
RESET_COUNTER = 0x01,
GEN_DEF_TRANS_TIME_STATE,
GEN_ONPOWERUP_STATE,
LIGHTNESS_TEMP_DEF_STATE,
LIGHTNESS_TEMP_LAST_STATE,
LIGHTNESS_RANGE,
TEMPERATURE_RANGE
};
extern u8_t reset_counter;
extern struct os_callout storage_work;
int ps_settings_init(void);
void save_on_flash(u8_t id);
#endif

View File

@@ -0,0 +1,792 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
/* Bluetooth: Mesh Generic OnOff, Generic Level, Lighting & Vendor Models
*
* Copyright (c) 2018 Vikrant More
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "ble_mesh.h"
#include "common.h"
#include "device_composition.h"
#include "state_binding.h"
#include "transition.h"
struct os_callout onoff_work;
struct os_callout level_lightness_work;
struct os_callout level_temp_work;
struct os_callout light_lightness_actual_work;
struct os_callout light_lightness_linear_work;
struct os_callout light_ctl_work;
struct os_callout light_ctl_temp_work;
struct os_callout dummy_timer;
u8_t transition_type, default_tt;
u32_t *ptr_counter;
struct os_callout *ptr_timer = &dummy_timer;
struct transition lightness_transition, temp_transition;
/* Function to calculate Remaining Time (Start) */
void calculate_rt(struct transition *transition)
{
u8_t steps, resolution;
s32_t duration_remainder;
s64_t now;
if (transition->just_started) {
transition->rt = transition->tt;
} else {
now = k_uptime_get();
duration_remainder = transition->total_duration -
(now - transition->start_timestamp);
if (duration_remainder > 620000) {
/* > 620 seconds -> resolution = 0b11 [10 minutes] */
resolution = 0x03;
steps = duration_remainder / 600000;
} else if (duration_remainder > 62000) {
/* > 62 seconds -> resolution = 0b10 [10 seconds] */
resolution = 0x02;
steps = duration_remainder / 10000;
} else if (duration_remainder > 6200) {
/* > 6.2 seconds -> resolution = 0b01 [1 seconds] */
resolution = 0x01;
steps = duration_remainder / 1000;
} else if (duration_remainder > 0) {
/* <= 6.2 seconds -> resolution = 0b00 [100 ms] */
resolution = 0x00;
steps = duration_remainder / 100;
} else {
resolution = 0x00;
steps = 0x00;
}
transition->rt = (resolution << 6) | steps;
}
}
/* Function to calculate Remaining Time (End) */
static void bound_states_transition_type_reassignment(u8_t type)
{
switch (type) {
case ONOFF:
case LEVEL:
case ACTUAL:
case LINEAR:
light_ctl_srv_user_data.transition = &lightness_transition;
break;
case CTL:
light_ctl_srv_user_data.transition = &lightness_transition;
gen_level_srv_s0_user_data.transition = &lightness_transition;
break;
case LEVEL_TEMP:
case CTL_TEMP:
gen_level_srv_s0_user_data.transition = &temp_transition;
light_ctl_srv_user_data.transition = &temp_transition;
break;
default:
break;
}
}
static void tt_values_calculator(struct transition *transition)
{
u8_t steps_multiplier, resolution;
resolution = (transition->tt >> 6);
steps_multiplier = (transition->tt & 0x3F);
switch (resolution) {
case 0: /* 100ms */
transition->total_duration = steps_multiplier * 100;
break;
case 1: /* 1 second */
transition->total_duration = steps_multiplier * 1000;
break;
case 2: /* 10 seconds */
transition->total_duration = steps_multiplier * 10000;
break;
case 3: /* 10 minutes */
transition->total_duration = steps_multiplier * 600000;
break;
}
transition->counter = ((float) transition->total_duration / 100);
if (transition->counter > DEVICE_SPECIFIC_RESOLUTION) {
transition->counter = DEVICE_SPECIFIC_RESOLUTION;
}
ptr_counter = &transition->counter;
}
void onoff_tt_values(struct generic_onoff_state *state, u8_t tt, u8_t delay)
{
bound_states_transition_type_reassignment(ONOFF);
calculate_lightness_target_values(ONOFF);
state->transition->tt = tt;
state->transition->delay = delay;
if (tt != 0) {
tt_values_calculator(state->transition);
} else {
return;
}
state->transition->quo_tt = state->transition->total_duration /
state->transition->counter;
state->tt_delta = ((float) (lightness - target_lightness) /
state->transition->counter);
}
void level_tt_values(struct generic_level_state *state, u8_t tt, u8_t delay)
{
if (state == &gen_level_srv_root_user_data) {
bound_states_transition_type_reassignment(LEVEL);
calculate_lightness_target_values(LEVEL);
} else if (state == &gen_level_srv_s0_user_data) {
bound_states_transition_type_reassignment(LEVEL_TEMP);
calculate_temp_target_values(LEVEL_TEMP);
}
state->transition->tt = tt;
state->transition->delay = delay;
if (tt != 0) {
tt_values_calculator(state->transition);
} else {
return;
}
state->transition->quo_tt = state->transition->total_duration /
state->transition->counter;
state->tt_delta = ((float) (state->level - state->target_level) /
state->transition->counter);
}
void light_lightness_actual_tt_values(struct light_lightness_state *state,
u8_t tt, u8_t delay)
{
bound_states_transition_type_reassignment(ACTUAL);
calculate_lightness_target_values(ACTUAL);
state->transition->tt = tt;
state->transition->delay = delay;
if (tt != 0) {
tt_values_calculator(state->transition);
} else {
return;
}
state->transition->quo_tt = state->transition->total_duration /
state->transition->counter;
state->tt_delta_actual =
((float) (state->actual - state->target_actual) /
state->transition->counter);
}
void light_lightness_linear_tt_values(struct light_lightness_state *state,
u8_t tt, u8_t delay)
{
bound_states_transition_type_reassignment(LINEAR);
calculate_lightness_target_values(LINEAR);
state->transition->tt = tt;
state->transition->delay = delay;
if (tt != 0) {
tt_values_calculator(state->transition);
} else {
return;
}
state->transition->quo_tt = state->transition->total_duration /
state->transition->counter;
state->tt_delta_linear =
((float) (state->linear - state->target_linear) /
state->transition->counter);
}
void light_ctl_tt_values(struct light_ctl_state *state, u8_t tt, u8_t delay)
{
bound_states_transition_type_reassignment(CTL);
calculate_lightness_target_values(CTL);
state->transition->tt = tt;
state->transition->delay = delay;
if (tt != 0) {
tt_values_calculator(state->transition);
} else {
return;
}
state->transition->quo_tt = state->transition->total_duration /
state->transition->counter;
state->tt_delta_lightness =
((float) (state->lightness - state->target_lightness) /
state->transition->counter);
state->tt_delta_temp =
((float) (state->temp - state->target_temp) /
state->transition->counter);
state->tt_delta_duv =
((float) (state->delta_uv - state->target_delta_uv) /
state->transition->counter);
}
void light_ctl_temp_tt_values(struct light_ctl_state *state,
u8_t tt, u8_t delay)
{
bound_states_transition_type_reassignment(CTL_TEMP);
calculate_temp_target_values(CTL_TEMP);
state->transition->tt = tt;
state->transition->delay = delay;
if (tt != 0) {
tt_values_calculator(state->transition);
} else {
return;
}
state->transition->quo_tt = state->transition->total_duration /
state->transition->counter;
state->tt_delta_temp = ((float) (state->temp - state->target_temp) /
state->transition->counter);
state->tt_delta_duv =
((float) (state->delta_uv - state->target_delta_uv) /
state->transition->counter);
}
/* Timers related handlers & threads (Start) */
static void onoff_work_handler(struct os_event *work)
{
struct generic_onoff_state *state = &gen_onoff_srv_root_user_data;
if (state->transition->just_started) {
state->transition->just_started = false;
if (state->transition->counter == 0) {
state_binding(ONOFF, IGNORE_TEMP);
update_light_state();
os_callout_stop(ptr_timer);
} else {
state->transition->start_timestamp = k_uptime_get();
if (state->target_onoff == STATE_ON) {
state->onoff = STATE_ON;
}
}
return;
}
if (state->transition->counter != 0) {
state->transition->counter--;
lightness -= state->tt_delta;
state_binding(IGNORE, IGNORE_TEMP);
update_light_state();
}
if (state->transition->counter == 0) {
state->onoff = state->target_onoff;
lightness = target_lightness;
state_binding(IGNORE, IGNORE_TEMP);
update_light_state();
os_callout_stop(ptr_timer);
}
}
static void level_lightness_work_handler(struct os_event *work)
{
u8_t level;
struct generic_level_state *state = &gen_level_srv_root_user_data;
switch (transition_type) {
case LEVEL_TT:
level = LEVEL;
break;
case LEVEL_TT_DELTA:
level = DELTA_LEVEL;
break;
case LEVEL_TT_MOVE:
level = LEVEL;
break;
default:
return;
}
if (state->transition->just_started) {
state->transition->just_started = false;
if (state->transition->counter == 0) {
state_binding(level, IGNORE_TEMP);
update_light_state();
os_callout_stop(ptr_timer);
} else {
state->transition->start_timestamp = k_uptime_get();
}
return;
}
if (state->transition->counter != 0) {
state->transition->counter--;
state->level -= state->tt_delta;
state_binding(level, IGNORE_TEMP);
update_light_state();
}
if (state->transition->counter == 0) {
state->level = state->target_level;
state_binding(level, IGNORE_TEMP);
update_light_state();
os_callout_stop(ptr_timer);
}
}
static void level_temp_work_handler(struct os_event *work)
{
struct generic_level_state *state = &gen_level_srv_s0_user_data;
switch (transition_type) {
case LEVEL_TEMP_TT:
break;
case LEVEL_TEMP_TT_DELTA:
break;
case LEVEL_TEMP_TT_MOVE:
break;
default:
return;
}
if (state->transition->just_started) {
state->transition->just_started = false;
if (state->transition->counter == 0) {
state_binding(IGNORE, LEVEL_TEMP);
update_light_state();
os_callout_stop(ptr_timer);
} else {
state->transition->start_timestamp = k_uptime_get();
}
return;
}
if (state->transition->counter != 0) {
state->transition->counter--;
state->level -= state->tt_delta;
state_binding(IGNORE, LEVEL_TEMP);
update_light_state();
}
if (state->transition->counter == 0) {
state->level = state->target_level;
state_binding(IGNORE, LEVEL_TEMP);
update_light_state();
os_callout_stop(ptr_timer);
}
}
static void light_lightness_actual_work_handler(struct os_event *work)
{
struct light_lightness_state *state = &light_lightness_srv_user_data;
if (state->transition->just_started) {
state->transition->just_started = false;
if (state->transition->counter == 0) {
state_binding(ACTUAL, IGNORE_TEMP);
update_light_state();
os_callout_stop(ptr_timer);
} else {
state->transition->start_timestamp = k_uptime_get();
}
return;
}
if (state->transition->counter != 0) {
state->transition->counter--;
state->actual -= state->tt_delta_actual;
state_binding(ACTUAL, IGNORE_TEMP);
update_light_state();
}
if (state->transition->counter == 0) {
state->actual = state->target_actual;
state_binding(ACTUAL, IGNORE_TEMP);
update_light_state();
os_callout_stop(ptr_timer);
}
}
static void light_lightness_linear_work_handler(struct os_event *work)
{
struct light_lightness_state *state = &light_lightness_srv_user_data;
if (state->transition->just_started) {
state->transition->just_started = false;
if (state->transition->counter == 0) {
state_binding(LINEAR, IGNORE_TEMP);
update_light_state();
os_callout_stop(ptr_timer);
} else {
state->transition->start_timestamp = k_uptime_get();
}
return;
}
if (state->transition->counter != 0) {
state->transition->counter--;
state->linear -= state->tt_delta_linear;
state_binding(LINEAR, IGNORE_TEMP);
update_light_state();
}
if (state->transition->counter == 0) {
state->linear = state->target_linear;
state_binding(LINEAR, IGNORE_TEMP);
update_light_state();
os_callout_stop(ptr_timer);
}
}
static void light_ctl_work_handler(struct os_event *work)
{
struct light_ctl_state *state = &light_ctl_srv_user_data;
if (state->transition->just_started) {
state->transition->just_started = false;
if (state->transition->counter == 0) {
state_binding(CTL, CTL_TEMP);
update_light_state();
os_callout_stop(ptr_timer);
} else {
state->transition->start_timestamp = k_uptime_get();
}
return;
}
if (state->transition->counter != 0) {
state->transition->counter--;
/* Lightness */
state->lightness -= state->tt_delta_lightness;
/* Temperature */
state->temp -= state->tt_delta_temp;
/* Delta_UV */
state->delta_uv -= state->tt_delta_duv;
state_binding(CTL, CTL_TEMP);
update_light_state();
}
if (state->transition->counter == 0) {
state->lightness = state->target_lightness;
state->temp = state->target_temp;
state->delta_uv = state->target_delta_uv;
state_binding(CTL, CTL_TEMP);
update_light_state();
os_callout_stop(ptr_timer);
}
}
static void light_ctl_temp_work_handler(struct os_event *work)
{
struct light_ctl_state *state = &light_ctl_srv_user_data;
if (state->transition->just_started) {
state->transition->just_started = false;
if (state->transition->counter == 0) {
state_binding(IGNORE, CTL_TEMP);
update_light_state();
os_callout_stop(ptr_timer);
} else {
state->transition->start_timestamp = k_uptime_get();
}
return;
}
if (state->transition->counter != 0) {
state->transition->counter--;
/* Temperature */
state->temp -= state->tt_delta_temp;
/* Delta UV */
state->delta_uv -= state->tt_delta_duv;
state_binding(IGNORE, CTL_TEMP);
update_light_state();
}
if (state->transition->counter == 0) {
state->temp = state->target_temp;
state->delta_uv = state->target_delta_uv;
state_binding(IGNORE, CTL_TEMP);
update_light_state();
os_callout_stop(ptr_timer);
}
}
static void dummy_timer_handler(struct os_event *ev)
{ }
static void onoff_tt_handler(struct os_event *ev)
{
struct generic_onoff_state *state = ev->ev_arg;
assert(state != NULL);
os_callout_reset(&onoff_work, 0);
os_callout_reset(&state->transition->timer,
os_time_ms_to_ticks32(
K_MSEC(state->transition->quo_tt)));
}
static void level_lightness_tt_handler(struct os_event *ev)
{
struct generic_level_state *state = ev->ev_arg;
assert(state != NULL);
os_callout_reset(&level_lightness_work, 0);
os_callout_reset(&state->transition->timer,
os_time_ms_to_ticks32(
K_MSEC(state->transition->quo_tt)));
}
static void level_temp_tt_handler(struct os_event *ev)
{
struct generic_level_state *state = ev->ev_arg;
assert(state != NULL);
os_callout_reset(&level_temp_work, 0);
os_callout_reset(&state->transition->timer,
os_time_ms_to_ticks32(
K_MSEC(state->transition->quo_tt)));
}
static void light_lightness_actual_tt_handler(struct os_event *ev)
{
struct light_lightness_state *state = ev->ev_arg;
assert(state != NULL);
os_callout_reset(&light_lightness_actual_work, 0);
os_callout_reset(&state->transition->timer,
os_time_ms_to_ticks32(
K_MSEC(state->transition->quo_tt)));
}
static void light_lightness_linear_tt_handler(struct os_event *ev)
{
struct light_lightness_state *state = ev->ev_arg;
assert(state != NULL);
os_callout_reset(&light_lightness_linear_work, 0);
os_callout_reset(&state->transition->timer,
os_time_ms_to_ticks32(
K_MSEC(state->transition->quo_tt)));
}
static void light_ctl_tt_handler(struct os_event *ev)
{
struct light_ctl_state *state = ev->ev_arg;
assert(state != NULL);
os_callout_reset(&light_ctl_work, 0);
os_callout_reset(&state->transition->timer,
os_time_ms_to_ticks32(
K_MSEC(state->transition->quo_tt)));
}
static void light_ctl_temp_tt_handler(struct os_event *ev)
{
struct light_ctl_state *state = ev->ev_arg;
assert(state != NULL);
os_callout_reset(&light_ctl_temp_work, 0);
os_callout_reset(&state->transition->timer,
os_time_ms_to_ticks32(
K_MSEC(state->transition->quo_tt)));
}
/* Timers related handlers & threads (End) */
/* Messages handlers (Start) */
void onoff_handler(struct generic_onoff_state *state)
{
ptr_timer = &state->transition->timer;
os_callout_init(ptr_timer, os_eventq_dflt_get(),
onoff_tt_handler, NULL);
ptr_timer->c_ev.ev_arg = state;
os_callout_reset(ptr_timer,
os_time_ms_to_ticks32(
K_MSEC(5 * state->transition->delay)));
}
void level_lightness_handler(struct generic_level_state *state)
{
ptr_timer = &state->transition->timer;
os_callout_init(ptr_timer, os_eventq_dflt_get(),
level_lightness_tt_handler, NULL);
ptr_timer->c_ev.ev_arg = state;
os_callout_reset(ptr_timer,
os_time_ms_to_ticks32(
K_MSEC(5 * state->transition->delay)));
}
void level_temp_handler(struct generic_level_state *state)
{
ptr_timer = &state->transition->timer;
os_callout_init(ptr_timer, os_eventq_dflt_get(),
level_temp_tt_handler, NULL);
ptr_timer->c_ev.ev_arg = state;
os_callout_reset(ptr_timer,
os_time_ms_to_ticks32(
K_MSEC(5 * state->transition->delay)));
}
void light_lightness_actual_handler(struct light_lightness_state *state)
{
ptr_timer = &state->transition->timer;
os_callout_init(ptr_timer, os_eventq_dflt_get(),
light_lightness_actual_tt_handler, NULL);
ptr_timer->c_ev.ev_arg = state;
os_callout_reset(ptr_timer,
os_time_ms_to_ticks32(
K_MSEC(5 * state->transition->delay)));
}
void light_lightness_linear_handler(struct light_lightness_state *state)
{
ptr_timer = &state->transition->timer;
os_callout_init(ptr_timer, os_eventq_dflt_get(),
light_lightness_linear_tt_handler, NULL);
ptr_timer->c_ev.ev_arg = state;
os_callout_reset(ptr_timer,
os_time_ms_to_ticks32(
K_MSEC(5 * state->transition->delay)));
}
void light_ctl_handler(struct light_ctl_state *state)
{
ptr_timer = &state->transition->timer;
os_callout_init(ptr_timer, os_eventq_dflt_get(),
light_ctl_tt_handler, NULL);
ptr_timer->c_ev.ev_arg = state;
os_callout_reset(ptr_timer,
os_time_ms_to_ticks32(
K_MSEC(5 * state->transition->delay)));
}
void light_ctl_temp_handler(struct light_ctl_state *state)
{
ptr_timer = &state->transition->timer;
os_callout_init(ptr_timer, os_eventq_dflt_get(),
light_ctl_temp_tt_handler, NULL);
ptr_timer->c_ev.ev_arg = state;
os_callout_reset(ptr_timer,
os_time_ms_to_ticks32(
K_MSEC(5 * state->transition->delay)));
}
/* Messages handlers (End) */
void transition_timers_init(void)
{
os_callout_init(&onoff_work, os_eventq_dflt_get(),
onoff_work_handler, NULL);
os_callout_init(&level_lightness_work, os_eventq_dflt_get(),
level_lightness_work_handler, NULL);
os_callout_init(&level_temp_work, os_eventq_dflt_get(),
level_temp_work_handler, NULL);
os_callout_init(&light_lightness_actual_work,
os_eventq_dflt_get(),
light_lightness_actual_work_handler, NULL);
os_callout_init(&light_lightness_linear_work,
os_eventq_dflt_get(),
light_lightness_linear_work_handler, NULL);
os_callout_init(&light_ctl_work, os_eventq_dflt_get(),
light_ctl_work_handler, NULL);
os_callout_init(&light_ctl_temp_work, os_eventq_dflt_get(),
light_ctl_temp_work_handler, NULL);
os_callout_init(&dummy_timer, os_eventq_dflt_get(),
dummy_timer_handler, NULL);
}

View File

@@ -0,0 +1,87 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
/* Bluetooth: Mesh Generic OnOff, Generic Level, Lighting & Vendor Models
*
* Copyright (c) 2018 Vikrant More
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef _TRANSITION_H
#define _TRANSITION_H
#define UNKNOWN_VALUE 0x3F
#define DEVICE_SPECIFIC_RESOLUTION 10
enum level_transition_types {
LEVEL_TT,
LEVEL_TT_DELTA,
LEVEL_TT_MOVE,
LEVEL_TEMP_TT,
LEVEL_TEMP_TT_DELTA,
LEVEL_TEMP_TT_MOVE,
};
struct transition {
bool just_started;
u8_t tt;
u8_t rt;
u8_t delay;
u32_t quo_tt;
u32_t counter;
u32_t total_duration;
s64_t start_timestamp;
struct os_callout timer;
};
extern u8_t transition_type, default_tt;
extern u32_t *ptr_counter;
extern struct os_callout *ptr_timer;
extern struct transition lightness_transition, temp_transition;
extern struct os_callout dummy_timer;
void calculate_rt(struct transition *transition);
void onoff_tt_values(struct generic_onoff_state *state, u8_t tt, u8_t delay);
void level_tt_values(struct generic_level_state *state, u8_t tt, u8_t delay);
void light_lightness_actual_tt_values(struct light_lightness_state *state,
u8_t tt, u8_t delay);
void light_lightness_linear_tt_values(struct light_lightness_state *state,
u8_t tt, u8_t delay);
void light_ctl_tt_values(struct light_ctl_state *state, u8_t tt, u8_t delay);
void light_ctl_temp_tt_values(struct light_ctl_state *state,
u8_t tt, u8_t delay);
void onoff_handler(struct generic_onoff_state *state);
void level_lightness_handler(struct generic_level_state *state);
void level_temp_handler(struct generic_level_state *state);
void light_lightness_actual_handler(struct light_lightness_state *state);
void light_lightness_linear_handler(struct light_lightness_state *state);
void light_ctl_handler(struct light_ctl_state *state);
void light_ctl_temp_handler(struct light_ctl_state *state);
void transition_timers_init(void);
#endif

View File

@@ -0,0 +1,73 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you 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.
#
syscfg.vals:
# Set log level to info (disable debug logging).
LOG_LEVEL: 1
# Default task settings
OS_MAIN_STACK_SIZE: 4096
# Newtmgr is not supported in this app, so disable newtmgr-over-shell.
SHELL_NEWTMGR: 0
MSYS_1_BLOCK_COUNT: 48
FLOAT_USER: 1
HARD_FLOAT: 1
BLE_MESH_DEV_UUID: "((uint8_t[16]){0xdd, 0xdd, 0})"
BLE_MESH_ADV_BUF_COUNT: 60
BLE_MESH_TX_SEG_MAX: 6
BLE_MESH_TX_SEG_MSG_COUNT: 3
BLE_MESH_RX_SEG_MSG_COUNT: 3
BLE_MESH_CRPL: 128
BLE_MESH_RPL_STORE_TIMEOUT: 120
BLE_MESH_MSG_CACHE_SIZE: 100
BLE_MESH_SETTINGS: 1
CONFIG_FCB: 1
BLE_MESH: 1
BLE_MESH_RELAY: 1
BLE_MESH_LOW_POWER: 0
BLE_MESH_LPN_AUTO: 0
BLE_MESH_FRIEND: 0
BLE_MESH_PROV: 1
BLE_MESH_PB_ADV: 1
BLE_MESH_PB_GATT: 1
BLE_MESH_GATT_PROXY: 1
BLE_MESH_SUBNET_COUNT: 2
BLE_MESH_APP_KEY_COUNT: 2
BLE_MESH_MODEL_GROUP_COUNT: 2
BLE_MESH_LABEL_COUNT: 3
BLE_MESH_DEBUG: 0
BLE_MESH_DEBUG_NET: 0
BLE_MESH_DEBUG_TRANS: 0
BLE_MESH_DEBUG_BEACON: 0
BLE_MESH_DEBUG_CRYPTO: 0
BLE_MESH_DEBUG_PROV: 0
BLE_MESH_DEBUG_ACCESS: 0
BLE_MESH_DEBUG_MODEL: 0
BLE_MESH_DEBUG_ADV: 0
BLE_MESH_DEBUG_LOW_POWER: 0
BLE_MESH_DEBUG_FRIEND: 0
BLE_MESH_DEBUG_PROXY: 0

View File

@@ -0,0 +1,37 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you 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.
#
pkg.name: apps/blemesh_shell
pkg.type: app
pkg.description: Sample application for BLE Mesh node with shell support
pkg.author: "Michał Narajowski <michal.narajowski@codecoup.pl>"
pkg.homepage: "http://mynewt.apache.org/"
pkg.keywords:
pkg.deps:
- "@apache-mynewt-core/kernel/os"
- "@apache-mynewt-core/sys/console/full"
- "@apache-mynewt-core/sys/log/full"
- "@apache-mynewt-core/sys/log/modlog"
- "@apache-mynewt-core/sys/stats/full"
- "@apache-mynewt-core/sys/shell"
- nimble/controller
- nimble/host
- nimble/host/services/gap
- nimble/host/services/gatt
- nimble/host/store/ram
- nimble/transport/ram

View File

@@ -0,0 +1,112 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
#include <assert.h>
#include "os/mynewt.h"
#include "mesh/mesh.h"
#include "console/console.h"
#include "hal/hal_system.h"
#include "hal/hal_gpio.h"
#include "bsp/bsp.h"
#include "shell/shell.h"
/* BLE */
#include "nimble/ble.h"
#include "host/ble_hs.h"
#include "services/gap/ble_svc_gap.h"
#include "mesh/glue.h"
#include "mesh/testing.h"
void net_recv_ev(uint8_t ttl, uint8_t ctl, uint16_t src, uint16_t dst,
const void *payload, size_t payload_len)
{
console_printf("Received net packet: ttl 0x%02x ctl 0x%02x src 0x%04x "
"dst 0x%04x " "payload_len %d\n", ttl, ctl, src, dst,
payload_len);
}
static void model_bound_cb(u16_t addr, struct bt_mesh_model *model,
u16_t key_idx)
{
console_printf("Model bound: remote addr 0x%04x key_idx 0x%04x model %p\n",
addr, key_idx, model);
}
static void model_unbound_cb(u16_t addr, struct bt_mesh_model *model,
u16_t key_idx)
{
console_printf("Model unbound: remote addr 0x%04x key_idx 0x%04x "
"model %p\n", addr, key_idx, model);
}
static void invalid_bearer_cb(u8_t opcode)
{
console_printf("Invalid bearer: opcode 0x%02x\n", opcode);
}
static void incomp_timer_exp_cb(void)
{
console_printf("Incomplete timer expired\n");
}
static struct bt_test_cb bt_test_cb = {
.mesh_net_recv = net_recv_ev,
.mesh_model_bound = model_bound_cb,
.mesh_model_unbound = model_unbound_cb,
.mesh_prov_invalid_bearer = invalid_bearer_cb,
.mesh_trans_incomp_timer_exp = incomp_timer_exp_cb,
};
static void
blemesh_on_reset(int reason)
{
BLE_HS_LOG(ERROR, "Resetting state; reason=%d\n", reason);
}
static void
blemesh_on_sync(void)
{
console_printf("Bluetooth initialized\n");
shell_register_default_module("mesh");
if (IS_ENABLED(CONFIG_BT_TESTING)) {
bt_test_cb_register(&bt_test_cb);
}
}
int
main(void)
{
/* Initialize OS */
sysinit();
/* Initialize the NimBLE host configuration. */
ble_hs_cfg.reset_cb = blemesh_on_reset;
ble_hs_cfg.sync_cb = blemesh_on_sync;
ble_hs_cfg.store_status_cb = ble_store_util_status_rr;
hal_gpio_init_out(LED_2, 0);
while (1) {
os_eventq_run(os_eventq_dflt_get());
}
return 0;
}

View File

@@ -0,0 +1,68 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you 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.
#
syscfg.vals:
# Enable the shell task.
SHELL_TASK: 1
# Set log level to info (disable debug logging).
LOG_LEVEL: 0
# Default task settings
OS_MAIN_STACK_SIZE: 768
# Newtmgr is not supported in this app, so disable newtmgr-over-shell.
SHELL_NEWTMGR: 0
MSYS_1_BLOCK_COUNT: 80
BLE_MESH_ADV_BUF_COUNT: 20
BLE_MESH_TX_SEG_MAX: 6
BLE_MESH: 1
BLE_MESH_SHELL: 1
BLE_MESH_PROV: 1
BLE_MESH_RELAY: 1
BLE_MESH_PB_ADV: 1
BLE_MESH_PB_GATT: 1
BLE_MESH_LOW_POWER: 1
BLE_MESH_LPN_AUTO: 0
BLE_MESH_GATT_PROXY: 1
BLE_MESH_LABEL_COUNT: 2
BLE_MESH_SUBNET_COUNT: 2
BLE_MESH_MODEL_GROUP_COUNT: 2
BLE_MESH_APP_KEY_COUNT: 4
BLE_MESH_IV_UPDATE_TEST: 1
BLE_MESH_TESTING: 1
BLE_MESH_FRIEND: 1
BLE_MESH_CFG_CLI: 1
BLE_MESH_SETTINGS: 0
CONFIG_NFFS: 0
BLE_MESH_DEBUG: 0
BLE_MESH_DEBUG_NET: 0
BLE_MESH_DEBUG_TRANS: 0
BLE_MESH_DEBUG_BEACON: 0
BLE_MESH_DEBUG_CRYPTO: 0
BLE_MESH_DEBUG_PROV: 0
BLE_MESH_DEBUG_ACCESS: 0
BLE_MESH_DEBUG_MODEL: 0
BLE_MESH_DEBUG_ADV: 0
BLE_MESH_DEBUG_LOW_POWER: 0
BLE_MESH_DEBUG_FRIEND: 0
BLE_MESH_DEBUG_PROXY: 0

View File

@@ -0,0 +1,45 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you 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.
#
pkg.name: apps/bleprph
pkg.type: app
pkg.description: Simple BLE peripheral application.
pkg.author: "Apache Mynewt <dev@mynewt.apache.org>"
pkg.homepage: "http://mynewt.apache.org/"
pkg.keywords:
pkg.deps:
- "@apache-mynewt-core/boot/split"
- "@mcuboot/boot/bootutil"
- "@apache-mynewt-core/kernel/os"
- "@apache-mynewt-core/mgmt/imgmgr"
- "@apache-mynewt-core/mgmt/newtmgr"
- "@apache-mynewt-core/mgmt/newtmgr/transport/ble"
- "@apache-mynewt-core/sys/console/full"
- "@apache-mynewt-core/sys/log/full"
- "@apache-mynewt-core/sys/log/modlog"
- "@apache-mynewt-core/sys/stats/full"
- "@apache-mynewt-core/sys/sysinit"
- "@apache-mynewt-core/sys/id"
- nimble/host
- nimble/host/services/ans
- nimble/host/services/dis
- nimble/host/services/gap
- nimble/host/services/gatt
- nimble/host/store/config
- nimble/host/util
- nimble/transport

View File

@@ -0,0 +1,61 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 H_BLEPRPH_
#define H_BLEPRPH_
#include <stdbool.h>
#include "nimble/ble.h"
#include "modlog/modlog.h"
#ifdef __cplusplus
extern "C" {
#endif
struct ble_hs_cfg;
struct ble_gatt_register_ctxt;
/** GATT server. */
#define GATT_SVR_SVC_ALERT_UUID 0x1811
#define GATT_SVR_CHR_SUP_NEW_ALERT_CAT_UUID 0x2A47
#define GATT_SVR_CHR_NEW_ALERT 0x2A46
#define GATT_SVR_CHR_SUP_UNR_ALERT_CAT_UUID 0x2A48
#define GATT_SVR_CHR_UNR_ALERT_STAT_UUID 0x2A45
#define GATT_SVR_CHR_ALERT_NOT_CTRL_PT 0x2A44
void gatt_svr_register_cb(struct ble_gatt_register_ctxt *ctxt, void *arg);
int gatt_svr_init(void);
/* PHY support */
#if MYNEWT_VAL(BLEPRPH_LE_PHY_SUPPORT)
#define CONN_HANDLE_INVALID 0xffff
void phy_init(void);
void phy_conn_changed(uint16_t handle);
void phy_update(uint8_t phy);
#endif
/** Misc. */
void print_bytes(const uint8_t *bytes, int len);
void print_addr(const void *addr);
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -0,0 +1,204 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
#include <assert.h>
#include <stdio.h>
#include <string.h>
#include "bsp/bsp.h"
#include "host/ble_hs.h"
#include "host/ble_uuid.h"
#include "bleprph.h"
/**
* The vendor specific security test service consists of two characteristics:
* o random-number-generator: generates a random 32-bit number each time
* it is read. This characteristic can only be read over an encrypted
* connection.
* o static-value: a single-byte characteristic that can always be read,
* but can only be written over an encrypted connection.
*/
/* 59462f12-9543-9999-12c8-58b459a2712d */
static const ble_uuid128_t gatt_svr_svc_sec_test_uuid =
BLE_UUID128_INIT(0x2d, 0x71, 0xa2, 0x59, 0xb4, 0x58, 0xc8, 0x12,
0x99, 0x99, 0x43, 0x95, 0x12, 0x2f, 0x46, 0x59);
/* 5c3a659e-897e-45e1-b016-007107c96df6 */
static const ble_uuid128_t gatt_svr_chr_sec_test_rand_uuid =
BLE_UUID128_INIT(0xf6, 0x6d, 0xc9, 0x07, 0x71, 0x00, 0x16, 0xb0,
0xe1, 0x45, 0x7e, 0x89, 0x9e, 0x65, 0x3a, 0x5c);
/* 5c3a659e-897e-45e1-b016-007107c96df7 */
static const ble_uuid128_t gatt_svr_chr_sec_test_static_uuid =
BLE_UUID128_INIT(0xf7, 0x6d, 0xc9, 0x07, 0x71, 0x00, 0x16, 0xb0,
0xe1, 0x45, 0x7e, 0x89, 0x9e, 0x65, 0x3a, 0x5c);
static uint8_t gatt_svr_sec_test_static_val;
static int
gatt_svr_chr_access_sec_test(uint16_t conn_handle, uint16_t attr_handle,
struct ble_gatt_access_ctxt *ctxt,
void *arg);
static const struct ble_gatt_svc_def gatt_svr_svcs[] = {
{
/*** Service: Security test. */
.type = BLE_GATT_SVC_TYPE_PRIMARY,
.uuid = &gatt_svr_svc_sec_test_uuid.u,
.characteristics = (struct ble_gatt_chr_def[]) { {
/*** Characteristic: Random number generator. */
.uuid = &gatt_svr_chr_sec_test_rand_uuid.u,
.access_cb = gatt_svr_chr_access_sec_test,
.flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_READ_ENC,
}, {
/*** Characteristic: Static value. */
.uuid = &gatt_svr_chr_sec_test_static_uuid.u,
.access_cb = gatt_svr_chr_access_sec_test,
.flags = BLE_GATT_CHR_F_READ |
BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_WRITE_ENC,
}, {
0, /* No more characteristics in this service. */
} },
},
{
0, /* No more services. */
},
};
static int
gatt_svr_chr_write(struct os_mbuf *om, uint16_t min_len, uint16_t max_len,
void *dst, uint16_t *len)
{
uint16_t om_len;
int rc;
om_len = OS_MBUF_PKTLEN(om);
if (om_len < min_len || om_len > max_len) {
return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
}
rc = ble_hs_mbuf_to_flat(om, dst, max_len, len);
if (rc != 0) {
return BLE_ATT_ERR_UNLIKELY;
}
return 0;
}
static int
gatt_svr_chr_access_sec_test(uint16_t conn_handle, uint16_t attr_handle,
struct ble_gatt_access_ctxt *ctxt,
void *arg)
{
const ble_uuid_t *uuid;
int rand_num;
int rc;
uuid = ctxt->chr->uuid;
/* Determine which characteristic is being accessed by examining its
* 128-bit UUID.
*/
if (ble_uuid_cmp(uuid, &gatt_svr_chr_sec_test_rand_uuid.u) == 0) {
assert(ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR);
/* Respond with a 32-bit random number. */
rand_num = rand();
rc = os_mbuf_append(ctxt->om, &rand_num, sizeof rand_num);
return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
}
if (ble_uuid_cmp(uuid, &gatt_svr_chr_sec_test_static_uuid.u) == 0) {
switch (ctxt->op) {
case BLE_GATT_ACCESS_OP_READ_CHR:
rc = os_mbuf_append(ctxt->om, &gatt_svr_sec_test_static_val,
sizeof gatt_svr_sec_test_static_val);
return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
case BLE_GATT_ACCESS_OP_WRITE_CHR:
rc = gatt_svr_chr_write(ctxt->om,
sizeof gatt_svr_sec_test_static_val,
sizeof gatt_svr_sec_test_static_val,
&gatt_svr_sec_test_static_val, NULL);
return rc;
default:
assert(0);
return BLE_ATT_ERR_UNLIKELY;
}
}
/* Unknown characteristic; the nimble stack should not have called this
* function.
*/
assert(0);
return BLE_ATT_ERR_UNLIKELY;
}
void
gatt_svr_register_cb(struct ble_gatt_register_ctxt *ctxt, void *arg)
{
char buf[BLE_UUID_STR_LEN];
switch (ctxt->op) {
case BLE_GATT_REGISTER_OP_SVC:
MODLOG_DFLT(DEBUG, "registered service %s with handle=%d\n",
ble_uuid_to_str(ctxt->svc.svc_def->uuid, buf),
ctxt->svc.handle);
break;
case BLE_GATT_REGISTER_OP_CHR:
MODLOG_DFLT(DEBUG, "registering characteristic %s with "
"def_handle=%d val_handle=%d\n",
ble_uuid_to_str(ctxt->chr.chr_def->uuid, buf),
ctxt->chr.def_handle,
ctxt->chr.val_handle);
break;
case BLE_GATT_REGISTER_OP_DSC:
MODLOG_DFLT(DEBUG, "registering descriptor %s with handle=%d\n",
ble_uuid_to_str(ctxt->dsc.dsc_def->uuid, buf),
ctxt->dsc.handle);
break;
default:
assert(0);
break;
}
}
int
gatt_svr_init(void)
{
int rc;
rc = ble_gatts_count_cfg(gatt_svr_svcs);
if (rc != 0) {
return rc;
}
rc = ble_gatts_add_svcs(gatt_svr_svcs);
if (rc != 0) {
return rc;
}
return 0;
}

View File

@@ -0,0 +1,363 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
#include <assert.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
#include "os/mynewt.h"
#include "bsp/bsp.h"
#include "hal/hal_gpio.h"
#include "console/console.h"
#include "hal/hal_system.h"
#include "config/config.h"
#include "split/split.h"
#if MYNEWT_VAL(BLE_SVC_DIS_FIRMWARE_REVISION_READ_PERM) >= 0
#include "bootutil/image.h"
#include "imgmgr/imgmgr.h"
#include "services/dis/ble_svc_dis.h"
#endif
/* BLE */
#include "nimble/ble.h"
#include "host/ble_hs.h"
#include "host/util/util.h"
#include "services/gap/ble_svc_gap.h"
/* Application-specified header. */
#include "bleprph.h"
static int bleprph_gap_event(struct ble_gap_event *event, void *arg);
/**
* Logs information about a connection to the console.
*/
static void
bleprph_print_conn_desc(struct ble_gap_conn_desc *desc)
{
MODLOG_DFLT(INFO, "handle=%d our_ota_addr_type=%d our_ota_addr=",
desc->conn_handle, desc->our_ota_addr.type);
print_addr(desc->our_ota_addr.val);
MODLOG_DFLT(INFO, " our_id_addr_type=%d our_id_addr=",
desc->our_id_addr.type);
print_addr(desc->our_id_addr.val);
MODLOG_DFLT(INFO, " peer_ota_addr_type=%d peer_ota_addr=",
desc->peer_ota_addr.type);
print_addr(desc->peer_ota_addr.val);
MODLOG_DFLT(INFO, " peer_id_addr_type=%d peer_id_addr=",
desc->peer_id_addr.type);
print_addr(desc->peer_id_addr.val);
MODLOG_DFLT(INFO, " conn_itvl=%d conn_latency=%d supervision_timeout=%d "
"encrypted=%d authenticated=%d bonded=%d\n",
desc->conn_itvl, desc->conn_latency,
desc->supervision_timeout,
desc->sec_state.encrypted,
desc->sec_state.authenticated,
desc->sec_state.bonded);
}
/**
* Enables advertising with the following parameters:
* o General discoverable mode.
* o Undirected connectable mode.
*/
static void
bleprph_advertise(void)
{
uint8_t own_addr_type;
struct ble_gap_adv_params adv_params;
struct ble_hs_adv_fields fields;
const char *name;
int rc;
/* Figure out address to use while advertising (no privacy for now) */
rc = ble_hs_id_infer_auto(0, &own_addr_type);
if (rc != 0) {
MODLOG_DFLT(ERROR, "error determining address type; rc=%d\n", rc);
return;
}
/**
* Set the advertisement data included in our advertisements:
* o Flags (indicates advertisement type and other general info).
* o Advertising tx power.
* o Device name.
* o 16-bit service UUIDs (alert notifications).
*/
memset(&fields, 0, sizeof fields);
/* Advertise two flags:
* o Discoverability in forthcoming advertisement (general)
* o BLE-only (BR/EDR unsupported).
*/
fields.flags = BLE_HS_ADV_F_DISC_GEN |
BLE_HS_ADV_F_BREDR_UNSUP;
/* Indicate that the TX power level field should be included; have the
* stack fill this value automatically. This is done by assiging the
* special value BLE_HS_ADV_TX_PWR_LVL_AUTO.
*/
fields.tx_pwr_lvl_is_present = 1;
fields.tx_pwr_lvl = BLE_HS_ADV_TX_PWR_LVL_AUTO;
name = ble_svc_gap_device_name();
fields.name = (uint8_t *)name;
fields.name_len = strlen(name);
fields.name_is_complete = 1;
fields.uuids16 = (ble_uuid16_t[]){
BLE_UUID16_INIT(GATT_SVR_SVC_ALERT_UUID)
};
fields.num_uuids16 = 1;
fields.uuids16_is_complete = 1;
rc = ble_gap_adv_set_fields(&fields);
if (rc != 0) {
MODLOG_DFLT(ERROR, "error setting advertisement data; rc=%d\n", rc);
return;
}
/* Begin advertising. */
memset(&adv_params, 0, sizeof adv_params);
adv_params.conn_mode = BLE_GAP_CONN_MODE_UND;
adv_params.disc_mode = BLE_GAP_DISC_MODE_GEN;
rc = ble_gap_adv_start(own_addr_type, NULL, BLE_HS_FOREVER,
&adv_params, bleprph_gap_event, NULL);
if (rc != 0) {
MODLOG_DFLT(ERROR, "error enabling advertisement; rc=%d\n", rc);
return;
}
}
/**
* The nimble host executes this callback when a GAP event occurs. The
* application associates a GAP event callback with each connection that forms.
* bleprph uses the same callback for all connections.
*
* @param event The type of event being signalled.
* @param ctxt Various information pertaining to the event.
* @param arg Application-specified argument; unuesd by
* bleprph.
*
* @return 0 if the application successfully handled the
* event; nonzero on failure. The semantics
* of the return code is specific to the
* particular GAP event being signalled.
*/
static int
bleprph_gap_event(struct ble_gap_event *event, void *arg)
{
struct ble_gap_conn_desc desc;
int rc;
switch (event->type) {
case BLE_GAP_EVENT_CONNECT:
/* A new connection was established or a connection attempt failed. */
MODLOG_DFLT(INFO, "connection %s; status=%d ",
event->connect.status == 0 ? "established" : "failed",
event->connect.status);
if (event->connect.status == 0) {
rc = ble_gap_conn_find(event->connect.conn_handle, &desc);
assert(rc == 0);
bleprph_print_conn_desc(&desc);
#if MYNEWT_VAL(BLEPRPH_LE_PHY_SUPPORT)
phy_conn_changed(event->connect.conn_handle);
#endif
}
MODLOG_DFLT(INFO, "\n");
if (event->connect.status != 0) {
/* Connection failed; resume advertising. */
bleprph_advertise();
}
return 0;
case BLE_GAP_EVENT_DISCONNECT:
MODLOG_DFLT(INFO, "disconnect; reason=%d ", event->disconnect.reason);
bleprph_print_conn_desc(&event->disconnect.conn);
MODLOG_DFLT(INFO, "\n");
#if MYNEWT_VAL(BLEPRPH_LE_PHY_SUPPORT)
phy_conn_changed(CONN_HANDLE_INVALID);
#endif
/* Connection terminated; resume advertising. */
bleprph_advertise();
return 0;
case BLE_GAP_EVENT_CONN_UPDATE:
/* The central has updated the connection parameters. */
MODLOG_DFLT(INFO, "connection updated; status=%d ",
event->conn_update.status);
rc = ble_gap_conn_find(event->connect.conn_handle, &desc);
assert(rc == 0);
bleprph_print_conn_desc(&desc);
MODLOG_DFLT(INFO, "\n");
return 0;
case BLE_GAP_EVENT_ADV_COMPLETE:
MODLOG_DFLT(INFO, "advertise complete; reason=%d",
event->adv_complete.reason);
bleprph_advertise();
return 0;
case BLE_GAP_EVENT_ENC_CHANGE:
/* Encryption has been enabled or disabled for this connection. */
MODLOG_DFLT(INFO, "encryption change event; status=%d ",
event->enc_change.status);
rc = ble_gap_conn_find(event->connect.conn_handle, &desc);
assert(rc == 0);
bleprph_print_conn_desc(&desc);
MODLOG_DFLT(INFO, "\n");
return 0;
case BLE_GAP_EVENT_SUBSCRIBE:
MODLOG_DFLT(INFO, "subscribe event; conn_handle=%d attr_handle=%d "
"reason=%d prevn=%d curn=%d previ=%d curi=%d\n",
event->subscribe.conn_handle,
event->subscribe.attr_handle,
event->subscribe.reason,
event->subscribe.prev_notify,
event->subscribe.cur_notify,
event->subscribe.prev_indicate,
event->subscribe.cur_indicate);
return 0;
case BLE_GAP_EVENT_MTU:
MODLOG_DFLT(INFO, "mtu update event; conn_handle=%d cid=%d mtu=%d\n",
event->mtu.conn_handle,
event->mtu.channel_id,
event->mtu.value);
return 0;
case BLE_GAP_EVENT_REPEAT_PAIRING:
/* We already have a bond with the peer, but it is attempting to
* establish a new secure link. This app sacrifices security for
* convenience: just throw away the old bond and accept the new link.
*/
/* Delete the old bond. */
rc = ble_gap_conn_find(event->repeat_pairing.conn_handle, &desc);
assert(rc == 0);
ble_store_util_delete_peer(&desc.peer_id_addr);
/* Return BLE_GAP_REPEAT_PAIRING_RETRY to indicate that the host should
* continue with the pairing operation.
*/
return BLE_GAP_REPEAT_PAIRING_RETRY;
#if MYNEWT_VAL(BLEPRPH_LE_PHY_SUPPORT)
case BLE_GAP_EVENT_PHY_UPDATE_COMPLETE:
/* XXX: assume symmetric phy for now */
phy_update(event->phy_updated.tx_phy);
return 0;
#endif
}
return 0;
}
static void
bleprph_on_reset(int reason)
{
MODLOG_DFLT(ERROR, "Resetting state; reason=%d\n", reason);
}
static void
bleprph_on_sync(void)
{
int rc;
/* Make sure we have proper identity address set (public preferred) */
rc = ble_hs_util_ensure_addr(0);
assert(rc == 0);
/* Begin advertising. */
bleprph_advertise();
}
/**
* main
*
* The main task for the project. This function initializes the packages,
* then starts serving events from default event queue.
*
* @return int NOTE: this function should never return!
*/
int
main(void)
{
#if MYNEWT_VAL(BLE_SVC_DIS_FIRMWARE_REVISION_READ_PERM) >= 0
struct image_version ver;
static char ver_str[IMGMGR_NMGR_MAX_VER];
#endif
int rc;
/* Initialize OS */
sysinit();
/* Initialize the NimBLE host configuration. */
ble_hs_cfg.reset_cb = bleprph_on_reset;
ble_hs_cfg.sync_cb = bleprph_on_sync;
ble_hs_cfg.gatts_register_cb = gatt_svr_register_cb;
ble_hs_cfg.store_status_cb = ble_store_util_status_rr;
rc = gatt_svr_init();
assert(rc == 0);
/* Set the default device name. */
rc = ble_svc_gap_device_name_set("nimble-bleprph");
assert(rc == 0);
#if MYNEWT_VAL(BLE_SVC_DIS_FIRMWARE_REVISION_READ_PERM) >= 0
/* Set firmware version in DIS */
imgr_my_version(&ver);
imgr_ver_str(&ver, ver_str);
ble_svc_dis_firmware_revision_set(ver_str);
#endif
#if MYNEWT_VAL(BLEPRPH_LE_PHY_SUPPORT)
phy_init();
#endif
conf_load();
/* If this app is acting as the loader in a split image setup, jump into
* the second stage application instead of starting the OS.
*/
#if MYNEWT_VAL(SPLIT_LOADER)
{
void *entry;
rc = split_app_go(&entry, true);
if (rc == 0) {
hal_system_start(entry);
}
}
#endif
/*
* As the last thing, process events from default event queue.
*/
while (1) {
os_eventq_run(os_eventq_dflt_get());
}
return 0;
}

View File

@@ -0,0 +1,43 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
#include "bleprph.h"
/**
* Utility function to log an array of bytes.
*/
void
print_bytes(const uint8_t *bytes, int len)
{
int i;
for (i = 0; i < len; i++) {
MODLOG_DFLT(INFO, "%s0x%02x", i != 0 ? ":" : "", bytes[i]);
}
}
void
print_addr(const void *addr)
{
const uint8_t *u8p;
u8p = addr;
MODLOG_DFLT(INFO, "%02x:%02x:%02x:%02x:%02x:%02x",
u8p[5], u8p[4], u8p[3], u8p[2], u8p[1], u8p[0]);
}

View File

@@ -0,0 +1,128 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
#include "os/mynewt.h"
#include "bsp/bsp.h"
#include "hal/hal_gpio.h"
#include "host/ble_gap.h"
#include "bleprph.h"
#if MYNEWT_VAL(BLEPRPH_LE_PHY_SUPPORT)
static const int button_gpio[4] = MYNEWT_VAL(BLEPRPH_LE_PHY_BUTTON_GPIO);
static const int led_gpio[3] = MYNEWT_VAL(BLEPRPH_LE_PHY_LED_GPIO);
#define PHY_TO_PTR(_mask, _opts) (void *)(((_opts) << 16) | ((_mask)))
#define PTR_TO_PHY_MASK(_ptr) (uint8_t)(((int)_ptr) & 0x0ff)
#define PTR_TO_PHY_OPTS(_ptr) (uint8_t)(((int)_ptr) >> 16)
static struct os_event gpio_event;
static uint16_t conn_handle = CONN_HANDLE_INVALID;
static void
gpio_irq_handler(void *arg)
{
gpio_event.ev_arg = arg;
os_eventq_put(os_eventq_dflt_get(), &gpio_event);
}
static void
gpio_event_handler(struct os_event *ev)
{
uint8_t phy_mask;
uint8_t phy_opts;
int sr;
OS_ENTER_CRITICAL(sr);
phy_mask = PTR_TO_PHY_MASK(ev->ev_arg);
phy_opts = PTR_TO_PHY_OPTS(ev->ev_arg);
OS_EXIT_CRITICAL(sr);
if (conn_handle != CONN_HANDLE_INVALID) {
ble_gap_set_prefered_le_phy(conn_handle, phy_mask, phy_mask, phy_opts);
}
}
static void
setup_button_gpio(int button, uint8_t phy_mask, uint8_t phy_opts)
{
if (button < 0) {
return;
}
hal_gpio_irq_init(button, gpio_irq_handler, PHY_TO_PTR(phy_mask, phy_opts),
HAL_GPIO_TRIG_FALLING, HAL_GPIO_PULL_UP);
hal_gpio_irq_enable(button);
}
void
phy_init(void)
{
gpio_event.ev_cb = gpio_event_handler;
/*
* XXX: we could make this configurable, but for now assume all pins are
* valid, buttons gpio pins are pulled-up and LEDs are active-low - this
* is valid for nRF52840 PDK.
*/
setup_button_gpio(button_gpio[0], BLE_GAP_LE_PHY_1M_MASK,
BLE_GAP_LE_PHY_CODED_ANY);
setup_button_gpio(button_gpio[1], BLE_GAP_LE_PHY_2M_MASK,
BLE_GAP_LE_PHY_CODED_ANY);
setup_button_gpio(button_gpio[2], BLE_GAP_LE_PHY_CODED_MASK,
BLE_GAP_LE_PHY_CODED_S2);
setup_button_gpio(button_gpio[3], BLE_GAP_LE_PHY_CODED_MASK,
BLE_GAP_LE_PHY_CODED_S8);
hal_gpio_init_out(led_gpio[0], 1);
hal_gpio_init_out(led_gpio[1], 1);
hal_gpio_init_out(led_gpio[2], 1);
}
void
phy_conn_changed(uint16_t handle)
{
uint8_t phy = 0;
conn_handle = handle;
if (handle != CONN_HANDLE_INVALID) {
/* XXX: assume symmetric phy for now */
ble_gap_read_le_phy(handle, &phy, &phy);
}
phy_update(phy);
}
void
phy_update(uint8_t phy)
{
if (conn_handle == CONN_HANDLE_INVALID) {
hal_gpio_write(led_gpio[0], 1);
hal_gpio_write(led_gpio[1], 1);
hal_gpio_write(led_gpio[2], 1);
} else {
hal_gpio_write(led_gpio[0], !(phy == BLE_GAP_LE_PHY_1M));
hal_gpio_write(led_gpio[1], !(phy == BLE_GAP_LE_PHY_2M));
hal_gpio_write(led_gpio[2], !(phy == BLE_GAP_LE_PHY_CODED));
}
}
#endif

View File

@@ -0,0 +1,66 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you 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.
#
syscfg.defs:
BLEPRPH_LE_PHY_SUPPORT:
description: >
Enable support for changing PHY preference on active connection.
PHY preference change is triggered by configured GPIO pins.
Current PHY is indicated using LEDs connected to configured
GPIO pins.
value: 0
BLEPRPH_LE_PHY_BUTTON_GPIO:
description: >
GPIO pins for changing PHY preference on active connection. This
is an array of 4 GPIO pin numbers for 1M, 2M, LE Coded S=2 and
LE Coded S=8 respectively.
value: "(int[]){ BUTTON_1, BUTTON_2, BUTTON_3, BUTTON_4 }"
BLEPRPH_LE_PHY_LED_GPIO:
description: >
GPIO pins for indicating current PHY on active connection. This
is an array of 3 GPIO pin numbers for 1M, 2M and LE Coded
respectively.
value: "(int[]){ LED_1, LED_2, LED_3 }"
syscfg.vals:
# Disable central and observer roles.
BLE_ROLE_BROADCASTER: 1
BLE_ROLE_CENTRAL: 0
BLE_ROLE_OBSERVER: 0
BLE_ROLE_PERIPHERAL: 1
# Configure DIS
BLE_SVC_DIS_FIRMWARE_REVISION_READ_PERM: 1
# Log reboot messages to a flash circular buffer.
REBOOT_LOG_FCB: 1
LOG_FCB: 1
CONFIG_FCB: 1
# Enable newtmgr commands.
STATS_NEWTMGR: 1
LOG_NEWTMGR: 1
CONFIG_NEWTMGR: 1
# OS main/default task
OS_MAIN_STACK_SIZE: 468
# Lots of smaller mbufs are required for newtmgr using typical BLE ATT MTU
# values.
MSYS_1_BLOCK_COUNT: 22
MSYS_1_BLOCK_SIZE: 110

View File

@@ -0,0 +1,39 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you 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.
#
pkg.name: apps/btshell
pkg.type: app
pkg.description: Shell application exposing the nimble GAP and GATT.
pkg.author: "Apache Mynewt <dev@mynewt.apache.org>"
pkg.homepage: "http://mynewt.apache.org/"
pkg.keywords:
pkg.deps:
- "@apache-mynewt-core/kernel/os"
- "@apache-mynewt-core/sys/log/full"
- "@apache-mynewt-core/sys/log/modlog"
- "@apache-mynewt-core/sys/stats/full"
- "@apache-mynewt-core/sys/console/full"
- "@apache-mynewt-core/sys/shell"
- nimble/host
- nimble/host/services/gap
- nimble/host/services/gatt
- nimble/host/store/ram
- nimble/transport
pkg.deps.BTSHELL_ANS:
- nimble/host/services/ans

View File

@@ -0,0 +1,207 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 H_BTSHELL_PRIV_
#define H_BTSHELL_PRIV_
#include <inttypes.h>
#include "os/mynewt.h"
#include "nimble/ble.h"
#include "nimble/nimble_opt.h"
#include "modlog/modlog.h"
#include "host/ble_gatt.h"
#include "host/ble_gap.h"
#ifdef __cplusplus
extern "C" {
#endif
struct ble_gap_white_entry;
struct ble_hs_adv_fields;
struct ble_gap_upd_params;
struct ble_gap_conn_params;
struct hci_adv_params;
struct ble_l2cap_sig_update_req;
struct ble_l2cap_sig_update_params;
union ble_store_value;
union ble_store_key;
struct ble_gap_adv_params;
struct ble_gap_conn_desc;
struct ble_gap_disc_params;
struct btshell_dsc {
SLIST_ENTRY(btshell_dsc) next;
struct ble_gatt_dsc dsc;
};
SLIST_HEAD(btshell_dsc_list, btshell_dsc);
struct btshell_chr {
SLIST_ENTRY(btshell_chr) next;
struct ble_gatt_chr chr;
struct btshell_dsc_list dscs;
};
SLIST_HEAD(btshell_chr_list, btshell_chr);
struct btshell_svc {
SLIST_ENTRY(btshell_svc) next;
struct ble_gatt_svc svc;
struct btshell_chr_list chrs;
};
SLIST_HEAD(btshell_svc_list, btshell_svc);
struct btshell_l2cap_coc {
SLIST_ENTRY(btshell_l2cap_coc) next;
struct ble_l2cap_chan *chan;
};
SLIST_HEAD(btshell_l2cap_coc_list, btshell_l2cap_coc);
struct btshell_conn {
uint16_t handle;
struct btshell_svc_list svcs;
struct btshell_l2cap_coc_list coc_list;
};
struct btshell_scan_opts {
uint16_t limit;
uint8_t ignore_legacy:1;
uint8_t periodic_only:1;
};
extern struct btshell_conn btshell_conns[MYNEWT_VAL(BLE_MAX_CONNECTIONS)];
extern int btshell_num_conns;
int btshell_exchange_mtu(uint16_t conn_handle);
int btshell_disc_svcs(uint16_t conn_handle);
int btshell_disc_svc_by_uuid(uint16_t conn_handle, const ble_uuid_t *uuid);
int btshell_disc_all_chrs(uint16_t conn_handle, uint16_t start_handle,
uint16_t end_handle);
int btshell_disc_chrs_by_uuid(uint16_t conn_handle, uint16_t start_handle,
uint16_t end_handle, const ble_uuid_t *uuid);
int btshell_disc_all_dscs(uint16_t conn_handle, uint16_t start_handle,
uint16_t end_handle);
int btshell_disc_full(uint16_t conn_handle);
int btshell_find_inc_svcs(uint16_t conn_handle, uint16_t start_handle,
uint16_t end_handle);
int btshell_read(uint16_t conn_handle, uint16_t attr_handle);
int btshell_read_long(uint16_t conn_handle, uint16_t attr_handle,
uint16_t offset);
int btshell_read_by_uuid(uint16_t conn_handle, uint16_t start_handle,
uint16_t end_handle, const ble_uuid_t *uuid);
int btshell_read_mult(uint16_t conn_handle, uint16_t *attr_handles,
int num_attr_handles);
int btshell_write(uint16_t conn_handle, uint16_t attr_handle,
struct os_mbuf *om);
int btshell_write_no_rsp(uint16_t conn_handle, uint16_t attr_handle,
struct os_mbuf *om);
int btshell_write_long(uint16_t conn_handle, uint16_t attr_handle,
uint16_t offset, struct os_mbuf *om);
int btshell_write_reliable(uint16_t conn_handle,
struct ble_gatt_attr *attrs, int num_attrs);
#if MYNEWT_VAL(BLE_EXT_ADV)
int btshell_ext_adv_configure(uint8_t instance,
const struct ble_gap_ext_adv_params *params,
int8_t *selected_tx_power);
int btshell_ext_adv_start(uint8_t instance, int duration,
int max_events, bool restart);
int btshell_ext_adv_stop(uint8_t instance);
#endif
int btshell_adv_start(uint8_t own_addr_type, const ble_addr_t *direct_addr,
int32_t duration_ms,
const struct ble_gap_adv_params *params,
bool restart);
int btshell_adv_stop(void);
int btshell_conn_initiate(uint8_t own_addr_type, const ble_addr_t *peer_addr,
int32_t duration_ms,
struct ble_gap_conn_params *params);
int btshell_ext_conn_initiate(uint8_t own_addr_type,
const ble_addr_t *peer_addr,
int32_t duration_ms,
struct ble_gap_conn_params *phy_1m_params,
struct ble_gap_conn_params *phy_2m_params,
struct ble_gap_conn_params *phy_coded_params);
int btshell_conn_cancel(void);
int btshell_term_conn(uint16_t conn_handle, uint8_t reason);
int btshell_wl_set(ble_addr_t *addrs, int addrs_count);
int btshell_scan(uint8_t own_addr_type, int32_t duration_ms,
const struct ble_gap_disc_params *disc_params, void *cb_args);
int btshell_ext_scan(uint8_t own_addr_type, uint16_t duration, uint16_t period,
uint8_t filter_duplicates, uint8_t filter_policy,
uint8_t limited,
const struct ble_gap_ext_disc_params *uncoded_params,
const struct ble_gap_ext_disc_params *coded_params,
void *cb_args);
int btshell_scan_cancel(void);
int btshell_update_conn(uint16_t conn_handle,
struct ble_gap_upd_params *params);
void btshell_notify(uint16_t attr_handle);
int btshell_datalen(uint16_t conn_handle, uint16_t tx_octets,
uint16_t tx_time);
int btshell_l2cap_update(uint16_t conn_handle,
struct ble_l2cap_sig_update_params *params);
int btshell_sec_start(uint16_t conn_handle);
int btshell_sec_pair(uint16_t conn_handle);
int btshell_sec_unpair(ble_addr_t *peer_addr);
int btshell_sec_restart(uint16_t conn_handle, uint8_t key_size,
uint8_t *ltk, uint16_t ediv,
uint64_t rand_val, int auth);
int btshell_tx_start(uint16_t conn_handle, uint16_t len, uint16_t rate,
uint16_t num);
void btshell_tx_stop(void);
int btshell_rssi(uint16_t conn_handle, int8_t *out_rssi);
int btshell_l2cap_create_srv(uint16_t psm, int accept_response);
int btshell_l2cap_connect(uint16_t conn, uint16_t psm);
int btshell_l2cap_disconnect(uint16_t conn, uint16_t idx);
int btshell_l2cap_send(uint16_t conn, uint16_t idx, uint16_t bytes);
int btshell_gap_event(struct ble_gap_event *event, void *arg);
void btshell_sync_stats(uint16_t handle);
/** GATT server. */
#define GATT_SVR_SVC_ALERT_UUID 0x1811
#define GATT_SVR_CHR_SUP_NEW_ALERT_CAT_UUID 0x2A47
#define GATT_SVR_CHR_NEW_ALERT 0x2A46
#define GATT_SVR_CHR_SUP_UNR_ALERT_CAT_UUID 0x2A48
#define GATT_SVR_CHR_UNR_ALERT_STAT_UUID 0x2A45
#define GATT_SVR_CHR_ALERT_NOT_CTRL_PT 0x2A44
void gatt_svr_register_cb(struct ble_gatt_register_ctxt *ctxt, void *arg);
int gatt_svr_init(void);
void gatt_svr_print_svcs(void);
/** Misc. */
void print_bytes(const uint8_t *bytes, int len);
void print_mbuf(const struct os_mbuf *om);
void print_addr(const void *addr);
void print_uuid(const ble_uuid_t *uuid);
int svc_is_empty(const struct btshell_svc *svc);
uint16_t chr_end_handle(const struct btshell_svc *svc,
const struct btshell_chr *chr);
int chr_is_empty(const struct btshell_svc *svc, const struct btshell_chr *chr);
void print_conn_desc(const struct ble_gap_conn_desc *desc);
void print_svc(struct btshell_svc *svc);
#ifdef __cplusplus
}
#endif
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,66 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 CMD_H
#define CMD_H
#include <inttypes.h>
#include "host/ble_uuid.h"
struct kv_pair {
char *key;
int val;
};
uint32_t parse_arg_time_dflt(char *name, int step, uint32_t dflt, int *out_status);
const struct kv_pair *parse_kv_find(const struct kv_pair *kvs, char *name);
int parse_arg_find_idx(const char *key);
char *parse_arg_extract(const char *key);
long parse_arg_long_bounds(char *name, long min, long max, int *out_status);
long parse_arg_long_bounds_dflt(char *name, long min, long max,
long dflt, int *out_status);
uint64_t parse_arg_uint64_bounds(char *name, uint64_t min,
uint64_t max, int *out_status);
long parse_arg_long(char *name, int *staus);
uint8_t parse_arg_bool(char *name, int *status);
uint8_t parse_arg_bool_dflt(char *name, uint8_t dflt, int *out_status);
uint8_t parse_arg_uint8(char *name, int *status);
uint8_t parse_arg_uint8_dflt(char *name, uint8_t dflt, int *out_status);
uint16_t parse_arg_uint16(char *name, int *status);
uint16_t parse_arg_uint16_dflt(char *name, uint16_t dflt, int *out_status);
uint32_t parse_arg_uint32(char *name, int *out_status);
uint32_t parse_arg_uint32_dflt(char *name, uint32_t dflt, int *out_status);
uint64_t parse_arg_uint64(char *name, int *out_status);
int parse_arg_kv(char *name, const struct kv_pair *kvs, int *out_status);
int parse_arg_kv_dflt(char *name, const struct kv_pair *kvs, int def_val,
int *out_status);
int parse_arg_byte_stream(char *name, int max_len, uint8_t *dst, int *out_len);
int parse_arg_byte_stream_exact_length(char *name, uint8_t *dst, int len);
int parse_arg_mac(char *name, uint8_t *dst);
int parse_arg_addr(char *name, ble_addr_t *addr);
int parse_arg_uuid(char *name, ble_uuid_any_t *uuid);
int parse_arg_all(int argc, char **argv);
int parse_eddystone_url(char *full_url, uint8_t *out_scheme, char *out_body,
uint8_t *out_body_len, uint8_t *out_suffix);
int cmd_parse_conn_start_end(uint16_t *out_conn, uint16_t *out_start,
uint16_t *out_end);
void cmd_init(void);
#endif

View File

@@ -0,0 +1,587 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
#include <inttypes.h>
#include <errno.h>
#include "bsp/bsp.h"
#include "host/ble_hs_mbuf.h"
#include "host/ble_gap.h"
#include "services/gatt/ble_svc_gatt.h"
#include "console/console.h"
#include "btshell.h"
#include "cmd.h"
#include "cmd_gatt.h"
#define CMD_BUF_SZ 256
static bssnz_t uint8_t cmd_buf[CMD_BUF_SZ];
/*****************************************************************************
* $gatt-discover *
*****************************************************************************/
int
cmd_gatt_discover_characteristic(int argc, char **argv)
{
uint16_t start_handle;
uint16_t conn_handle;
uint16_t end_handle;
ble_uuid_any_t uuid;
int rc;
rc = parse_arg_all(argc - 1, argv + 1);
if (rc != 0) {
return rc;
}
rc = cmd_parse_conn_start_end(&conn_handle, &start_handle, &end_handle);
if (rc != 0) {
console_printf("invalid 'conn start end' parameter\n");
return rc;
}
rc = parse_arg_uuid("uuid", &uuid);
if (rc == 0) {
rc = btshell_disc_chrs_by_uuid(conn_handle, start_handle, end_handle,
&uuid.u);
} else if (rc == ENOENT) {
rc = btshell_disc_all_chrs(conn_handle, start_handle, end_handle);
} else {
console_printf("invalid 'uuid' parameter\n");
return rc;
}
if (rc != 0) {
console_printf("error discovering characteristics; rc=%d\n", rc);
return rc;
}
return 0;
}
int
cmd_gatt_discover_descriptor(int argc, char **argv)
{
uint16_t start_handle;
uint16_t conn_handle;
uint16_t end_handle;
int rc;
rc = parse_arg_all(argc - 1, argv + 1);
if (rc != 0) {
return rc;
}
rc = cmd_parse_conn_start_end(&conn_handle, &start_handle, &end_handle);
if (rc != 0) {
console_printf("invalid 'conn start end' parameter\n");
return rc;
}
rc = btshell_disc_all_dscs(conn_handle, start_handle, end_handle);
if (rc != 0) {
console_printf("error discovering descriptors; rc=%d\n", rc);
return rc;
}
return 0;
}
int
cmd_gatt_discover_service(int argc, char **argv)
{
ble_uuid_any_t uuid;
int conn_handle;
int rc;
rc = parse_arg_all(argc - 1, argv + 1);
if (rc != 0) {
return rc;
}
conn_handle = parse_arg_uint16("conn", &rc);
if (rc != 0) {
console_printf("invalid 'conn' parameter\n");
return rc;
}
rc = parse_arg_uuid("uuid", &uuid);
if (rc == 0) {
rc = btshell_disc_svc_by_uuid(conn_handle, &uuid.u);
} else if (rc == ENOENT) {
rc = btshell_disc_svcs(conn_handle);
} else {
console_printf("invalid 'uuid' parameter\n");
return rc;
}
if (rc != 0) {
console_printf("error discovering services; rc=%d\n", rc);
return rc;
}
return 0;
}
int
cmd_gatt_discover_full(int argc, char **argv)
{
int conn_handle;
int rc;
rc = parse_arg_all(argc - 1, argv + 1);
if (rc != 0) {
return rc;
}
conn_handle = parse_arg_uint16("conn", &rc);
if (rc != 0) {
console_printf("invalid 'conn' parameter\n");
return rc;
}
rc = btshell_disc_full(conn_handle);
if (rc != 0) {
console_printf("error discovering all; rc=%d\n", rc);
return rc;
}
return 0;
}
/*****************************************************************************
* $gatt-exchange-mtu *
*****************************************************************************/
int
cmd_gatt_exchange_mtu(int argc, char **argv)
{
uint16_t conn_handle;
int rc;
rc = parse_arg_all(argc - 1, argv + 1);
if (rc != 0) {
return rc;
}
conn_handle = parse_arg_uint16("conn", &rc);
if (rc != 0) {
console_printf("invalid 'conn' parameter\n");
return rc;
}
rc = btshell_exchange_mtu(conn_handle);
if (rc != 0) {
console_printf("error exchanging mtu; rc=%d\n", rc);
return rc;
}
return 0;
}
/*****************************************************************************
* $gatt-notify *
*****************************************************************************/
int
cmd_gatt_notify(int argc, char **argv)
{
uint16_t attr_handle;
int rc;
rc = parse_arg_all(argc - 1, argv + 1);
if (rc != 0) {
return rc;
}
attr_handle = parse_arg_uint16("attr", &rc);
if (rc != 0) {
console_printf("invalid 'attr' parameter\n");
return rc;
}
btshell_notify(attr_handle);
return 0;
}
/*****************************************************************************
* $gatt-read *
*****************************************************************************/
#define CMD_READ_MAX_ATTRS 8
int
cmd_gatt_read(int argc, char **argv)
{
static uint16_t attr_handles[CMD_READ_MAX_ATTRS];
uint16_t conn_handle;
uint16_t start;
uint16_t end;
uint16_t offset;
ble_uuid_any_t uuid;
uint8_t num_attr_handles;
int is_uuid;
int is_long;
int rc;
rc = parse_arg_all(argc - 1, argv + 1);
if (rc != 0) {
return rc;
}
conn_handle = parse_arg_uint16("conn", &rc);
if (rc != 0) {
console_printf("invalid 'conn' parameter\n");
return rc;
}
is_long = parse_arg_long("long", &rc);
if (rc == ENOENT) {
is_long = 0;
} else if (rc != 0) {
console_printf("invalid 'long' parameter\n");
return rc;
}
for (num_attr_handles = 0;
num_attr_handles < CMD_READ_MAX_ATTRS;
num_attr_handles++) {
attr_handles[num_attr_handles] = parse_arg_uint16("attr", &rc);
if (rc == ENOENT) {
break;
} else if (rc != 0) {
console_printf("invalid 'attr' parameter\n");
return rc;
}
}
rc = parse_arg_uuid("uuid", &uuid);
if (rc == ENOENT) {
is_uuid = 0;
} else if (rc == 0) {
is_uuid = 1;
} else {
console_printf("invalid 'uuid' parameter\n");
return rc;
}
start = parse_arg_uint16("start", &rc);
if (rc == ENOENT) {
start = 0;
} else if (rc != 0) {
console_printf("invalid 'start' parameter\n");
return rc;
}
end = parse_arg_uint16("end", &rc);
if (rc == ENOENT) {
end = 0;
} else if (rc != 0) {
console_printf("invalid 'end' parameter\n");
return rc;
}
offset = parse_arg_uint16("offset", &rc);
if (rc == ENOENT) {
offset = 0;
} else if (rc != 0) {
console_printf("invalid 'offset' parameter\n");
return rc;
}
if (num_attr_handles == 1) {
if (is_long) {
rc = btshell_read_long(conn_handle, attr_handles[0], offset);
} else {
rc = btshell_read(conn_handle, attr_handles[0]);
}
} else if (num_attr_handles > 1) {
rc = btshell_read_mult(conn_handle, attr_handles, num_attr_handles);
} else if (is_uuid) {
if (start == 0 || end == 0) {
rc = EINVAL;
} else {
rc = btshell_read_by_uuid(conn_handle, start, end, &uuid.u);
}
} else {
rc = EINVAL;
}
if (rc != 0) {
console_printf("error reading characteristic; rc=%d\n", rc);
return rc;
}
return 0;
}
/*****************************************************************************
* $gatt-service-changed *
*****************************************************************************/
int
cmd_gatt_service_changed(int argc, char **argv)
{
uint16_t start;
uint16_t end;
int rc;
rc = parse_arg_all(argc - 1, argv + 1);
if (rc != 0) {
return rc;
}
start = parse_arg_uint16("start", &rc);
if (rc != 0) {
console_printf("invalid 'start' parameter\n");
return rc;
}
end = parse_arg_uint16("end", &rc);
if (rc != 0) {
console_printf("invalid 'end' parameter\n");
return rc;
}
ble_svc_gatt_changed(start, end);
return 0;
}
/*****************************************************************************
* $gatt-service-visibility *
*****************************************************************************/
int
cmd_gatt_service_visibility(int argc, char **argv)
{
uint16_t handle;
bool vis;
int rc;
rc = parse_arg_all(argc - 1, argv + 1);
if (rc != 0) {
return rc;
}
handle = parse_arg_uint16("handle", &rc);
if (rc != 0) {
console_printf("invalid 'handle' parameter\n");
return rc;
}
vis = parse_arg_bool("visibility", &rc);
if (rc != 0) {
console_printf("invalid 'visibility' parameter\n");
return rc;
}
ble_gatts_svc_set_visibility(handle, vis);
return 0;
}
/*****************************************************************************
* $gatt-find-included-services *
*****************************************************************************/
int
cmd_gatt_find_included_services(int argc, char **argv)
{
uint16_t start_handle;
uint16_t conn_handle;
uint16_t end_handle;
int rc;
rc = parse_arg_all(argc - 1, argv + 1);
if (rc != 0) {
return rc;
}
rc = cmd_parse_conn_start_end(&conn_handle, &start_handle, &end_handle);
if (rc != 0) {
console_printf("invalid 'conn start end' parameter\n");
return rc;
}
rc = btshell_find_inc_svcs(conn_handle, start_handle, end_handle);
if (rc != 0) {
console_printf("error finding included services; rc=%d\n", rc);
return rc;
}
return 0;
}
/*****************************************************************************
* $gatt-show *
*****************************************************************************/
int
cmd_gatt_show(int argc, char **argv)
{
struct btshell_conn *conn;
struct btshell_svc *svc;
int i;
for (i = 0; i < btshell_num_conns; i++) {
conn = btshell_conns + i;
console_printf("CONNECTION: handle=%d\n", conn->handle);
SLIST_FOREACH(svc, &conn->svcs, next) {
print_svc(svc);
}
}
return 0;
}
int
cmd_gatt_show_local(int argc, char **argv)
{
gatt_svr_print_svcs();
return 0;
}
/*****************************************************************************
* $gatt-write *
*****************************************************************************/
int
cmd_gatt_write(int argc, char **argv)
{
struct ble_gatt_attr attrs[MYNEWT_VAL(BLE_GATT_WRITE_MAX_ATTRS)] = { { 0 } };
uint16_t attr_handle;
uint16_t conn_handle;
uint16_t offset;
int total_attr_len;
int num_attrs;
int attr_len;
int is_long;
int no_rsp;
int rc;
int i;
rc = parse_arg_all(argc - 1, argv + 1);
if (rc != 0) {
return rc;
}
conn_handle = parse_arg_uint16("conn", &rc);
if (rc != 0) {
console_printf("invalid 'conn' parameter\n");
return rc;
}
no_rsp = parse_arg_bool_dflt("no_rsp", 0, &rc);
if (rc != 0) {
console_printf("invalid 'no_rsp' parameter\n");
return rc;
}
is_long = parse_arg_bool_dflt("long", 0, &rc);
if (rc != 0) {
console_printf("invalid 'long' parameter\n");
return rc;
}
total_attr_len = 0;
num_attrs = 0;
while (1) {
attr_handle = parse_arg_uint16("attr", &rc);
if (rc == ENOENT) {
break;
} else if (rc != 0) {
rc = -rc;
console_printf("invalid 'attr' parameter\n");
goto done;
}
rc = parse_arg_byte_stream("value", sizeof cmd_buf - total_attr_len,
cmd_buf + total_attr_len, &attr_len);
if (rc == ENOENT) {
break;
} else if (rc != 0) {
console_printf("invalid 'value' parameter\n");
goto done;
}
offset = parse_arg_uint16("offset", &rc);
if (rc == ENOENT) {
offset = 0;
} else if (rc != 0) {
console_printf("invalid 'offset' parameter\n");
return rc;
}
if (num_attrs >= sizeof attrs / sizeof attrs[0]) {
rc = -EINVAL;
goto done;
}
attrs[num_attrs].handle = attr_handle;
attrs[num_attrs].offset = offset;
attrs[num_attrs].om = ble_hs_mbuf_from_flat(cmd_buf + total_attr_len,
attr_len);
if (attrs[num_attrs].om == NULL) {
goto done;
}
total_attr_len += attr_len;
num_attrs++;
}
if (no_rsp) {
if (num_attrs != 1) {
rc = -EINVAL;
goto done;
}
rc = btshell_write_no_rsp(conn_handle, attrs[0].handle, attrs[0].om);
attrs[0].om = NULL;
} else if (is_long) {
if (num_attrs != 1) {
rc = -EINVAL;
goto done;
}
rc = btshell_write_long(conn_handle, attrs[0].handle,
attrs[0].offset, attrs[0].om);
attrs[0].om = NULL;
} else if (num_attrs > 1) {
rc = btshell_write_reliable(conn_handle, attrs, num_attrs);
} else if (num_attrs == 1) {
rc = btshell_write(conn_handle, attrs[0].handle, attrs[0].om);
attrs[0].om = NULL;
} else {
rc = -EINVAL;
}
done:
for (i = 0; i < sizeof attrs / sizeof attrs[0]; i++) {
os_mbuf_free_chain(attrs[i].om);
}
if (rc != 0) {
console_printf("error writing characteristic; rc=%d\n", rc);
}
return rc;
}

View File

@@ -0,0 +1,39 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 CMD_GATT_H
#define CMD_GATT_H
#include "cmd.h"
int cmd_gatt_discover_characteristic(int argc, char **argv);
int cmd_gatt_discover_descriptor(int argc, char **argv);
int cmd_gatt_discover_service(int argc, char **argv);
int cmd_gatt_discover_full(int argc, char **argv);
int cmd_gatt_find_included_services(int argc, char **argv);
int cmd_gatt_exchange_mtu(int argc, char **argv);
int cmd_gatt_notify(int argc, char **argv);
int cmd_gatt_read(int argc, char **argv);
int cmd_gatt_service_changed(int argc, char **argv);
int cmd_gatt_service_visibility(int argc, char **argv);
int cmd_gatt_show(int argc, char **argv);
int cmd_gatt_show_local(int argc, char **argv);
int cmd_gatt_write(int argc, char **argv);
#endif

View File

@@ -0,0 +1,263 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
#include <inttypes.h>
#include <errno.h>
#include "host/ble_gap.h"
#include "host/ble_l2cap.h"
#include "console/console.h"
#include "btshell.h"
#include "cmd.h"
#include "cmd_l2cap.h"
/*****************************************************************************
* $l2cap-update *
*****************************************************************************/
int
cmd_l2cap_update(int argc, char **argv)
{
struct ble_l2cap_sig_update_params params;
uint16_t conn_handle;
int rc;
rc = parse_arg_all(argc - 1, argv + 1);
if (rc != 0) {
return rc;
}
conn_handle = parse_arg_uint16("conn", &rc);
if (rc != 0) {
console_printf("invalid 'conn' parameter\n");
return rc;
}
params.itvl_min = parse_arg_uint16_dflt("interval_min",
BLE_GAP_INITIAL_CONN_ITVL_MIN,
&rc);
if (rc != 0) {
console_printf("invalid 'interval_min' parameter\n");
return rc;
}
params.itvl_max = parse_arg_uint16_dflt("interval_max",
BLE_GAP_INITIAL_CONN_ITVL_MAX,
&rc);
if (rc != 0) {
console_printf("invalid 'interval_max' parameter\n");
return rc;
}
params.slave_latency = parse_arg_uint16_dflt("latency", 0, &rc);
if (rc != 0) {
console_printf("invalid 'latency' parameter\n");
return rc;
}
params.timeout_multiplier = parse_arg_uint16_dflt("timeout", 0x0100, &rc);
if (rc != 0) {
console_printf("invalid 'timeout' parameter\n");
return rc;
}
rc = btshell_l2cap_update(conn_handle, &params);
if (rc != 0) {
console_printf("error txing l2cap update; rc=%d\n", rc);
return rc;
}
return 0;
}
/*****************************************************************************
* $l2cap-create-server *
*****************************************************************************/
int
cmd_l2cap_create_server(int argc, char **argv)
{
uint16_t psm = 0;
int error;
int accept_response = 0;
int rc;
rc = parse_arg_all(argc - 1, argv + 1);
if (rc != 0) {
return rc;
}
psm = parse_arg_uint16("psm", &rc);
if (rc != 0) {
console_printf("invalid 'psm' parameter\n");
return rc;
}
error = parse_arg_uint32_dflt("error", 0, &rc);
if (rc != 0) {
console_printf("invalid 'error' parameter\n");
return rc;
}
switch (error) {
case 1:
accept_response = BLE_HS_EAUTHEN;
break;
case 2:
accept_response = BLE_HS_EAUTHOR;
break;
case 3:
accept_response = BLE_HS_EENCRYPT_KEY_SZ;
break;
}
rc = btshell_l2cap_create_srv(psm, accept_response);
if (rc) {
console_printf("Server create error: 0x%02x\n", rc);
return rc;
}
console_printf("Server created successfully\n");
return 0;
}
/*****************************************************************************
* $l2cap-connect *
*****************************************************************************/
int
cmd_l2cap_connect(int argc, char **argv)
{
uint16_t conn = 0;
uint16_t psm = 0;
int rc;
rc = parse_arg_all(argc - 1, argv + 1);
if (rc != 0) {
return rc;
}
conn = parse_arg_uint16("conn", &rc);
if (rc != 0) {
console_printf("invalid 'conn' parameter\n");
return rc;
}
psm = parse_arg_uint16("psm", &rc);
if (rc != 0) {
console_printf("invalid 'psm' parameter\n");
return rc;
}
return btshell_l2cap_connect(conn, psm);
}
/*****************************************************************************
* $l2cap-disconnect *
*****************************************************************************/
int
cmd_l2cap_disconnect(int argc, char **argv)
{
uint16_t conn;
uint16_t idx;
int rc;
rc = parse_arg_all(argc - 1, argv + 1);
if (rc != 0) {
return rc;
}
conn = parse_arg_uint16("conn", &rc);
if (rc != 0) {
console_printf("invalid 'conn' parameter\n");
return rc;
}
idx = parse_arg_uint16("idx", &rc);
if (rc != 0) {
console_printf("invalid 'idx' parameter\n");
return rc;
}
return btshell_l2cap_disconnect(conn, idx);
}
/*****************************************************************************
* $l2cap-send *
*****************************************************************************/
int
cmd_l2cap_send(int argc, char **argv)
{
uint16_t conn;
uint16_t idx;
uint16_t bytes;
int rc;
rc = parse_arg_all(argc - 1, argv + 1);
if (rc != 0) {
return rc;
}
conn = parse_arg_uint16("conn", &rc);
if (rc != 0) {
console_printf("invalid 'conn' parameter\n");
return rc;
}
idx = parse_arg_uint16("idx", &rc);
if (rc != 0) {
console_printf("invalid 'idx' parameter\n");
return rc;
}
bytes = parse_arg_uint16("bytes", &rc);
if (rc != 0) {
console_printf("invalid 'bytes' parameter\n");
return rc;
}
return btshell_l2cap_send(conn, idx, bytes);
}
int
cmd_l2cap_show_coc(int argc, char **argv)
{
struct btshell_conn *conn = NULL;
struct btshell_l2cap_coc *coc;
int i, j;
for (i = 0; i < btshell_num_conns; i++) {
conn = btshell_conns + i;
if (SLIST_EMPTY(&conn->coc_list)) {
continue;
}
console_printf("conn_handle: 0x%04x\n", conn->handle);
j = 0;
SLIST_FOREACH(coc, &conn->coc_list, next) {
console_printf(" idx: %i, chan pointer = %p\n", j++, coc->chan);
}
}
return 0;
}

View File

@@ -0,0 +1,32 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 CMD_L2CAP_H
#define CMD_L2CAP_H
#include "cmd.h"
int cmd_l2cap_update(int argc, char **argv);
int cmd_l2cap_create_server(int argc, char **argv);
int cmd_l2cap_connect(int argc, char **argv);
int cmd_l2cap_disconnect(int argc, char **argv);
int cmd_l2cap_send(int argc, char **argv);
int cmd_l2cap_show_coc(int argc, char **argv);
#endif

View File

@@ -0,0 +1,633 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
#include <assert.h>
#include <string.h>
#include "bsp/bsp.h"
#include "console/console.h"
#include "host/ble_hs.h"
#include "host/ble_uuid.h"
#include "host/ble_gatt.h"
#include "btshell.h"
/* 0000xxxx-8c26-476f-89a7-a108033a69c7 */
#define PTS_UUID_DECLARE(uuid16) \
((const ble_uuid_t *) (&(ble_uuid128_t) BLE_UUID128_INIT( \
0xc7, 0x69, 0x3a, 0x03, 0x08, 0xa1, 0xa7, 0x89, \
0x6f, 0x47, 0x26, 0x8c, uuid16, uuid16 >> 8, 0x00, 0x00 \
)))
#define PTS_SVC 0x0001
#define PTS_CHR_READ 0x0002
#define PTS_CHR_WRITE 0x0003
#define PTS_CHR_RELIABLE_WRITE 0x0004
#define PTS_CHR_WRITE_NO_RSP 0x0005
#define PTS_CHR_READ_WRITE 0x0006
#define PTS_CHR_READ_WRITE_ENC 0x0007
#define PTS_CHR_READ_WRITE_AUTHEN 0x0008
#define PTS_DSC_READ 0x0009
#define PTS_DSC_WRITE 0x000a
#define PTS_DSC_READ_WRITE 0x000b
#define PTS_DSC_READ_WRITE_ENC 0x000c
#define PTS_DSC_READ_WRITE_AUTHEN 0x000d
#define PTS_LONG_SVC 0x0011
#define PTS_LONG_CHR_READ 0x0012
#define PTS_LONG_CHR_WRITE 0x0013
#define PTS_LONG_CHR_RELIABLE_WRITE 0x0014
#define PTS_LONG_CHR_READ_WRITE 0x0015
#define PTS_LONG_CHR_READ_WRITE_ALT 0x0016
#define PTS_LONG_CHR_READ_WRITE_ENC 0x0017
#define PTS_LONG_CHR_READ_WRITE_AUTHEN 0x0018
#define PTS_LONG_DSC_READ 0x0019
#define PTS_LONG_DSC_WRITE 0x001a
#define PTS_LONG_DSC_READ_WRITE 0x001b
#define PTS_LONG_DSC_READ_WRITE_ENC 0x001c
#define PTS_LONG_DSC_READ_WRITE_AUTHEN 0x001d
#define PTS_INC_SVC 0x001e
#define PTS_CHR_READ_WRITE_ALT 0x001f
/**
* The vendor specific security test service consists of two characteristics:
* o random-number-generator: generates a random 32-bit number each time
* it is read. This characteristic can only be read over an encrypted
* connection.
* o static-value: a single-byte characteristic that can always be read,
* but can only be written over an encrypted connection.
*/
/* 59462f12-9543-9999-12c8-58b459a2712d */
static const ble_uuid128_t gatt_svr_svc_sec_test_uuid =
BLE_UUID128_INIT(0x2d, 0x71, 0xa2, 0x59, 0xb4, 0x58, 0xc8, 0x12,
0x99, 0x99, 0x43, 0x95, 0x12, 0x2f, 0x46, 0x59);
/* 5c3a659e-897e-45e1-b016-007107c96df6 */
static const ble_uuid128_t gatt_svr_chr_sec_test_rand_uuid =
BLE_UUID128_INIT(0xf6, 0x6d, 0xc9, 0x07, 0x71, 0x00, 0x16, 0xb0,
0xe1, 0x45, 0x7e, 0x89, 0x9e, 0x65, 0x3a, 0x5c);
/* 5c3a659e-897e-45e1-b016-007107c96df7 */
static const ble_uuid128_t gatt_svr_chr_sec_test_static_uuid =
BLE_UUID128_INIT(0xf7, 0x6d, 0xc9, 0x07, 0x71, 0x00, 0x16, 0xb0,
0xe1, 0x45, 0x7e, 0x89, 0x9e, 0x65, 0x3a, 0x5c);
/* 5c3a659e-897e-45e1-b016-007107c96df8 */
static const ble_uuid128_t gatt_svr_chr_sec_test_static_auth_uuid =
BLE_UUID128_INIT(0xf8, 0x6d, 0xc9, 0x07, 0x71, 0x00, 0x16, 0xb0,
0xe1, 0x45, 0x7e, 0x89, 0x9e, 0x65, 0x3a, 0x5c);
static uint8_t gatt_svr_sec_test_static_val;
static uint8_t gatt_svr_pts_static_val;
static uint8_t gatt_svr_pts_static_long_val[30];
static uint8_t gatt_svr_pts_static_long_val_alt[30];
static int
gatt_svr_chr_access_sec_test(uint16_t conn_handle, uint16_t attr_handle,
struct ble_gatt_access_ctxt *ctxt,
void *arg);
static int
gatt_svr_access_test(uint16_t conn_handle, uint16_t attr_handle,
struct ble_gatt_access_ctxt *ctxt,
void *arg);
static int
gatt_svr_long_access_test(uint16_t conn_handle, uint16_t attr_handle,
struct ble_gatt_access_ctxt *ctxt,
void *arg);
static const struct ble_gatt_svc_def gatt_svr_svcs[] = {
{
/*** Service: PTS test. */
.type = BLE_GATT_SVC_TYPE_PRIMARY,
.uuid = PTS_UUID_DECLARE(PTS_SVC),
.characteristics = (struct ble_gatt_chr_def[]) { {
.uuid = PTS_UUID_DECLARE(PTS_CHR_READ),
.access_cb = gatt_svr_access_test,
.flags = BLE_GATT_CHR_F_READ,
}, {
.uuid = PTS_UUID_DECLARE(PTS_CHR_WRITE),
.access_cb = gatt_svr_access_test,
.flags = BLE_GATT_CHR_F_WRITE,
}, {
.uuid = PTS_UUID_DECLARE(PTS_CHR_RELIABLE_WRITE),
.access_cb = gatt_svr_access_test,
.flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_RELIABLE_WRITE,
}, {
.uuid = PTS_UUID_DECLARE(PTS_CHR_WRITE_NO_RSP),
.access_cb = gatt_svr_access_test,
.flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_WRITE_NO_RSP,
}, {
.uuid = PTS_UUID_DECLARE(PTS_CHR_READ_WRITE),
.access_cb = gatt_svr_access_test,
.flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_WRITE,
}, {
.uuid = PTS_UUID_DECLARE(PTS_CHR_READ_WRITE_ENC),
.access_cb = gatt_svr_access_test,
.flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_READ_ENC |
BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_WRITE_ENC,
.min_key_size = 16,
}, {
.uuid = PTS_UUID_DECLARE(PTS_CHR_READ_WRITE_AUTHEN),
.access_cb = gatt_svr_access_test,
.flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_READ_AUTHEN |
BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_WRITE_AUTHEN,
.descriptors = (struct ble_gatt_dsc_def[]){ {
.uuid = PTS_UUID_DECLARE(PTS_DSC_READ),
.access_cb = gatt_svr_access_test,
.att_flags = BLE_ATT_F_READ,
}, {
.uuid = PTS_UUID_DECLARE(PTS_DSC_WRITE),
.access_cb = gatt_svr_access_test,
.att_flags = BLE_ATT_F_WRITE,
}, {
.uuid = PTS_UUID_DECLARE(PTS_DSC_READ_WRITE),
.access_cb = gatt_svr_access_test,
.att_flags = BLE_ATT_F_READ | BLE_ATT_F_WRITE,
}, {
.uuid = PTS_UUID_DECLARE(PTS_DSC_READ_WRITE_ENC),
.access_cb = gatt_svr_access_test,
.att_flags = BLE_ATT_F_READ | BLE_ATT_F_READ_ENC |
BLE_ATT_F_WRITE | BLE_ATT_F_WRITE_ENC,
.min_key_size = 16,
}, {
.uuid = PTS_UUID_DECLARE(PTS_DSC_READ_WRITE_AUTHEN),
.access_cb = gatt_svr_access_test,
.att_flags = BLE_ATT_F_READ | BLE_ATT_F_READ_AUTHEN |
BLE_ATT_F_WRITE | BLE_ATT_F_WRITE_AUTHEN,
}, {
0, /* No more descriptors in this characteristic. */
} }
}, {
0, /* No more characteristics in this service. */
} },
},
{
/*** Service: PTS long test. */
.type = BLE_GATT_SVC_TYPE_PRIMARY,
.uuid = PTS_UUID_DECLARE(PTS_LONG_SVC),
.characteristics = (struct ble_gatt_chr_def[]) { {
.uuid = PTS_UUID_DECLARE(PTS_LONG_CHR_READ),
.access_cb = gatt_svr_long_access_test,
.flags = BLE_GATT_CHR_F_READ,
}, {
.uuid = PTS_UUID_DECLARE(PTS_LONG_CHR_WRITE),
.access_cb = gatt_svr_long_access_test,
.flags = BLE_GATT_CHR_F_WRITE,
}, {
.uuid = PTS_UUID_DECLARE(PTS_LONG_CHR_RELIABLE_WRITE),
.access_cb = gatt_svr_long_access_test,
.flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_RELIABLE_WRITE,
}, {
.uuid = PTS_UUID_DECLARE(PTS_LONG_CHR_READ_WRITE),
.access_cb = gatt_svr_long_access_test,
.flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_WRITE,
}, {
.uuid = PTS_UUID_DECLARE(PTS_LONG_CHR_READ_WRITE_ALT),
.access_cb = gatt_svr_long_access_test,
.flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_WRITE,
}, {
.uuid = PTS_UUID_DECLARE(PTS_LONG_CHR_READ_WRITE_ENC),
.access_cb = gatt_svr_long_access_test,
.flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_READ_ENC |
BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_WRITE_ENC,
.min_key_size = 16,
}, {
.uuid = PTS_UUID_DECLARE(PTS_LONG_CHR_READ_WRITE_AUTHEN),
.access_cb = gatt_svr_long_access_test,
.flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_READ_AUTHEN |
BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_WRITE_AUTHEN,
.descriptors = (struct ble_gatt_dsc_def[]){ {
.uuid = PTS_UUID_DECLARE(PTS_LONG_DSC_READ),
.access_cb = gatt_svr_long_access_test,
.att_flags = BLE_ATT_F_READ,
}, {
.uuid = PTS_UUID_DECLARE(PTS_LONG_DSC_WRITE),
.access_cb = gatt_svr_long_access_test,
.att_flags = BLE_ATT_F_WRITE,
}, {
.uuid = PTS_UUID_DECLARE(PTS_LONG_DSC_READ_WRITE),
.access_cb = gatt_svr_long_access_test,
.att_flags = BLE_ATT_F_READ | BLE_ATT_F_WRITE,
}, {
.uuid = PTS_UUID_DECLARE(PTS_LONG_DSC_READ_WRITE_ENC),
.access_cb = gatt_svr_long_access_test,
.att_flags = BLE_ATT_F_READ | BLE_ATT_F_READ_ENC |
BLE_ATT_F_WRITE | BLE_ATT_F_WRITE_ENC,
.min_key_size = 16,
}, {
.uuid = PTS_UUID_DECLARE(PTS_LONG_DSC_READ_WRITE_AUTHEN),
.access_cb = gatt_svr_long_access_test,
.att_flags = BLE_ATT_F_READ | BLE_ATT_F_READ_AUTHEN |
BLE_ATT_F_WRITE | BLE_ATT_F_WRITE_AUTHEN,
}, {
0, /* No more descriptors in this characteristic. */
} }
}, {
0, /* No more characteristics in this service. */
} },
},
{
/*** Service: Security test. */
.type = BLE_GATT_SVC_TYPE_PRIMARY,
.uuid = &gatt_svr_svc_sec_test_uuid.u,
.characteristics = (struct ble_gatt_chr_def[]) { {
/*** Characteristic: Random number generator. */
.uuid = &gatt_svr_chr_sec_test_rand_uuid.u,
.access_cb = gatt_svr_chr_access_sec_test,
.flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_READ_ENC,
}, {
/*** Characteristic: Static value. */
.uuid = &gatt_svr_chr_sec_test_static_uuid.u,
.access_cb = gatt_svr_chr_access_sec_test,
.flags = BLE_GATT_CHR_F_READ |
BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_WRITE_ENC,
}, {
/*** Characteristic: Static value. */
.uuid = &gatt_svr_chr_sec_test_static_auth_uuid.u,
.access_cb = gatt_svr_chr_access_sec_test,
.flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_READ_AUTHEN,
}, {
0, /* No more characteristics in this service. */
} },
},
{
0, /* No more services. */
},
};
static const struct ble_gatt_svc_def *inc_svcs[] = {
&gatt_svr_svcs[0],
NULL,
};
static const struct ble_gatt_svc_def gatt_svr_inc_svcs[] = {
{
.type = BLE_GATT_SVC_TYPE_PRIMARY,
.uuid = PTS_UUID_DECLARE(PTS_INC_SVC),
.includes = inc_svcs,
.characteristics = (struct ble_gatt_chr_def[]) {{
.uuid = PTS_UUID_DECLARE(PTS_CHR_READ_WRITE_ALT),
.access_cb = gatt_svr_access_test,
.flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_WRITE,
}, {
0, /* No more characteristics */
}, },
},
{
0, /* No more services. */
},
};
static int
gatt_svr_chr_write(struct os_mbuf *om, uint16_t min_len, uint16_t max_len,
void *dst, uint16_t *len)
{
uint16_t om_len;
int rc;
om_len = OS_MBUF_PKTLEN(om);
if (om_len < min_len || om_len > max_len) {
return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
}
rc = ble_hs_mbuf_to_flat(om, dst, max_len, len);
if (rc != 0) {
return BLE_ATT_ERR_UNLIKELY;
}
return 0;
}
static int
gatt_svr_chr_access_sec_test(uint16_t conn_handle, uint16_t attr_handle,
struct ble_gatt_access_ctxt *ctxt,
void *arg)
{
const ble_uuid_t *uuid;
int rand_num;
int rc;
uuid = ctxt->chr->uuid;
/* Determine which characteristic is being accessed by examining its
* 128-bit UUID.
*/
if (ble_uuid_cmp(uuid, &gatt_svr_chr_sec_test_rand_uuid.u) == 0) {
assert(ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR);
/* Respond with a 32-bit random number. */
rand_num = rand();
rc = os_mbuf_append(ctxt->om, &rand_num, sizeof rand_num);
return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
}
if (ble_uuid_cmp(uuid, &gatt_svr_chr_sec_test_static_uuid.u) == 0 ||
ble_uuid_cmp(uuid, &gatt_svr_chr_sec_test_static_auth_uuid.u) == 0) {
switch (ctxt->op) {
case BLE_GATT_ACCESS_OP_READ_CHR:
rc = os_mbuf_append(ctxt->om, &gatt_svr_sec_test_static_val,
sizeof gatt_svr_sec_test_static_val);
return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
case BLE_GATT_ACCESS_OP_WRITE_CHR:
rc = gatt_svr_chr_write(ctxt->om,
sizeof gatt_svr_sec_test_static_val,
sizeof gatt_svr_sec_test_static_val,
&gatt_svr_sec_test_static_val, NULL);
return rc;
default:
assert(0);
return BLE_ATT_ERR_UNLIKELY;
}
}
/* Unknown characteristic; the nimble stack should not have called this
* function.
*/
assert(0);
return BLE_ATT_ERR_UNLIKELY;
}
/* This method is used for PTS testing only, to extract 16 bit value
* from 128 bit vendor specific UUID.
*/
static uint16_t
extract_uuid16_from_pts_uuid128(const ble_uuid_t *uuid)
{
const uint8_t *u8ptr;
uint16_t uuid16;
u8ptr = BLE_UUID128(uuid)->value;
uuid16 = u8ptr[12];
uuid16 |= (uint16_t)u8ptr[13] << 8;
return uuid16;
}
static int
gatt_svr_access_test(uint16_t conn_handle, uint16_t attr_handle,
struct ble_gatt_access_ctxt *ctxt,
void *arg)
{
uint16_t uuid16;
int rc;
uuid16 = extract_uuid16_from_pts_uuid128(ctxt->chr->uuid);
assert(uuid16 != 0);
switch (uuid16) {
case PTS_CHR_READ:
assert(ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR);
rc = os_mbuf_append(ctxt->om, &gatt_svr_pts_static_val,
sizeof gatt_svr_pts_static_val);
return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
case PTS_CHR_WRITE:
case PTS_CHR_RELIABLE_WRITE:
case PTS_CHR_WRITE_NO_RSP:
if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR) {
rc = gatt_svr_chr_write(ctxt->om,0,
sizeof gatt_svr_pts_static_val,
&gatt_svr_pts_static_val, NULL);
return rc;
} else if (ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR) {
rc = os_mbuf_append(ctxt->om, &gatt_svr_pts_static_val,
sizeof gatt_svr_pts_static_val);
return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
}
case PTS_CHR_READ_WRITE:
case PTS_CHR_READ_WRITE_ENC:
case PTS_CHR_READ_WRITE_AUTHEN:
case PTS_CHR_READ_WRITE_ALT:
if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR) {
rc = gatt_svr_chr_write(ctxt->om,0,
sizeof gatt_svr_pts_static_val,
&gatt_svr_pts_static_val, NULL);
return rc;
} else if (ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR) {
rc = os_mbuf_append(ctxt->om, &gatt_svr_pts_static_val,
sizeof gatt_svr_pts_static_val);
return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
}
case PTS_DSC_READ:
assert(ctxt->op == BLE_GATT_ACCESS_OP_READ_DSC);
rc = os_mbuf_append(ctxt->om, &gatt_svr_pts_static_val,
sizeof gatt_svr_pts_static_val);
return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
case PTS_DSC_WRITE:
assert(ctxt->op == BLE_GATT_ACCESS_OP_WRITE_DSC);
rc = gatt_svr_chr_write(ctxt->om,0,
sizeof gatt_svr_pts_static_val,
&gatt_svr_pts_static_val, NULL);
return rc;
case PTS_DSC_READ_WRITE:
case PTS_DSC_READ_WRITE_ENC:
case PTS_DSC_READ_WRITE_AUTHEN:
if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_DSC) {
rc = gatt_svr_chr_write(ctxt->om,0,
sizeof gatt_svr_pts_static_val,
&gatt_svr_pts_static_val, NULL);
return rc;
} else if (ctxt->op == BLE_GATT_ACCESS_OP_READ_DSC) {
rc = os_mbuf_append(ctxt->om, &gatt_svr_pts_static_val,
sizeof gatt_svr_pts_static_val);
return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
}
default:
assert(0);
return BLE_ATT_ERR_UNLIKELY;
}
}
static int
gatt_svr_long_access_test(uint16_t conn_handle, uint16_t attr_handle,
struct ble_gatt_access_ctxt *ctxt,
void *arg)
{
uint16_t uuid16;
int rc;
uuid16 = extract_uuid16_from_pts_uuid128(ctxt->chr->uuid);
assert(uuid16 != 0);
switch (uuid16) {
case PTS_LONG_CHR_READ:
assert(ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR);
rc = os_mbuf_append(ctxt->om, &gatt_svr_pts_static_long_val,
sizeof gatt_svr_pts_static_long_val);
return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
case PTS_LONG_CHR_WRITE:
case PTS_LONG_CHR_RELIABLE_WRITE:
assert(ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR);
rc = gatt_svr_chr_write(ctxt->om,0,
sizeof gatt_svr_pts_static_long_val,
&gatt_svr_pts_static_long_val, NULL);
return rc;
case PTS_LONG_CHR_READ_WRITE:
if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR) {
rc = gatt_svr_chr_write(ctxt->om,0,
sizeof gatt_svr_pts_static_long_val,
&gatt_svr_pts_static_long_val, NULL);
return rc;
} else if (ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR) {
rc = os_mbuf_append(ctxt->om, &gatt_svr_pts_static_long_val,
sizeof gatt_svr_pts_static_long_val);
return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
}
case PTS_LONG_CHR_READ_WRITE_ALT:
if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR) {
rc = gatt_svr_chr_write(ctxt->om,0,
sizeof gatt_svr_pts_static_long_val_alt,
&gatt_svr_pts_static_long_val_alt, NULL);
return rc;
} else if (ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR) {
rc = os_mbuf_append(ctxt->om, &gatt_svr_pts_static_long_val_alt,
sizeof gatt_svr_pts_static_long_val_alt);
return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
}
case PTS_LONG_CHR_READ_WRITE_ENC:
case PTS_LONG_CHR_READ_WRITE_AUTHEN:
if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR) {
rc = gatt_svr_chr_write(ctxt->om,0,
sizeof gatt_svr_pts_static_long_val,
&gatt_svr_pts_static_long_val, NULL);
return rc;
} else if (ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR) {
rc = os_mbuf_append(ctxt->om, &gatt_svr_pts_static_long_val,
sizeof gatt_svr_pts_static_long_val);
return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
}
case PTS_LONG_DSC_READ:
assert(ctxt->op == BLE_GATT_ACCESS_OP_READ_DSC);
rc = os_mbuf_append(ctxt->om, &gatt_svr_pts_static_long_val,
sizeof gatt_svr_pts_static_long_val);
return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
case PTS_LONG_DSC_WRITE:
assert(ctxt->op == BLE_GATT_ACCESS_OP_WRITE_DSC);
rc = gatt_svr_chr_write(ctxt->om,0,
sizeof gatt_svr_pts_static_long_val,
&gatt_svr_pts_static_long_val, NULL);
return rc;
case PTS_LONG_DSC_READ_WRITE:
case PTS_LONG_DSC_READ_WRITE_ENC:
case PTS_LONG_DSC_READ_WRITE_AUTHEN:
if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_DSC) {
rc = gatt_svr_chr_write(ctxt->om,0,
sizeof gatt_svr_pts_static_long_val,
&gatt_svr_pts_static_long_val, NULL);
return rc;
} else if (ctxt->op == BLE_GATT_ACCESS_OP_READ_DSC) {
rc = os_mbuf_append(ctxt->om, &gatt_svr_pts_static_long_val,
sizeof gatt_svr_pts_static_long_val);
return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
}
default:
assert(0);
return BLE_ATT_ERR_UNLIKELY;
}
}
void
gatt_svr_register_cb(struct ble_gatt_register_ctxt *ctxt, void *arg)
{
char buf[BLE_UUID_STR_LEN];
switch (ctxt->op) {
case BLE_GATT_REGISTER_OP_SVC:
MODLOG_DFLT(DEBUG, "registered service %s with handle=%d\n",
ble_uuid_to_str(ctxt->svc.svc_def->uuid, buf),
ctxt->svc.handle);
break;
case BLE_GATT_REGISTER_OP_CHR:
MODLOG_DFLT(DEBUG, "registering characteristic %s with "
"def_handle=%d val_handle=%d\n",
ble_uuid_to_str(ctxt->chr.chr_def->uuid, buf),
ctxt->chr.def_handle,
ctxt->chr.val_handle);
break;
case BLE_GATT_REGISTER_OP_DSC:
MODLOG_DFLT(DEBUG, "registering descriptor %s with handle=%d\n",
ble_uuid_to_str(ctxt->dsc.dsc_def->uuid, buf),
ctxt->dsc.handle);
break;
default:
assert(0);
break;
}
}
void
gatt_svr_print_svcs(void)
{
ble_gatts_show_local();
}
int
gatt_svr_init(void)
{
int rc;
rc = ble_gatts_count_cfg(gatt_svr_svcs);
if (rc != 0) {
return rc;
}
rc = ble_gatts_add_svcs(gatt_svr_svcs);
if (rc != 0) {
return rc;
}
rc = ble_gatts_count_cfg(gatt_svr_inc_svcs);
if (rc != 0) {
return rc;
}
rc = ble_gatts_add_svcs(gatt_svr_inc_svcs);
if (rc != 0) {
return rc;
}
return 0;
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,163 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
#include "console/console.h"
#include "host/ble_uuid.h"
#include "host/ble_gap.h"
#include "btshell.h"
/**
* Utility function to log an array of bytes.
*/
void
print_bytes(const uint8_t *bytes, int len)
{
int i;
for (i = 0; i < len; i++) {
console_printf("%s0x%02x", i != 0 ? ":" : "", bytes[i]);
}
}
void
print_mbuf(const struct os_mbuf *om)
{
int colon;
colon = 0;
while (om != NULL) {
if (colon) {
console_printf(":");
} else {
colon = 1;
}
print_bytes(om->om_data, om->om_len);
om = SLIST_NEXT(om, om_next);
}
}
void
print_addr(const void *addr)
{
const uint8_t *u8p;
u8p = addr;
console_printf("%02x:%02x:%02x:%02x:%02x:%02x",
u8p[5], u8p[4], u8p[3], u8p[2], u8p[1], u8p[0]);
}
void
print_uuid(const ble_uuid_t *uuid)
{
char buf[BLE_UUID_STR_LEN];
ble_uuid_to_str(uuid, buf);
console_printf("%s", buf);
}
int
svc_is_empty(const struct btshell_svc *svc)
{
return svc->svc.end_handle <= svc->svc.start_handle;
}
uint16_t
chr_end_handle(const struct btshell_svc *svc, const struct btshell_chr *chr)
{
const struct btshell_chr *next_chr;
next_chr = SLIST_NEXT(chr, next);
if (next_chr != NULL) {
return next_chr->chr.def_handle - 1;
} else {
return svc->svc.end_handle;
}
}
int
chr_is_empty(const struct btshell_svc *svc, const struct btshell_chr *chr)
{
return chr_end_handle(svc, chr) <= chr->chr.val_handle;
}
void
print_conn_desc(const struct ble_gap_conn_desc *desc)
{
console_printf("handle=%d our_ota_addr_type=%d our_ota_addr=",
desc->conn_handle, desc->our_ota_addr.type);
print_addr(desc->our_ota_addr.val);
console_printf(" our_id_addr_type=%d our_id_addr=",
desc->our_id_addr.type);
print_addr(desc->our_id_addr.val);
console_printf(" peer_ota_addr_type=%d peer_ota_addr=",
desc->peer_ota_addr.type);
print_addr(desc->peer_ota_addr.val);
console_printf(" peer_id_addr_type=%d peer_id_addr=",
desc->peer_id_addr.type);
print_addr(desc->peer_id_addr.val);
console_printf(" conn_itvl=%d conn_latency=%d supervision_timeout=%d"
" key_size=%d encrypted=%d authenticated=%d bonded=%d\n",
desc->conn_itvl, desc->conn_latency,
desc->supervision_timeout,
desc->sec_state.key_size,
desc->sec_state.encrypted,
desc->sec_state.authenticated,
desc->sec_state.bonded);
}
static void
print_dsc(struct btshell_dsc *dsc)
{
console_printf(" dsc_handle=%d uuid=", dsc->dsc.handle);
print_uuid(&dsc->dsc.uuid.u);
console_printf("\n");
}
static void
print_chr(struct btshell_chr *chr)
{
struct btshell_dsc *dsc;
console_printf(" def_handle=%d val_handle=%d properties=0x%02x "
"uuid=", chr->chr.def_handle, chr->chr.val_handle,
chr->chr.properties);
print_uuid(&chr->chr.uuid.u);
console_printf("\n");
SLIST_FOREACH(dsc, &chr->dscs, next) {
print_dsc(dsc);
}
}
void
print_svc(struct btshell_svc *svc)
{
struct btshell_chr *chr;
console_printf(" start=%d end=%d uuid=", svc->svc.start_handle,
svc->svc.end_handle);
print_uuid(&svc->svc.uuid.u);
console_printf("\n");
SLIST_FOREACH(chr, &svc->chrs, next) {
print_chr(chr);
}
}

View File

@@ -0,0 +1,720 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
#include <string.h>
#include <stdlib.h>
#include <limits.h>
#include <stdarg.h>
#include <errno.h>
#include <assert.h>
#include <ctype.h>
#include "console/console.h"
#include "host/ble_hs.h"
#include "host/ble_uuid.h"
#include "host/ble_eddystone.h"
#include "cmd.h"
#include "btshell.h"
#define CMD_MAX_ARGS 16
static char *cmd_args[CMD_MAX_ARGS][2];
static int cmd_num_args;
int
parse_arg_find_idx(const char *key)
{
int i;
for (i = 0; i < cmd_num_args; i++) {
if (strcmp(cmd_args[i][0], key) == 0) {
return i;
}
}
return -1;
}
char *
parse_arg_peek(const char *key)
{
int i;
for (i = 0; i < cmd_num_args; i++) {
if (strcmp(cmd_args[i][0], key) == 0) {
return cmd_args[i][1];
}
}
return NULL;
}
char *
parse_arg_extract(const char *key)
{
int i;
for (i = 0; i < cmd_num_args; i++) {
if (strcmp(cmd_args[i][0], key) == 0) {
/* Erase parameter. */
cmd_args[i][0][0] = '\0';
return cmd_args[i][1];
}
}
return NULL;
}
/**
* Determines which number base to use when parsing the specified numeric
* string. This just avoids base '0' so that numbers don't get interpreted as
* octal.
*/
static int
parse_arg_long_base(char *sval)
{
if (sval[0] == '0' && sval[1] == 'x') {
return 0;
} else {
return 10;
}
}
long
parse_long_bounds(char *sval, long min, long max, int *out_status)
{
char *endptr;
long lval;
lval = strtol(sval, &endptr, parse_arg_long_base(sval));
if (sval[0] != '\0' && *endptr == '\0' &&
lval >= min && lval <= max) {
*out_status = 0;
return lval;
}
*out_status = EINVAL;
return 0;
}
long
parse_arg_long_bounds_peek(char *name, long min, long max, int *out_status)
{
char *sval;
sval = parse_arg_peek(name);
if (sval == NULL) {
*out_status = ENOENT;
return 0;
}
return parse_long_bounds(sval, min, max, out_status);
}
long
parse_arg_long_bounds(char *name, long min, long max, int *out_status)
{
char *sval;
sval = parse_arg_extract(name);
if (sval == NULL) {
*out_status = ENOENT;
return 0;
}
return parse_long_bounds(sval, min, max, out_status);
}
long
parse_arg_long_bounds_dflt(char *name, long min, long max,
long dflt, int *out_status)
{
long val;
int rc;
val = parse_arg_long_bounds(name, min, max, &rc);
if (rc == ENOENT) {
rc = 0;
val = dflt;
}
*out_status = rc;
return val;
}
uint64_t
parse_arg_uint64_bounds(char *name, uint64_t min, uint64_t max, int *out_status)
{
char *endptr;
char *sval;
uint64_t lval;
sval = parse_arg_extract(name);
if (sval == NULL) {
*out_status = ENOENT;
return 0;
}
lval = strtoull(sval, &endptr, parse_arg_long_base(sval));
if (sval[0] != '\0' && *endptr == '\0' &&
lval >= min && lval <= max) {
*out_status = 0;
return lval;
}
*out_status = EINVAL;
return 0;
}
long
parse_arg_long(char *name, int *out_status)
{
return parse_arg_long_bounds(name, LONG_MIN, LONG_MAX, out_status);
}
uint8_t
parse_arg_bool(char *name, int *out_status)
{
return parse_arg_long_bounds(name, 0, 1, out_status);
}
uint8_t
parse_arg_bool_dflt(char *name, uint8_t dflt, int *out_status)
{
return parse_arg_long_bounds_dflt(name, 0, 1, dflt, out_status);
}
uint8_t
parse_arg_uint8(char *name, int *out_status)
{
return parse_arg_long_bounds(name, 0, UINT8_MAX, out_status);
}
uint16_t
parse_arg_uint16(char *name, int *out_status)
{
return parse_arg_long_bounds(name, 0, UINT16_MAX, out_status);
}
uint16_t
parse_arg_uint16_peek(char *name, int *out_status)
{
return parse_arg_long_bounds_peek(name, 0, UINT16_MAX, out_status);
}
uint32_t
parse_arg_uint32(char *name, int *out_status)
{
return parse_arg_uint64_bounds(name, 0, UINT32_MAX, out_status);
}
uint64_t
parse_arg_uint64(char *name, int *out_status)
{
return parse_arg_uint64_bounds(name, 0, UINT64_MAX, out_status);
}
uint8_t
parse_arg_uint8_dflt(char *name, uint8_t dflt, int *out_status)
{
uint8_t val;
int rc;
val = parse_arg_uint8(name, &rc);
if (rc == ENOENT) {
val = dflt;
rc = 0;
}
*out_status = rc;
return val;
}
uint16_t
parse_arg_uint16_dflt(char *name, uint16_t dflt, int *out_status)
{
uint16_t val;
int rc;
val = parse_arg_uint16(name, &rc);
if (rc == ENOENT) {
val = dflt;
rc = 0;
}
*out_status = rc;
return val;
}
uint32_t
parse_arg_uint32_dflt(char *name, uint32_t dflt, int *out_status)
{
uint32_t val;
int rc;
val = parse_arg_uint32(name, &rc);
if (rc == ENOENT) {
val = dflt;
rc = 0;
}
*out_status = rc;
return val;
}
static uint32_t
parse_time_unit_mult(const char *str)
{
if (!strcasecmp(str, "us")) {
return 1;
} else if (!strcasecmp(str, "ms")) {
return 1000;
} else if (!strcasecmp(str, "s")) {
return 1000000;
}
return 0;
}
static uint32_t
parse_time_us(const char *str, int *out_status)
{
uint32_t val = 0;
uint32_t val_div = 1;
uint32_t val_mult = 1;
uint32_t val_us;
while (isdigit(*str)) {
val *= 10;
val += *str - '0';
str++;
}
if (*str == '.') {
str++;
while (isdigit(*str)) {
val *= 10;
val += *str - '0';
val_div *= 10;
str++;
}
}
val_mult = parse_time_unit_mult(str);
if (val_mult == 0) {
*out_status = EINVAL;
return 0;
}
if (val_mult > val_div) {
val_us = val * (val_mult / val_div);
} else {
val_us = val * (val_div / val_mult);
}
*out_status = 0;
return val_us;
}
uint32_t
parse_arg_time_dflt(char *name, int step_us, uint32_t dflt, int *out_status)
{
const char *arg;
uint32_t val;
int rc;
arg = parse_arg_peek(name);
if (!arg) {
*out_status = 0;
return dflt;
}
val = parse_time_us(arg, &rc);
if (rc) {
val = parse_arg_uint32(name, &rc);
if (rc == ENOENT) {
*out_status = 0;
return dflt;
}
} else {
val /= step_us;
parse_arg_extract(name);
}
*out_status = rc;
return val;
}
const struct kv_pair *
parse_kv_find(const struct kv_pair *kvs, char *name)
{
const struct kv_pair *kv;
int i;
for (i = 0; kvs[i].key != NULL; i++) {
kv = kvs + i;
if (strcmp(name, kv->key) == 0) {
return kv;
}
}
return NULL;
}
int
parse_arg_kv(char *name, const struct kv_pair *kvs, int *out_status)
{
const struct kv_pair *kv;
char *sval;
sval = parse_arg_extract(name);
if (sval == NULL) {
*out_status = ENOENT;
return -1;
}
kv = parse_kv_find(kvs, sval);
if (kv == NULL) {
*out_status = EINVAL;
return -1;
}
*out_status = 0;
return kv->val;
}
int
parse_arg_kv_dflt(char *name, const struct kv_pair *kvs, int def_val,
int *out_status)
{
int val;
int rc;
val = parse_arg_kv(name, kvs, &rc);
if (rc == ENOENT) {
rc = 0;
val = def_val;
}
*out_status = rc;
return val;
}
static int
parse_arg_byte_stream_delim(char *sval, char *delims, int max_len,
uint8_t *dst, int *out_len)
{
unsigned long ul;
char *endptr;
char *token;
int i;
i = 0;
for (token = strtok(sval, delims);
token != NULL;
token = strtok(NULL, delims)) {
if (i >= max_len) {
return EINVAL;
}
ul = strtoul(token, &endptr, 16);
if (sval[0] == '\0' || *endptr != '\0' || ul > UINT8_MAX) {
return -1;
}
dst[i] = ul;
i++;
}
*out_len = i;
return 0;
}
int
parse_arg_byte_stream(char *name, int max_len, uint8_t *dst, int *out_len)
{
char *sval;
sval = parse_arg_extract(name);
if (sval == NULL) {
return ENOENT;
}
return parse_arg_byte_stream_delim(sval, ":-", max_len, dst, out_len);
}
int
parse_arg_byte_stream_exact_length(char *name, uint8_t *dst, int len)
{
int actual_len;
int rc;
rc = parse_arg_byte_stream(name, len, dst, &actual_len);
if (rc != 0) {
return rc;
}
if (actual_len != len) {
return EINVAL;
}
return 0;
}
static void
parse_reverse_bytes(uint8_t *bytes, int len)
{
uint8_t tmp;
int i;
for (i = 0; i < len / 2; i++) {
tmp = bytes[i];
bytes[i] = bytes[len - i - 1];
bytes[len - i - 1] = tmp;
}
}
int
parse_arg_mac(char *name, uint8_t *dst)
{
int rc;
rc = parse_arg_byte_stream_exact_length(name, dst, 6);
if (rc != 0) {
return rc;
}
parse_reverse_bytes(dst, 6);
return 0;
}
int
parse_arg_addr(char *name, ble_addr_t *addr)
{
char *arg;
size_t len;
uint8_t addr_type;
bool addr_type_found;
int rc;
arg = parse_arg_peek(name);
if (!arg) {
return ENOENT;
}
len = strlen(arg);
if (len < 2) {
return EINVAL;
}
addr_type_found = false;
if ((arg[len - 2] == ':') || (arg[len - 2] == '-')) {
if (tolower(arg[len - 1]) == 'p') {
addr_type = BLE_ADDR_PUBLIC;
addr_type_found = true;
} else if (tolower(arg[len - 1]) == 'r') {
addr_type = BLE_ADDR_RANDOM;
addr_type_found = true;
}
if (addr_type_found) {
arg[len - 2] = '\0';
}
}
rc = parse_arg_mac(name, addr->val);
if (rc != 0) {
return rc;
}
if (addr_type_found) {
addr->type = addr_type;
} else {
rc = EAGAIN;
}
return rc;
}
int
parse_arg_uuid(char *str, ble_uuid_any_t *uuid)
{
uint16_t uuid16;
uint8_t val[16];
int len;
int rc;
uuid16 = parse_arg_uint16_peek(str, &rc);
switch (rc) {
case ENOENT:
parse_arg_extract(str);
return ENOENT;
case 0:
len = 2;
val[0] = uuid16;
val[1] = uuid16 >> 8;
parse_arg_extract(str);
break;
default:
len = 16;
rc = parse_arg_byte_stream_exact_length(str, val, 16);
if (rc != 0) {
return EINVAL;
}
parse_reverse_bytes(val, 16);
break;
}
rc = ble_uuid_init_from_buf(uuid, val, len);
if (rc != 0) {
return EINVAL;
} else {
return 0;
}
}
int
parse_arg_all(int argc, char **argv)
{
char *key;
char *val;
int i;
cmd_num_args = 0;
for (i = 0; i < argc; i++) {
key = strtok(argv[i], "=");
val = strtok(NULL, "=");
if (key != NULL && val != NULL) {
if (strlen(key) == 0) {
console_printf("Error: invalid argument: %s\n", argv[i]);
return -1;
}
if (cmd_num_args >= CMD_MAX_ARGS) {
console_printf("Error: too many arguments");
return -1;
}
cmd_args[cmd_num_args][0] = key;
cmd_args[cmd_num_args][1] = val;
cmd_num_args++;
}
}
return 0;
}
int
parse_eddystone_url(char *full_url, uint8_t *out_scheme, char *out_body,
uint8_t *out_body_len, uint8_t *out_suffix)
{
static const struct {
char *s;
uint8_t scheme;
} schemes[] = {
{ "http://www.", BLE_EDDYSTONE_URL_SCHEME_HTTP_WWW },
{ "https://www.", BLE_EDDYSTONE_URL_SCHEME_HTTPS_WWW },
{ "http://", BLE_EDDYSTONE_URL_SCHEME_HTTP },
{ "https://", BLE_EDDYSTONE_URL_SCHEME_HTTPS },
};
static const struct {
char *s;
uint8_t code;
} suffixes[] = {
{ ".com/", BLE_EDDYSTONE_URL_SUFFIX_COM_SLASH },
{ ".org/", BLE_EDDYSTONE_URL_SUFFIX_ORG_SLASH },
{ ".edu/", BLE_EDDYSTONE_URL_SUFFIX_EDU_SLASH },
{ ".net/", BLE_EDDYSTONE_URL_SUFFIX_NET_SLASH },
{ ".info/", BLE_EDDYSTONE_URL_SUFFIX_INFO_SLASH },
{ ".biz/", BLE_EDDYSTONE_URL_SUFFIX_BIZ_SLASH },
{ ".gov/", BLE_EDDYSTONE_URL_SUFFIX_GOV_SLASH },
{ ".com", BLE_EDDYSTONE_URL_SUFFIX_COM },
{ ".org", BLE_EDDYSTONE_URL_SUFFIX_ORG },
{ ".edu", BLE_EDDYSTONE_URL_SUFFIX_EDU },
{ ".net", BLE_EDDYSTONE_URL_SUFFIX_NET },
{ ".info", BLE_EDDYSTONE_URL_SUFFIX_INFO },
{ ".biz", BLE_EDDYSTONE_URL_SUFFIX_BIZ },
{ ".gov", BLE_EDDYSTONE_URL_SUFFIX_GOV },
};
char *prefix;
char *suffix;
int full_url_len;
int prefix_len;
int suffix_len;
int suffix_idx;
int rc;
int i;
full_url_len = strlen(full_url);
rc = BLE_HS_EINVAL;
for (i = 0; i < sizeof schemes / sizeof schemes[0]; i++) {
prefix = schemes[i].s;
prefix_len = strlen(schemes[i].s);
if (full_url_len >= prefix_len &&
memcmp(full_url, prefix, prefix_len) == 0) {
*out_scheme = i;
rc = 0;
break;
}
}
if (rc != 0) {
return rc;
}
rc = BLE_HS_EINVAL;
for (i = 0; i < sizeof suffixes / sizeof suffixes[0]; i++) {
suffix = suffixes[i].s;
suffix_len = strlen(suffixes[i].s);
suffix_idx = full_url_len - suffix_len;
if (suffix_idx >= prefix_len &&
memcmp(full_url + suffix_idx, suffix, suffix_len) == 0) {
*out_suffix = i;
rc = 0;
break;
}
}
if (rc != 0) {
*out_suffix = BLE_EDDYSTONE_URL_SUFFIX_NONE;
*out_body_len = full_url_len - prefix_len;
} else {
*out_body_len = full_url_len - prefix_len - suffix_len;
}
memcpy(out_body, full_url + prefix_len, *out_body_len);
return 0;
}

View File

@@ -0,0 +1,42 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you 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.
#
syscfg.defs:
BTSHELL_ANS:
description: Include support for the alert notification service.
value: 1
syscfg.vals:
# Enable the shell task.
SHELL_TASK: 1
# Set log level to info (disable debug logging).
LOG_LEVEL: 1
# Disable security manager (pairing and bonding).
BLE_SM_LEGACY: 0
BLE_SM_SC: 0
# Default task settings
OS_MAIN_STACK_SIZE: 512
# Newtmgr is not supported in this app, so disable newtmgr-over-shell.
SHELL_NEWTMGR: 0
syscfg.vals.BLE_MESH:
MSYS_1_BLOCK_COUNT: 16

View File

@@ -0,0 +1,14 @@
Title: Bluetooth tester application
Description:
Tester application uses binary protocol to control Mynewt Nimble stack
and is aimed at automated testing. It uses Bluetooth Testing Protocol (BTP)
to drive Bluetooth stack. BTP commands and events are received and buffered for
further processing.
--------------------------------------------------------------------------------
Supported Profiles:
GAP, GATT, SM, L2CAP, MESH
--------------------------------------------------------------------------------

View File

@@ -0,0 +1,42 @@
#
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you 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.
#
pkg.name: apps/bttester
pkg.type: app
pkg.description: Bluetooth tester application
pkg.author: "Apache Mynewt <dev@mynewt.apache.org>"
pkg.homepage: "http://mynewt.apache.org/"
pkg.keywords:
pkg.deps:
- "@apache-mynewt-core/kernel/os"
- "@apache-mynewt-core/sys/console/full"
- "@apache-mynewt-core/sys/log/full"
- "@apache-mynewt-core/sys/log/modlog"
- "@apache-mynewt-core/sys/stats/full"
- "@apache-mynewt-core/sys/shell"
- "@apache-mynewt-nimble/nimble/controller"
- "@apache-mynewt-nimble/nimble/host"
- "@apache-mynewt-nimble/nimble/host/services/gap"
- "@apache-mynewt-nimble/nimble/host/services/gatt"
- "@apache-mynewt-nimble/nimble/host/store/ram"
- "@apache-mynewt-nimble/nimble/transport/ram"
- "@apache-mynewt-core/hw/drivers/uart"
- "@apache-mynewt-core/hw/drivers/rtt"

View File

@@ -0,0 +1,405 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
/* atomic operations */
/*
* Copyright (c) 1997-2015, Wind River Systems, Inc.
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef __ATOMIC_H__
#define __ATOMIC_H__
#ifdef __cplusplus
extern "C"
{
#endif
typedef int atomic_t;
typedef atomic_t atomic_val_t;
/**
* @defgroup atomic_apis Atomic Services APIs
* @ingroup kernel_apis
* @{
*/
/**
* @brief Atomic compare-and-set.
*
* This routine performs an atomic compare-and-set on @a target. If the current
* value of @a target equals @a old_value, @a target is set to @a new_value.
* If the current value of @a target does not equal @a old_value, @a target
* is left unchanged.
*
* @param target Address of atomic variable.
* @param old_value Original value to compare against.
* @param new_value New value to store.
* @return 1 if @a new_value is written, 0 otherwise.
*/
static inline int atomic_cas(atomic_t *target, atomic_val_t old_value,
atomic_val_t new_value)
{
return __atomic_compare_exchange_n(target, &old_value, new_value,
0, __ATOMIC_SEQ_CST,
__ATOMIC_SEQ_CST);
}
/**
*
* @brief Atomic addition.
*
* This routine performs an atomic addition on @a target.
*
* @param target Address of atomic variable.
* @param value Value to add.
*
* @return Previous value of @a target.
*/
static inline atomic_val_t atomic_add(atomic_t *target, atomic_val_t value)
{
return __atomic_fetch_add(target, value, __ATOMIC_SEQ_CST);
}
/**
*
* @brief Atomic subtraction.
*
* This routine performs an atomic subtraction on @a target.
*
* @param target Address of atomic variable.
* @param value Value to subtract.
*
* @return Previous value of @a target.
*/
static inline atomic_val_t atomic_sub(atomic_t *target, atomic_val_t value)
{
return __atomic_fetch_sub(target, value, __ATOMIC_SEQ_CST);
}
/**
*
* @brief Atomic increment.
*
* This routine performs an atomic increment by 1 on @a target.
*
* @param target Address of atomic variable.
*
* @return Previous value of @a target.
*/
static inline atomic_val_t atomic_inc(atomic_t *target)
{
return atomic_add(target, 1);
}
/**
*
* @brief Atomic decrement.
*
* This routine performs an atomic decrement by 1 on @a target.
*
* @param target Address of atomic variable.
*
* @return Previous value of @a target.
*/
static inline atomic_val_t atomic_dec(atomic_t *target)
{
return atomic_sub(target, 1);
}
/**
*
* @brief Atomic get.
*
* This routine performs an atomic read on @a target.
*
* @param target Address of atomic variable.
*
* @return Value of @a target.
*/
static inline atomic_val_t atomic_get(const atomic_t *target)
{
return __atomic_load_n(target, __ATOMIC_SEQ_CST);
}
/**
*
* @brief Atomic get-and-set.
*
* This routine atomically sets @a target to @a value and returns
* the previous value of @a target.
*
* @param target Address of atomic variable.
* @param value Value to write to @a target.
*
* @return Previous value of @a target.
*/
static inline atomic_val_t atomic_set(atomic_t *target, atomic_val_t value)
{
/* This builtin, as described by Intel, is not a traditional
* test-and-set operation, but rather an atomic exchange operation. It
* writes value into *ptr, and returns the previous contents of *ptr.
*/
return __atomic_exchange_n(target, value, __ATOMIC_SEQ_CST);
}
/**
*
* @brief Atomic clear.
*
* This routine atomically sets @a target to zero and returns its previous
* value. (Hence, it is equivalent to atomic_set(target, 0).)
*
* @param target Address of atomic variable.
*
* @return Previous value of @a target.
*/
static inline atomic_val_t atomic_clear(atomic_t *target)
{
return atomic_set(target, 0);
}
/**
*
* @brief Atomic bitwise inclusive OR.
*
* This routine atomically sets @a target to the bitwise inclusive OR of
* @a target and @a value.
*
* @param target Address of atomic variable.
* @param value Value to OR.
*
* @return Previous value of @a target.
*/
static inline atomic_val_t atomic_or(atomic_t *target, atomic_val_t value)
{
return __atomic_fetch_or(target, value, __ATOMIC_SEQ_CST);
}
/**
*
* @brief Atomic bitwise exclusive OR (XOR).
*
* This routine atomically sets @a target to the bitwise exclusive OR (XOR) of
* @a target and @a value.
*
* @param target Address of atomic variable.
* @param value Value to XOR
*
* @return Previous value of @a target.
*/
static inline atomic_val_t atomic_xor(atomic_t *target, atomic_val_t value)
{
return __atomic_fetch_xor(target, value, __ATOMIC_SEQ_CST);
}
/**
*
* @brief Atomic bitwise AND.
*
* This routine atomically sets @a target to the bitwise AND of @a target
* and @a value.
*
* @param target Address of atomic variable.
* @param value Value to AND.
*
* @return Previous value of @a target.
*/
static inline atomic_val_t atomic_and(atomic_t *target, atomic_val_t value)
{
return __atomic_fetch_and(target, value, __ATOMIC_SEQ_CST);
}
/**
*
* @brief Atomic bitwise NAND.
*
* This routine atomically sets @a target to the bitwise NAND of @a target
* and @a value. (This operation is equivalent to target = ~(target & value).)
*
* @param target Address of atomic variable.
* @param value Value to NAND.
*
* @return Previous value of @a target.
*/
static inline atomic_val_t atomic_nand(atomic_t *target, atomic_val_t value)
{
return __atomic_fetch_nand(target, value, __ATOMIC_SEQ_CST);
}
/**
* @brief Initialize an atomic variable.
*
* This macro can be used to initialize an atomic variable. For example,
* @code atomic_t my_var = ATOMIC_INIT(75); @endcode
*
* @param i Value to assign to atomic variable.
*/
#define ATOMIC_INIT(i) (i)
/**
* @cond INTERNAL_HIDDEN
*/
#define ATOMIC_BITS (sizeof(atomic_val_t) * 8)
#define ATOMIC_MASK(bit) (1 << ((bit) & (ATOMIC_BITS - 1)))
#define ATOMIC_ELEM(addr, bit) ((addr) + ((bit) / ATOMIC_BITS))
/**
* INTERNAL_HIDDEN @endcond
*/
/**
* @brief Define an array of atomic variables.
*
* This macro defines an array of atomic variables containing at least
* @a num_bits bits.
*
* @note
* If used from file scope, the bits of the array are initialized to zero;
* if used from within a function, the bits are left uninitialized.
*
* @param name Name of array of atomic variables.
* @param num_bits Number of bits needed.
*/
#define ATOMIC_DEFINE(name, num_bits) \
atomic_t name[1 + ((num_bits) - 1) / ATOMIC_BITS]
/**
* @brief Atomically test a bit.
*
* This routine tests whether bit number @a bit of @a target is set or not.
* The target may be a single atomic variable or an array of them.
*
* @param target Address of atomic variable or array.
* @param bit Bit number (starting from 0).
*
* @return 1 if the bit was set, 0 if it wasn't.
*/
static inline int
atomic_test_bit(const atomic_t *target, int bit)
{
atomic_val_t val = atomic_get(ATOMIC_ELEM(target, bit));
return (1 & (val >> (bit & (ATOMIC_BITS - 1))));
}
/**
* @brief Atomically test and clear a bit.
*
* Atomically clear bit number @a bit of @a target and return its old value.
* The target may be a single atomic variable or an array of them.
*
* @param target Address of atomic variable or array.
* @param bit Bit number (starting from 0).
*
* @return 1 if the bit was set, 0 if it wasn't.
*/
static inline int
atomic_test_and_clear_bit(atomic_t *target, int bit)
{
atomic_val_t mask = ATOMIC_MASK(bit);
atomic_val_t old;
old = atomic_and(ATOMIC_ELEM(target, bit), ~mask);
return (old & mask) != 0;
}
/**
* @brief Atomically set a bit.
*
* Atomically set bit number @a bit of @a target and return its old value.
* The target may be a single atomic variable or an array of them.
*
* @param target Address of atomic variable or array.
* @param bit Bit number (starting from 0).
*
* @return 1 if the bit was set, 0 if it wasn't.
*/
static inline int
atomic_test_and_set_bit(atomic_t *target, int bit)
{
atomic_val_t mask = ATOMIC_MASK(bit);
atomic_val_t old;
old = atomic_or(ATOMIC_ELEM(target, bit), mask);
return (old & mask) != 0;
}
/**
* @brief Atomically clear a bit.
*
* Atomically clear bit number @a bit of @a target.
* The target may be a single atomic variable or an array of them.
*
* @param target Address of atomic variable or array.
* @param bit Bit number (starting from 0).
*
* @return N/A
*/
static inline void
atomic_clear_bit(atomic_t *target, int bit)
{
atomic_val_t mask = ATOMIC_MASK(bit);
atomic_and(ATOMIC_ELEM(target, bit), ~mask);
}
/**
* @brief Atomically set a bit.
*
* Atomically set bit number @a bit of @a target.
* The target may be a single atomic variable or an array of them.
*
* @param target Address of atomic variable or array.
* @param bit Bit number (starting from 0).
*
* @return N/A
*/
static inline void
atomic_set_bit(atomic_t *target, int bit)
{
atomic_val_t mask = ATOMIC_MASK(bit);
atomic_or(ATOMIC_ELEM(target, bit), mask);
}
/**
* @}
*/
#ifdef __cplusplus
}
#endif
#endif /* __ATOMIC_H__ */

View File

@@ -0,0 +1,359 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
/* bttester.c - Bluetooth Tester */
/*
* Copyright (c) 2015-2016 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "syscfg/syscfg.h"
#include "console/console.h"
#include "bttester_pipe.h"
#include "bttester.h"
#define CMD_QUEUED 2
static struct os_eventq avail_queue;
static struct os_eventq *cmds_queue;
static struct os_event bttester_ev[CMD_QUEUED];
struct btp_buf {
struct os_event *ev;
union {
u8_t data[BTP_MTU];
struct btp_hdr hdr;
};
};
static struct btp_buf cmd_buf[CMD_QUEUED];
static void supported_commands(u8_t *data, u16_t len)
{
u8_t buf[1];
struct core_read_supported_commands_rp *rp = (void *) buf;
memset(buf, 0, sizeof(buf));
tester_set_bit(buf, CORE_READ_SUPPORTED_COMMANDS);
tester_set_bit(buf, CORE_READ_SUPPORTED_SERVICES);
tester_set_bit(buf, CORE_REGISTER_SERVICE);
tester_set_bit(buf, CORE_UNREGISTER_SERVICE);
tester_send(BTP_SERVICE_ID_CORE, CORE_READ_SUPPORTED_COMMANDS,
BTP_INDEX_NONE, (u8_t *) rp, sizeof(buf));
}
static void supported_services(u8_t *data, u16_t len)
{
u8_t buf[1];
struct core_read_supported_services_rp *rp = (void *) buf;
memset(buf, 0, sizeof(buf));
tester_set_bit(buf, BTP_SERVICE_ID_CORE);
tester_set_bit(buf, BTP_SERVICE_ID_GAP);
tester_set_bit(buf, BTP_SERVICE_ID_GATT);
#if MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM)
tester_set_bit(buf, BTP_SERVICE_ID_L2CAP);
#endif /* MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM) */
#if MYNEWT_VAL(BLE_MESH)
tester_set_bit(buf, BTP_SERVICE_ID_MESH);
#endif /* MYNEWT_VAL(BLE_MESH) */
tester_send(BTP_SERVICE_ID_CORE, CORE_READ_SUPPORTED_SERVICES,
BTP_INDEX_NONE, (u8_t *) rp, sizeof(buf));
}
static void register_service(u8_t *data, u16_t len)
{
struct core_register_service_cmd *cmd = (void *) data;
u8_t status;
switch (cmd->id) {
case BTP_SERVICE_ID_GAP:
status = tester_init_gap();
/* Rsp with success status will be handled by bt enable cb */
if (status == BTP_STATUS_FAILED) {
goto rsp;
}
return;
case BTP_SERVICE_ID_GATT:
status = tester_init_gatt();
break;
#if MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM)
case BTP_SERVICE_ID_L2CAP:
status = tester_init_l2cap();
break;
#endif /* MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM) */
#if MYNEWT_VAL(BLE_MESH)
case BTP_SERVICE_ID_MESH:
status = tester_init_mesh();
break;
#endif /* MYNEWT_VAL(BLE_MESH) */
default:
status = BTP_STATUS_FAILED;
break;
}
rsp:
tester_rsp(BTP_SERVICE_ID_CORE, CORE_REGISTER_SERVICE, BTP_INDEX_NONE,
status);
}
static void unregister_service(u8_t *data, u16_t len)
{
struct core_unregister_service_cmd *cmd = (void *) data;
u8_t status;
switch (cmd->id) {
case BTP_SERVICE_ID_GAP:
status = tester_unregister_gap();
break;
case BTP_SERVICE_ID_GATT:
status = tester_unregister_gatt();
break;
#if MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM)
case BTP_SERVICE_ID_L2CAP:
status = tester_unregister_l2cap();
break;
#endif /* MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM) */
#if MYNEWT_VAL(BLE_MESH)
case BTP_SERVICE_ID_MESH:
status = tester_unregister_mesh();
break;
#endif /* MYNEWT_VAL(BLE_MESH) */
default:
status = BTP_STATUS_FAILED;
break;
}
tester_rsp(BTP_SERVICE_ID_CORE, CORE_UNREGISTER_SERVICE, BTP_INDEX_NONE,
status);
}
static void handle_core(u8_t opcode, u8_t index, u8_t *data,
u16_t len)
{
if (index != BTP_INDEX_NONE) {
tester_rsp(BTP_SERVICE_ID_CORE, opcode, index,
BTP_STATUS_FAILED);
return;
}
switch (opcode) {
case CORE_READ_SUPPORTED_COMMANDS:
supported_commands(data, len);
return;
case CORE_READ_SUPPORTED_SERVICES:
supported_services(data, len);
return;
case CORE_REGISTER_SERVICE:
register_service(data, len);
return;
case CORE_UNREGISTER_SERVICE:
unregister_service(data, len);
return;
default:
tester_rsp(BTP_SERVICE_ID_CORE, opcode, BTP_INDEX_NONE,
BTP_STATUS_UNKNOWN_CMD);
return;
}
}
static void cmd_handler(struct os_event *ev)
{
u16_t len;
struct btp_buf *cmd;
if (!ev || !ev->ev_arg) {
return;
}
cmd = ev->ev_arg;
len = sys_le16_to_cpu(cmd->hdr.len);
if (MYNEWT_VAL(BTTESTER_BTP_LOG)) {
console_printf("[DBG] received %d bytes: %s\n",
sizeof(cmd->hdr) + len,
bt_hex(cmd->data,
sizeof(cmd->hdr) + len));
}
/* TODO
* verify if service is registered before calling handler
*/
switch (cmd->hdr.service) {
case BTP_SERVICE_ID_CORE:
handle_core(cmd->hdr.opcode, cmd->hdr.index,
cmd->hdr.data, len);
break;
case BTP_SERVICE_ID_GAP:
tester_handle_gap(cmd->hdr.opcode, cmd->hdr.index,
cmd->hdr.data, len);
break;
case BTP_SERVICE_ID_GATT:
tester_handle_gatt(cmd->hdr.opcode, cmd->hdr.index,
cmd->hdr.data, len);
break;
#if MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM)
case BTP_SERVICE_ID_L2CAP:
tester_handle_l2cap(cmd->hdr.opcode, cmd->hdr.index,
cmd->hdr.data, len);
break;
#endif /* MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM) */
#if MYNEWT_VAL(BLE_MESH)
case BTP_SERVICE_ID_MESH:
tester_handle_mesh(cmd->hdr.opcode, cmd->hdr.index,
cmd->hdr.data, len);
break;
#endif /* MYNEWT_VAL(BLE_MESH) */
default:
tester_rsp(cmd->hdr.service, cmd->hdr.opcode,
cmd->hdr.index, BTP_STATUS_FAILED);
break;
}
os_eventq_put(&avail_queue, ev);
}
static u8_t *recv_cb(u8_t *buf, size_t *off)
{
struct btp_hdr *cmd = (void *) buf;
struct os_event *new_ev;
struct btp_buf *new_buf, *old_buf;
u16_t len;
if (*off < sizeof(*cmd)) {
return buf;
}
len = sys_le16_to_cpu(cmd->len);
if (len > BTP_MTU - sizeof(*cmd)) {
SYS_LOG_ERR("BT tester: invalid packet length");
*off = 0;
return buf;
}
if (*off < sizeof(*cmd) + len) {
return buf;
}
new_ev = os_eventq_get_no_wait(&avail_queue);
if (!new_ev) {
SYS_LOG_ERR("BT tester: RX overflow");
*off = 0;
return buf;
}
old_buf = CONTAINER_OF(buf, struct btp_buf, data);
os_eventq_put(cmds_queue, old_buf->ev);
new_buf = new_ev->ev_arg;
*off = 0;
return new_buf->data;
}
static void avail_queue_init(void)
{
int i;
os_eventq_init(&avail_queue);
for (i = 0; i < CMD_QUEUED; i++) {
cmd_buf[i].ev = &bttester_ev[i];
bttester_ev[i].ev_cb = cmd_handler;
bttester_ev[i].ev_arg = &cmd_buf[i];
os_eventq_put(&avail_queue, &bttester_ev[i]);
}
}
void bttester_evq_set(struct os_eventq *evq)
{
cmds_queue = evq;
}
void tester_init(void)
{
struct os_event *ev;
struct btp_buf *buf;
avail_queue_init();
bttester_evq_set(os_eventq_dflt_get());
ev = os_eventq_get(&avail_queue);
buf = ev->ev_arg;
if (bttester_pipe_init()) {
SYS_LOG_ERR("Failed to initialize pipe");
return;
}
bttester_pipe_register(buf->data, BTP_MTU, recv_cb);
tester_send(BTP_SERVICE_ID_CORE, CORE_EV_IUT_READY, BTP_INDEX_NONE,
NULL, 0);
}
void tester_send(u8_t service, u8_t opcode, u8_t index, u8_t *data,
size_t len)
{
struct btp_hdr msg;
msg.service = service;
msg.opcode = opcode;
msg.index = index;
msg.len = len;
bttester_pipe_send((u8_t *)&msg, sizeof(msg));
if (data && len) {
bttester_pipe_send(data, len);
}
if (MYNEWT_VAL(BTTESTER_BTP_LOG)) {
console_printf("[DBG] send %d bytes hdr: %s\n", sizeof(msg),
bt_hex((char *) &msg, sizeof(msg)));
if (data && len) {
console_printf("[DBG] send %d bytes data: %s\n", len,
bt_hex((char *) data, len));
}
}
}
void tester_rsp(u8_t service, u8_t opcode, u8_t index, u8_t status)
{
struct btp_status s;
if (status == BTP_STATUS_SUCCESS) {
tester_send(service, opcode, index, NULL, 0);
return;
}
s.code = status;
tester_send(service, BTP_STATUS, index, (u8_t *) &s, sizeof(s));
}

View File

@@ -0,0 +1,928 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
/* bttester.h - Bluetooth tester headers */
/*
* Copyright (c) 2015-2016 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef __BTTESTER_H__
#define __BTTESTER_H__
#include "syscfg/syscfg.h"
#include "host/ble_gatt.h"
#if MYNEWT_VAL(BLE_MESH)
#include "mesh/glue.h"
#else
#include "glue.h"
#endif
#define BTP_MTU MYNEWT_VAL(BTTESTER_BTP_DATA_SIZE_MAX)
#define BTP_DATA_MAX_SIZE (BTP_MTU - sizeof(struct btp_hdr))
#define BTP_INDEX_NONE 0xff
#define BTP_SERVICE_ID_CORE 0
#define BTP_SERVICE_ID_GAP 1
#define BTP_SERVICE_ID_GATT 2
#define BTP_SERVICE_ID_L2CAP 3
#define BTP_SERVICE_ID_MESH 4
#define BTP_STATUS_SUCCESS 0x00
#define BTP_STATUS_FAILED 0x01
#define BTP_STATUS_UNKNOWN_CMD 0x02
#define BTP_STATUS_NOT_READY 0x03
#define SYS_LOG_DBG(fmt, ...) \
if (MYNEWT_VAL(BTTESTER_DEBUG)) { \
console_printf("[DBG] %s: " fmt "\n", \
__func__, ## __VA_ARGS__); \
}
#define SYS_LOG_INF(fmt, ...) console_printf("[INF] %s: " fmt "\n", \
__func__, ## __VA_ARGS__);
#define SYS_LOG_ERR(fmt, ...) console_printf("[WRN] %s: " fmt "\n", \
__func__, ## __VA_ARGS__);
#define SYS_LOG_LEVEL SYS_LOG_LEVEL_DEBUG
#define SYS_LOG_DOMAIN "bttester"
#define sys_cpu_to_le32 htole32
#define sys_le32_to_cpu le32toh
#define sys_cpu_to_le16 htole16
struct btp_hdr {
u8_t service;
u8_t opcode;
u8_t index;
u16_t len;
u8_t data[0];
} __packed;
#define BTP_STATUS 0x00
struct btp_status {
u8_t code;
} __packed;
/* Core Service */
#define CORE_READ_SUPPORTED_COMMANDS 0x01
struct core_read_supported_commands_rp {
u8_t data[0];
} __packed;
#define CORE_READ_SUPPORTED_SERVICES 0x02
struct core_read_supported_services_rp {
u8_t data[0];
} __packed;
#define CORE_REGISTER_SERVICE 0x03
struct core_register_service_cmd {
u8_t id;
} __packed;
#define CORE_UNREGISTER_SERVICE 0x04
struct core_unregister_service_cmd {
u8_t id;
} __packed;
/* events */
#define CORE_EV_IUT_READY 0x80
/* GAP Service */
/* commands */
#define GAP_READ_SUPPORTED_COMMANDS 0x01
struct gap_read_supported_commands_rp {
u8_t data[0];
} __packed;
#define GAP_READ_CONTROLLER_INDEX_LIST 0x02
struct gap_read_controller_index_list_rp {
u8_t num;
u8_t index[0];
} __packed;
#define GAP_SETTINGS_POWERED 0
#define GAP_SETTINGS_CONNECTABLE 1
#define GAP_SETTINGS_FAST_CONNECTABLE 2
#define GAP_SETTINGS_DISCOVERABLE 3
#define GAP_SETTINGS_BONDABLE 4
#define GAP_SETTINGS_LINK_SEC_3 5
#define GAP_SETTINGS_SSP 6
#define GAP_SETTINGS_BREDR 7
#define GAP_SETTINGS_HS 8
#define GAP_SETTINGS_LE 9
#define GAP_SETTINGS_ADVERTISING 10
#define GAP_SETTINGS_SC 11
#define GAP_SETTINGS_DEBUG_KEYS 12
#define GAP_SETTINGS_PRIVACY 13
#define GAP_SETTINGS_CONTROLLER_CONFIG 14
#define GAP_SETTINGS_STATIC_ADDRESS 15
#define GAP_READ_CONTROLLER_INFO 0x03
struct gap_read_controller_info_rp {
u8_t address[6];
u32_t supported_settings;
u32_t current_settings;
u8_t cod[3];
u8_t name[249];
u8_t short_name[11];
} __packed;
#define GAP_RESET 0x04
struct gap_reset_rp {
u32_t current_settings;
} __packed;
#define GAP_SET_POWERED 0x05
struct gap_set_powered_cmd {
u8_t powered;
} __packed;
struct gap_set_powered_rp {
u32_t current_settings;
} __packed;
#define GAP_SET_CONNECTABLE 0x06
struct gap_set_connectable_cmd {
u8_t connectable;
} __packed;
struct gap_set_connectable_rp {
u32_t current_settings;
} __packed;
#define GAP_SET_FAST_CONNECTABLE 0x07
struct gap_set_fast_connectable_cmd {
u8_t fast_connectable;
} __packed;
struct gap_set_fast_connectable_rp {
u32_t current_settings;
} __packed;
#define GAP_NON_DISCOVERABLE 0x00
#define GAP_GENERAL_DISCOVERABLE 0x01
#define GAP_LIMITED_DISCOVERABLE 0x02
#define GAP_SET_DISCOVERABLE 0x08
struct gap_set_discoverable_cmd {
u8_t discoverable;
} __packed;
struct gap_set_discoverable_rp {
u32_t current_settings;
} __packed;
#define GAP_SET_BONDABLE 0x09
struct gap_set_bondable_cmd {
u8_t gap_set_bondable_cmd;
} __packed;
struct gap_set_bondable_rp {
u32_t current_settings;
} __packed;
#define GAP_START_ADVERTISING 0x0a
struct gap_start_advertising_cmd {
u8_t adv_data_len;
u8_t scan_rsp_len;
u8_t adv_data[0];
u8_t scan_rsp[0];
} __packed;
struct gap_start_advertising_rp {
u32_t current_settings;
} __packed;
#define GAP_STOP_ADVERTISING 0x0b
struct gap_stop_advertising_rp {
u32_t current_settings;
} __packed;
#define GAP_DISCOVERY_FLAG_LE 0x01
#define GAP_DISCOVERY_FLAG_BREDR 0x02
#define GAP_DISCOVERY_FLAG_LIMITED 0x04
#define GAP_DISCOVERY_FLAG_LE_ACTIVE_SCAN 0x08
#define GAP_DISCOVERY_FLAG_LE_OBSERVE 0x10
#define GAP_START_DISCOVERY 0x0c
struct gap_start_discovery_cmd {
u8_t flags;
} __packed;
#define GAP_STOP_DISCOVERY 0x0d
#define GAP_CONNECT 0x0e
struct gap_connect_cmd {
u8_t address_type;
u8_t address[6];
} __packed;
#define GAP_DISCONNECT 0x0f
struct gap_disconnect_cmd {
u8_t address_type;
u8_t address[6];
} __packed;
#define GAP_IO_CAP_DISPLAY_ONLY 0
#define GAP_IO_CAP_DISPLAY_YESNO 1
#define GAP_IO_CAP_KEYBOARD_ONLY 2
#define GAP_IO_CAP_NO_INPUT_OUTPUT 3
#define GAP_IO_CAP_KEYBOARD_DISPLAY 4
#define GAP_SET_IO_CAP 0x10
struct gap_set_io_cap_cmd {
u8_t io_cap;
} __packed;
#define GAP_PAIR 0x11
struct gap_pair_cmd {
u8_t address_type;
u8_t address[6];
} __packed;
#define GAP_UNPAIR 0x12
struct gap_unpair_cmd {
u8_t address_type;
u8_t address[6];
} __packed;
#define GAP_PASSKEY_ENTRY 0x13
struct gap_passkey_entry_cmd {
u8_t address_type;
u8_t address[6];
u32_t passkey;
} __packed;
#define GAP_PASSKEY_CONFIRM 0x14
struct gap_passkey_confirm_cmd {
u8_t address_type;
u8_t address[6];
u8_t match;
} __packed;
/* events */
#define GAP_EV_NEW_SETTINGS 0x80
struct gap_new_settings_ev {
u32_t current_settings;
} __packed;
#define GAP_DEVICE_FOUND_FLAG_RSSI 0x01
#define GAP_DEVICE_FOUND_FLAG_AD 0x02
#define GAP_DEVICE_FOUND_FLAG_SD 0x04
#define GAP_EV_DEVICE_FOUND 0x81
struct gap_device_found_ev {
u8_t address_type;
u8_t address[6];
s8_t rssi;
u8_t flags;
u16_t eir_data_len;
u8_t eir_data[0];
} __packed;
#define GAP_EV_DEVICE_CONNECTED 0x82
struct gap_device_connected_ev {
u8_t address_type;
u8_t address[6];
} __packed;
#define GAP_EV_DEVICE_DISCONNECTED 0x83
struct gap_device_disconnected_ev {
u8_t address_type;
u8_t address[6];
} __packed;
#define GAP_EV_PASSKEY_DISPLAY 0x84
struct gap_passkey_display_ev {
u8_t address_type;
u8_t address[6];
u32_t passkey;
} __packed;
#define GAP_EV_PASSKEY_ENTRY_REQ 0x85
struct gap_passkey_entry_req_ev {
u8_t address_type;
u8_t address[6];
} __packed;
#define GAP_EV_PASSKEY_CONFIRM_REQ 0x86
struct gap_passkey_confirm_req_ev {
u8_t address_type;
u8_t address[6];
u32_t passkey;
} __packed;
#define GAP_EV_IDENTITY_RESOLVED 0x87
struct gap_identity_resolved_ev {
u8_t address_type;
u8_t address[6];
u8_t identity_address_type;
u8_t identity_address[6];
} __packed;
/* GATT Service */
/* commands */
#define GATT_READ_SUPPORTED_COMMANDS 0x01
struct gatt_read_supported_commands_rp {
u8_t data[0];
} __packed;
#define GATT_SERVICE_PRIMARY 0x00
#define GATT_SERVICE_SECONDARY 0x01
#define GATT_ADD_SERVICE 0x02
struct gatt_add_service_cmd {
u8_t type;
u8_t uuid_length;
u8_t uuid[0];
} __packed;
struct gatt_add_service_rp {
u16_t svc_id;
} __packed;
#define GATT_ADD_CHARACTERISTIC 0x03
struct gatt_add_characteristic_cmd {
u16_t svc_id;
u8_t properties;
u8_t permissions;
u8_t uuid_length;
u8_t uuid[0];
} __packed;
struct gatt_add_characteristic_rp {
u16_t char_id;
} __packed;
#define GATT_ADD_DESCRIPTOR 0x04
struct gatt_add_descriptor_cmd {
u16_t char_id;
u8_t permissions;
u8_t uuid_length;
u8_t uuid[0];
} __packed;
struct gatt_add_descriptor_rp {
u16_t desc_id;
} __packed;
#define GATT_ADD_INCLUDED_SERVICE 0x05
struct gatt_add_included_service_cmd {
u16_t svc_id;
} __packed;
struct gatt_add_included_service_rp {
u16_t included_service_id;
} __packed;
#define GATT_SET_VALUE 0x06
struct gatt_set_value_cmd {
u16_t attr_id;
u16_t len;
u8_t value[0];
} __packed;
#define GATT_START_SERVER 0x07
struct gatt_start_server_rp {
u16_t db_attr_off;
u8_t db_attr_cnt;
} __packed;
#define GATT_SET_ENC_KEY_SIZE 0x09
struct gatt_set_enc_key_size_cmd {
u16_t attr_id;
u8_t key_size;
} __packed;
/* Gatt Client */
struct gatt_service {
u16_t start_handle;
u16_t end_handle;
u8_t uuid_length;
u8_t uuid[0];
} __packed;
struct gatt_included {
u16_t included_handle;
struct gatt_service service;
} __packed;
struct gatt_characteristic {
u16_t characteristic_handle;
u16_t value_handle;
u8_t properties;
u8_t uuid_length;
u8_t uuid[0];
} __packed;
struct gatt_descriptor {
u16_t descriptor_handle;
u8_t uuid_length;
u8_t uuid[0];
} __packed;
#define GATT_EXCHANGE_MTU 0x0a
#define GATT_DISC_ALL_PRIM_SVCS 0x0b
struct gatt_disc_all_prim_svcs_cmd {
u8_t address_type;
u8_t address[6];
} __packed;
struct gatt_disc_all_prim_svcs_rp {
u8_t services_count;
struct gatt_service services[0];
} __packed;
#define GATT_DISC_PRIM_UUID 0x0c
struct gatt_disc_prim_uuid_cmd {
u8_t address_type;
u8_t address[6];
u8_t uuid_length;
u8_t uuid[0];
} __packed;
struct gatt_disc_prim_uuid_rp {
u8_t services_count;
struct gatt_service services[0];
} __packed;
#define GATT_FIND_INCLUDED 0x0d
struct gatt_find_included_cmd {
u8_t address_type;
u8_t address[6];
u16_t start_handle;
u16_t end_handle;
} __packed;
struct gatt_find_included_rp {
u8_t services_count;
struct gatt_included included[0];
} __packed;
#define GATT_DISC_ALL_CHRC 0x0e
struct gatt_disc_all_chrc_cmd {
u8_t address_type;
u8_t address[6];
u16_t start_handle;
u16_t end_handle;
} __packed;
struct gatt_disc_chrc_rp {
u8_t characteristics_count;
struct gatt_characteristic characteristics[0];
} __packed;
#define GATT_DISC_CHRC_UUID 0x0f
struct gatt_disc_chrc_uuid_cmd {
u8_t address_type;
u8_t address[6];
u16_t start_handle;
u16_t end_handle;
u8_t uuid_length;
u8_t uuid[0];
} __packed;
#define GATT_DISC_ALL_DESC 0x10
struct gatt_disc_all_desc_cmd {
u8_t address_type;
u8_t address[6];
u16_t start_handle;
u16_t end_handle;
} __packed;
struct gatt_disc_all_desc_rp {
u8_t descriptors_count;
struct gatt_descriptor descriptors[0];
} __packed;
#define GATT_READ 0x11
struct gatt_read_cmd {
u8_t address_type;
u8_t address[6];
u16_t handle;
} __packed;
struct gatt_read_rp {
u8_t att_response;
u16_t data_length;
u8_t data[0];
} __packed;
#define GATT_READ_UUID 0x12
struct gatt_read_uuid_cmd {
u8_t address_type;
u8_t address[6];
u16_t start_handle;
u16_t end_handle;
u8_t uuid_length;
u8_t uuid[0];
} __packed;
#define GATT_READ_LONG 0x13
struct gatt_read_long_cmd {
u8_t address_type;
u8_t address[6];
u16_t handle;
u16_t offset;
} __packed;
#define GATT_READ_MULTIPLE 0x14
struct gatt_read_multiple_cmd {
u8_t address_type;
u8_t address[6];
u8_t handles_count;
u16_t handles[0];
} __packed;
#define GATT_WRITE_WITHOUT_RSP 0x15
struct gatt_write_without_rsp_cmd {
u8_t address_type;
u8_t address[6];
u16_t handle;
u16_t data_length;
u8_t data[0];
} __packed;
#define GATT_SIGNED_WRITE_WITHOUT_RSP 0x16
struct gatt_signed_write_without_rsp_cmd {
u8_t address_type;
u8_t address[6];
u16_t handle;
u16_t data_length;
u8_t data[0];
} __packed;
#define GATT_WRITE 0x17
struct gatt_write_cmd {
u8_t address_type;
u8_t address[6];
u16_t handle;
u16_t data_length;
u8_t data[0];
} __packed;
#define GATT_WRITE_LONG 0x18
struct gatt_write_long_cmd {
u8_t address_type;
u8_t address[6];
u16_t handle;
u16_t offset;
u16_t data_length;
u8_t data[0];
} __packed;
#define GATT_RELIABLE_WRITE 0x19
struct gatt_reliable_write_cmd {
u8_t address_type;
u8_t address[6];
u16_t handle;
u16_t offset;
u16_t data_length;
u8_t data[0];
} __packed;
#define GATT_CFG_NOTIFY 0x1a
#define GATT_CFG_INDICATE 0x1b
struct gatt_cfg_notify_cmd {
u8_t address_type;
u8_t address[6];
u8_t enable;
u16_t ccc_handle;
} __packed;
#define GATT_GET_ATTRIBUTES 0x1c
struct gatt_get_attributes_cmd {
u16_t start_handle;
u16_t end_handle;
u8_t type_length;
u8_t type[0];
} __packed;
struct gatt_get_attributes_rp {
u8_t attrs_count;
u8_t attrs[0];
} __packed;
struct gatt_attr {
u16_t handle;
u8_t permission;
u8_t type_length;
u8_t type[0];
} __packed;
#define GATT_GET_ATTRIBUTE_VALUE 0x1d
struct gatt_get_attribute_value_cmd {
u16_t handle;
} __packed;
struct gatt_get_attribute_value_rp {
u8_t att_response;
u16_t value_length;
u8_t value[0];
} __packed;
/* GATT events */
#define GATT_EV_NOTIFICATION 0x80
struct gatt_notification_ev {
u8_t address_type;
u8_t address[6];
u8_t type;
u16_t handle;
u16_t data_length;
u8_t data[0];
} __packed;
#define GATT_EV_ATTR_VALUE_CHANGED 0x81
struct gatt_attr_value_changed_ev {
u16_t handle;
u16_t data_length;
u8_t data[0];
} __packed;
static inline void tester_set_bit(u8_t *addr, unsigned int bit)
{
u8_t *p = addr + (bit / 8);
*p |= BIT(bit % 8);
}
static inline u8_t tester_test_bit(const u8_t *addr, unsigned int bit)
{
const u8_t *p = addr + (bit / 8);
return *p & BIT(bit % 8);
}
/* L2CAP Service */
/* commands */
#define L2CAP_READ_SUPPORTED_COMMANDS 0x01
struct l2cap_read_supported_commands_rp {
u8_t data[0];
} __packed;
#define L2CAP_CONNECT 0x02
struct l2cap_connect_cmd {
u8_t address_type;
u8_t address[6];
u16_t psm;
} __packed;
struct l2cap_connect_rp {
u8_t chan_id;
} __packed;
#define L2CAP_DISCONNECT 0x03
struct l2cap_disconnect_cmd {
u8_t chan_id;
} __packed;
#define L2CAP_SEND_DATA 0x04
struct l2cap_send_data_cmd {
u8_t chan_id;
u16_t data_len;
u8_t data[];
} __packed;
#define L2CAP_TRANSPORT_BREDR 0x00
#define L2CAP_TRANSPORT_LE 0x01
#define L2CAP_LISTEN 0x05
struct l2cap_listen_cmd {
u16_t psm;
u8_t transport;
} __packed;
#define L2CAP_ACCEPT_CONNECTION 0x06
struct l2cap_accept_connection_cmd {
u8_t chan_id;
u16_t result;
} __packed;
/* events */
#define L2CAP_EV_CONNECTION_REQ 0x80
struct l2cap_connection_req_ev {
u8_t chan_id;
u16_t psm;
u8_t address_type;
u8_t address[6];
} __packed;
#define L2CAP_EV_CONNECTED 0x81
struct l2cap_connected_ev {
u8_t chan_id;
u16_t psm;
u8_t address_type;
u8_t address[6];
} __packed;
#define L2CAP_EV_DISCONNECTED 0x82
struct l2cap_disconnected_ev {
u16_t result;
u8_t chan_id;
u16_t psm;
u8_t address_type;
u8_t address[6];
} __packed;
#define L2CAP_EV_DATA_RECEIVED 0x83
struct l2cap_data_received_ev {
u8_t chan_id;
u16_t data_length;
u8_t data[0];
} __packed;
/* MESH Service */
/* commands */
#define MESH_READ_SUPPORTED_COMMANDS 0x01
struct mesh_read_supported_commands_rp {
u8_t data[0];
} __packed;
#define MESH_OUT_BLINK BIT(0)
#define MESH_OUT_BEEP BIT(1)
#define MESH_OUT_VIBRATE BIT(2)
#define MESH_OUT_DISPLAY_NUMBER BIT(3)
#define MESH_OUT_DISPLAY_STRING BIT(4)
#define MESH_IN_PUSH BIT(0)
#define MESH_IN_TWIST BIT(1)
#define MESH_IN_ENTER_NUMBER BIT(2)
#define MESH_IN_ENTER_STRING BIT(3)
#define MESH_CONFIG_PROVISIONING 0x02
struct mesh_config_provisioning_cmd {
u8_t uuid[16];
u8_t static_auth[16];
u8_t out_size;
u16_t out_actions;
u8_t in_size;
u16_t in_actions;
} __packed;
#define MESH_PROVISION_NODE 0x03
struct mesh_provision_node_cmd {
u8_t net_key[16];
u16_t net_key_idx;
u8_t flags;
u32_t iv_index;
u32_t seq_num;
u16_t addr;
u8_t dev_key[16];
} __packed;
#define MESH_INIT 0x04
#define MESH_RESET 0x05
#define MESH_INPUT_NUMBER 0x06
struct mesh_input_number_cmd {
u32_t number;
} __packed;
#define MESH_INPUT_STRING 0x07
struct mesh_input_string_cmd {
u8_t string_len;
u8_t string[0];
} __packed;
#define MESH_IVU_TEST_MODE 0x08
struct mesh_ivu_test_mode_cmd {
u8_t enable;
} __packed;
#define MESH_IVU_TOGGLE_STATE 0x09
#define MESH_NET_SEND 0x0a
struct mesh_net_send_cmd {
u8_t ttl;
u16_t src;
u16_t dst;
u8_t payload_len;
u8_t payload[0];
} __packed;
#define MESH_HEALTH_GENERATE_FAULTS 0x0b
struct mesh_health_generate_faults_rp {
u8_t test_id;
u8_t cur_faults_count;
u8_t reg_faults_count;
u8_t current_faults[0];
u8_t registered_faults[0];
} __packed;
#define MESH_HEALTH_CLEAR_FAULTS 0x0c
#define MESH_LPN 0x0d
struct mesh_lpn_set_cmd {
u8_t enable;
} __packed;
#define MESH_LPN_POLL 0x0e
#define MESH_MODEL_SEND 0x0f
struct mesh_model_send_cmd {
u16_t src;
u16_t dst;
u8_t payload_len;
u8_t payload[0];
} __packed;
#define MESH_LPN_SUBSCRIBE 0x10
struct mesh_lpn_subscribe_cmd {
u16_t address;
} __packed;
#define MESH_LPN_UNSUBSCRIBE 0x11
struct mesh_lpn_unsubscribe_cmd {
u16_t address;
} __packed;
#define MESH_RPL_CLEAR 0x12
#define MESH_PROXY_IDENTITY 0x13
/* events */
#define MESH_EV_OUT_NUMBER_ACTION 0x80
struct mesh_out_number_action_ev {
u16_t action;
u32_t number;
} __packed;
#define MESH_EV_OUT_STRING_ACTION 0x81
struct mesh_out_string_action_ev {
u8_t string_len;
u8_t string[0];
} __packed;
#define MESH_EV_IN_ACTION 0x82
struct mesh_in_action_ev {
u16_t action;
u8_t size;
} __packed;
#define MESH_EV_PROVISIONED 0x83
#define MESH_PROV_BEARER_PB_ADV 0x00
#define MESH_PROV_BEARER_PB_GATT 0x01
#define MESH_EV_PROV_LINK_OPEN 0x84
struct mesh_prov_link_open_ev {
u8_t bearer;
} __packed;
#define MESH_EV_PROV_LINK_CLOSED 0x85
struct mesh_prov_link_closed_ev {
u8_t bearer;
} __packed;
#define MESH_EV_NET_RECV 0x86
struct mesh_net_recv_ev {
u8_t ttl;
u8_t ctl;
u16_t src;
u16_t dst;
u8_t payload_len;
u8_t payload[0];
} __packed;
#define MESH_EV_INVALID_BEARER 0x87
struct mesh_invalid_bearer_ev {
u8_t opcode;
} __packed;
#define MESH_EV_INCOMP_TIMER_EXP 0x88
void tester_init(void);
void tester_rsp(u8_t service, u8_t opcode, u8_t index, u8_t status);
void tester_send(u8_t service, u8_t opcode, u8_t index, u8_t *data,
size_t len);
u8_t tester_init_gap(void);
u8_t tester_unregister_gap(void);
void tester_handle_gap(u8_t opcode, u8_t index, u8_t *data,
u16_t len);
u8_t tester_init_gatt(void);
u8_t tester_unregister_gatt(void);
void tester_handle_gatt(u8_t opcode, u8_t index, u8_t *data,
u16_t len);
int tester_gatt_notify_rx_ev(u16_t conn_handle, u16_t attr_handle,
u8_t indication, struct os_mbuf *om);
int tester_gatt_subscribe_ev(u16_t conn_handle, u16_t attr_handle, u8_t reason,
u8_t prev_notify, u8_t cur_notify,
u8_t prev_indicate, u8_t cur_indicate);
#if MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM)
u8_t tester_init_l2cap(void);
u8_t tester_unregister_l2cap(void);
void tester_handle_l2cap(u8_t opcode, u8_t index, u8_t *data,
u16_t len);
#endif
#if MYNEWT_VAL(BLE_MESH)
u8_t tester_init_mesh(void);
u8_t tester_unregister_mesh(void);
void tester_handle_mesh(u8_t opcode, u8_t index, u8_t *data, u16_t len);
#endif /* MYNEWT_VAL(BLE_MESH) */
void gatt_svr_register_cb(struct ble_gatt_register_ctxt *ctxt, void *arg);
int gatt_svr_init(void);
#endif /* __BTTESTER_H__ */

View File

@@ -0,0 +1,39 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 __BTTESTER_PIPE_H__
#define __BTTESTER_PIPE_H__
#include <stdlib.h>
#include "bttester.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef u8_t *(*bttester_pipe_recv_cb)(u8_t *buf, size_t *off);
void bttester_pipe_register(u8_t *buf, size_t len, bttester_pipe_recv_cb cb);
int bttester_pipe_send(const u8_t *data, int len);
int bttester_pipe_init(void);
#ifdef __cplusplus
}
#endif
#endif /* __BTTESTER_PIPE_H__ */

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,129 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
#include "syscfg/syscfg.h"
#if !MYNEWT_VAL(BLE_MESH)
#include <assert.h>
#include <string.h>
#include "os/os.h"
#include "os/os_mbuf.h"
#include "glue.h"
#define ASSERT_NOT_CHAIN(om) assert(SLIST_NEXT(om, om_next) == NULL)
const char *bt_hex(const void *buf, size_t len)
{
static const char hex[] = "0123456789abcdef";
static char hexbufs[4][137];
static u8_t curbuf;
const u8_t *b = buf;
char *str;
int i;
str = hexbufs[curbuf++];
curbuf %= ARRAY_SIZE(hexbufs);
len = min(len, (sizeof(hexbufs[0]) - 1) / 2);
for (i = 0; i < len; i++) {
str[i * 2] = hex[b[i] >> 4];
str[i * 2 + 1] = hex[b[i] & 0xf];
}
str[i * 2] = '\0';
return str;
}
struct os_mbuf * NET_BUF_SIMPLE(uint16_t size)
{
struct os_mbuf *buf;
buf = os_msys_get(size, 0);
assert(buf);
return buf;
}
/* This is by purpose */
void net_buf_simple_init(struct os_mbuf *buf,
size_t reserve_head)
{
/* This is called in Zephyr after init.
* Note in Mynewt case we don't care abour reserved head*/
buf->om_data = &buf->om_databuf[buf->om_pkthdr_len] + reserve_head;
buf->om_len = 0;
}
void
net_buf_simple_add_le16(struct os_mbuf *om, uint16_t val)
{
val = htole16(val);
os_mbuf_append(om, &val, sizeof(val));
ASSERT_NOT_CHAIN(om);
}
void
net_buf_simple_add_be16(struct os_mbuf *om, uint16_t val)
{
val = htobe16(val);
os_mbuf_append(om, &val, sizeof(val));
ASSERT_NOT_CHAIN(om);
}
void
net_buf_simple_add_be32(struct os_mbuf *om, uint32_t val)
{
val = htobe32(val);
os_mbuf_append(om, &val, sizeof(val));
ASSERT_NOT_CHAIN(om);
}
void
net_buf_simple_add_u8(struct os_mbuf *om, uint8_t val)
{
os_mbuf_append(om, &val, 1);
ASSERT_NOT_CHAIN(om);
}
void*
net_buf_simple_add(struct os_mbuf *om, uint8_t len)
{
void * tmp;
tmp = os_mbuf_extend(om, len);
ASSERT_NOT_CHAIN(om);
return tmp;
}
uint8_t *
net_buf_simple_push(struct os_mbuf *om, uint8_t len)
{
uint8_t headroom = om->om_data - &om->om_databuf[om->om_pkthdr_len];
assert(headroom >= len);
om->om_data -= len;
om->om_len += len;
return om->om_data;
}
#endif

View File

@@ -0,0 +1,63 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 __GLUE_H__
#define __GLUE_H__
#include "os/endian.h"
#define u8_t uint8_t
#define s8_t int8_t
#define u16_t uint16_t
#define u32_t uint32_t
#define s32_t int32_t
#ifndef BIT
#define BIT(n) (1UL << (n))
#endif
#define __packed __attribute__((__packed__))
#define sys_le16_to_cpu le16toh
struct bt_data {
u8_t type;
u8_t data_len;
const u8_t *data;
};
#define BT_DATA(_type, _data, _data_len) \
{ \
.type = (_type), \
.data_len = (_data_len), \
.data = (const u8_t *)(_data), \
}
struct os_mbuf * NET_BUF_SIMPLE(uint16_t size);
void net_buf_simple_init(struct os_mbuf *buf, size_t reserve_head);
void net_buf_simple_add_le16(struct os_mbuf *om, uint16_t val);
void net_buf_simple_add_u8(struct os_mbuf *om, uint8_t val);
void *net_buf_simple_add(struct os_mbuf *om, uint8_t len);
uint8_t *net_buf_simple_push(struct os_mbuf *om, uint8_t len);
#define net_buf_simple_add_mem(a,b,c) os_mbuf_append(a,b,c)
const char *bt_hex(const void *buf, size_t len);
#endif /* __GLUE_H__ */

View File

@@ -0,0 +1,476 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
/* l2cap.c - Bluetooth L2CAP Tester */
/*
* Copyright (c) 2016 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "syscfg/syscfg.h"
#if MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM)
#include "console/console.h"
#include "host/ble_gap.h"
#include "host/ble_l2cap.h"
#include "bttester.h"
#define CONTROLLER_INDEX 0
#define CHANNELS MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM)
#define TESTER_COC_MTU (230)
#define TESTER_COC_BUF_COUNT (3 * MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM))
static os_membuf_t tester_sdu_coc_mem[
OS_MEMPOOL_SIZE(TESTER_COC_BUF_COUNT, TESTER_COC_MTU)
];
struct os_mbuf_pool sdu_os_mbuf_pool;
static struct os_mempool sdu_coc_mbuf_mempool;
static struct channel {
u8_t chan_id; /* Internal number that identifies L2CAP channel. */
u8_t state;
struct ble_l2cap_chan *chan;
} channels[CHANNELS];
static u8_t recv_cb_buf[TESTER_COC_MTU + sizeof(struct l2cap_data_received_ev)];
struct channel *find_channel(struct ble_l2cap_chan *chan) {
int i;
for (i = 0; i < CHANNELS; ++i) {
if (channels[i].chan == chan) {
return &channels[i];
}
}
return NULL;
}
static void
tester_l2cap_coc_recv(struct ble_l2cap_chan *chan, struct os_mbuf *sdu)
{
SYS_LOG_DBG("LE CoC SDU received, chan: 0x%08lx, data len %d",
(uint32_t) chan, OS_MBUF_PKTLEN(sdu));
os_mbuf_free_chain(sdu);
sdu = os_mbuf_get_pkthdr(&sdu_os_mbuf_pool, 0);
assert(sdu != NULL);
ble_l2cap_recv_ready(chan, sdu);
}
static void recv_cb(uint16_t conn_handle, struct ble_l2cap_chan *chan,
struct os_mbuf *buf, void *arg)
{
struct l2cap_data_received_ev *ev = (void *) recv_cb_buf;
struct channel *channel = arg;
ev->chan_id = channel->chan_id;
ev->data_length = buf->om_len;
memcpy(ev->data, buf->om_data, buf->om_len);
tester_send(BTP_SERVICE_ID_L2CAP, L2CAP_EV_DATA_RECEIVED,
CONTROLLER_INDEX, recv_cb_buf, sizeof(*ev) + buf->om_len);
tester_l2cap_coc_recv(chan, buf);
}
static void unstalled_cb(uint16_t conn_handle, struct ble_l2cap_chan *chan,
int status, void *arg)
{
if (status) {
tester_rsp(BTP_SERVICE_ID_L2CAP, L2CAP_SEND_DATA,
CONTROLLER_INDEX, BTP_STATUS_FAILED);
} else {
tester_rsp(BTP_SERVICE_ID_L2CAP, L2CAP_SEND_DATA,
CONTROLLER_INDEX, BTP_STATUS_SUCCESS);
}
}
static struct channel *get_free_channel()
{
u8_t i;
struct channel *chan;
for (i = 0; i < CHANNELS; i++) {
if (channels[i].state) {
continue;
}
chan = &channels[i];
chan->chan_id = i;
return chan;
}
return NULL;
}
static void connected_cb(uint16_t conn_handle, struct ble_l2cap_chan *chan,
void *arg)
{
struct l2cap_connected_ev ev;
struct ble_gap_conn_desc desc;
struct channel *channel;
channel = get_free_channel();
if (!channel) {
assert(0);
}
channel->chan = chan;
channel->state = 0;
ev.chan_id = channel->chan_id;
channel->state = 1;
channel->chan = chan;
/* TODO: ev.psm */
if (!ble_gap_conn_find(conn_handle, &desc)) {
ev.address_type = desc.peer_ota_addr.type;
memcpy(ev.address, desc.peer_ota_addr.val,
sizeof(ev.address));
}
tester_send(BTP_SERVICE_ID_L2CAP, L2CAP_EV_CONNECTED, CONTROLLER_INDEX,
(u8_t *) &ev, sizeof(ev));
}
static void disconnected_cb(uint16_t conn_handle, struct ble_l2cap_chan *chan,
void *arg)
{
struct l2cap_disconnected_ev ev;
struct ble_gap_conn_desc desc;
struct channel *channel;
memset(&ev, 0, sizeof(struct l2cap_disconnected_ev));
channel = find_channel(chan);
if (channel != NULL) {
channel->state = 0;
channel->chan = chan;
ev.chan_id = channel->chan_id;
/* TODO: ev.result */
/* TODO: ev.psm */
}
if (!ble_gap_conn_find(conn_handle, &desc)) {
ev.address_type = desc.peer_ota_addr.type;
memcpy(ev.address, desc.peer_ota_addr.val,
sizeof(ev.address));
}
tester_send(BTP_SERVICE_ID_L2CAP, L2CAP_EV_DISCONNECTED,
CONTROLLER_INDEX, (u8_t *) &ev, sizeof(ev));
}
static int accept_cb(uint16_t conn_handle, uint16_t peer_mtu,
struct ble_l2cap_chan *chan)
{
struct os_mbuf *sdu_rx;
SYS_LOG_DBG("LE CoC accepting, chan: 0x%08lx, peer_mtu %d",
(uint32_t) chan, peer_mtu);
sdu_rx = os_mbuf_get_pkthdr(&sdu_os_mbuf_pool, 0);
if (!sdu_rx) {
return BLE_HS_ENOMEM;
}
ble_l2cap_recv_ready(chan, sdu_rx);
return 0;
}
static int
tester_l2cap_event(struct ble_l2cap_event *event, void *arg)
{
switch (event->type) {
case BLE_L2CAP_EVENT_COC_CONNECTED:
if (event->connect.status) {
console_printf("LE COC error: %d\n", event->connect.status);
disconnected_cb(event->connect.conn_handle,
event->connect.chan, arg);
return 0;
}
console_printf("LE COC connected, conn: %d, chan: 0x%08lx, scid: 0x%04x, "
"dcid: 0x%04x, our_mtu: 0x%04x, peer_mtu: 0x%04x\n",
event->connect.conn_handle,
(uint32_t) event->connect.chan,
ble_l2cap_get_scid(event->connect.chan),
ble_l2cap_get_dcid(event->connect.chan),
ble_l2cap_get_our_mtu(event->connect.chan),
ble_l2cap_get_peer_mtu(event->connect.chan));
connected_cb(event->connect.conn_handle,
event->connect.chan, arg);
return 0;
case BLE_L2CAP_EVENT_COC_DISCONNECTED:
console_printf("LE CoC disconnected, chan: 0x%08lx\n",
(uint32_t) event->disconnect.chan);
disconnected_cb(event->disconnect.conn_handle,
event->disconnect.chan, arg);
return 0;
case BLE_L2CAP_EVENT_COC_ACCEPT:
console_printf("LE CoC accept, chan: 0x%08lx, handle: %u, sdu_size: %u\n",
(uint32_t) event->accept.chan,
event->accept.conn_handle,
event->accept.peer_sdu_size);
return accept_cb(event->accept.conn_handle,
event->accept.peer_sdu_size,
event->accept.chan);
case BLE_L2CAP_EVENT_COC_DATA_RECEIVED:
console_printf("LE CoC data received, chan: 0x%08lx, handle: %u, sdu_len: %u\n",
(uint32_t) event->receive.chan,
event->receive.conn_handle,
event->receive.sdu_rx->om_len);
recv_cb(event->receive.conn_handle, event->receive.chan,
event->receive.sdu_rx, arg);
return 0;
case BLE_L2CAP_EVENT_COC_TX_UNSTALLED:
console_printf("LE CoC tx unstalled, chan: 0x%08lx, handle: %u, status: %d\n",
(uint32_t) event->tx_unstalled.chan,
event->tx_unstalled.conn_handle,
event->tx_unstalled.status);
unstalled_cb(event->tx_unstalled.conn_handle,
event->tx_unstalled.chan,
event->tx_unstalled.status, arg);
return 0;
default:
return 0;
}
}
static void connect(u8_t *data, u16_t len)
{
const struct l2cap_connect_cmd *cmd = (void *) data;
struct l2cap_connect_rp rp;
struct ble_gap_conn_desc desc;
struct channel *chan;
struct os_mbuf *sdu_rx;
ble_addr_t *addr = (void *) data;
int rc;
SYS_LOG_DBG("connect: type: %d addr: %s", addr->type, bt_hex(addr->val, 6));
rc = ble_gap_conn_find_by_addr(addr, &desc);
if (rc) {
SYS_LOG_ERR("GAP conn find failed");
goto fail;
}
chan = get_free_channel();
if (!chan) {
SYS_LOG_ERR("No free channels");
goto fail;
}
sdu_rx = os_mbuf_get_pkthdr(&sdu_os_mbuf_pool, 0);
if (sdu_rx == NULL) {
SYS_LOG_ERR("Failed to alloc buf");
goto fail;
}
rc = ble_l2cap_connect(desc.conn_handle, htole16(cmd->psm),
TESTER_COC_MTU, sdu_rx,
tester_l2cap_event, chan);
if (rc) {
SYS_LOG_ERR("L2CAP connect failed\n");
goto fail;
}
rp.chan_id = chan->chan_id;
tester_send(BTP_SERVICE_ID_L2CAP, L2CAP_CONNECT, CONTROLLER_INDEX,
(u8_t *) &rp, sizeof(rp));
return;
fail:
tester_rsp(BTP_SERVICE_ID_L2CAP, L2CAP_CONNECT, CONTROLLER_INDEX,
BTP_STATUS_FAILED);
}
static void disconnect(u8_t *data, u16_t len)
{
const struct l2cap_disconnect_cmd *cmd = (void *) data;
struct channel *chan;
u8_t status;
int err;
SYS_LOG_DBG("");
chan = &channels[cmd->chan_id];
err = ble_l2cap_disconnect(chan->chan);
if (err) {
status = BTP_STATUS_FAILED;
goto rsp;
}
status = BTP_STATUS_SUCCESS;
rsp:
tester_rsp(BTP_SERVICE_ID_L2CAP, L2CAP_DISCONNECT, CONTROLLER_INDEX,
status);
}
static void send_data(u8_t *data, u16_t len)
{
const struct l2cap_send_data_cmd *cmd = (void *) data;
struct channel *chan = &channels[cmd->chan_id];
struct os_mbuf *sdu_tx = NULL;
int rc;
u16_t data_len = sys_le16_to_cpu(cmd->data_len);
SYS_LOG_DBG("cmd->chan_id=%d", cmd->chan_id);
/* FIXME: For now, fail if data length exceeds buffer length */
if (data_len > TESTER_COC_MTU) {
SYS_LOG_ERR("Data length exceeds buffer length");
goto fail;
}
sdu_tx = os_mbuf_get_pkthdr(&sdu_os_mbuf_pool, 0);
if (sdu_tx == NULL) {
SYS_LOG_ERR("No memory in the test sdu pool\n");
goto fail;
}
os_mbuf_append(sdu_tx, cmd->data, data_len);
rc = ble_l2cap_send(chan->chan, sdu_tx);
if (rc == BLE_HS_ESTALLED) {
/* Wait for TX_UNSTALLED event before sending response */
/* ble_l2cap_send takes ownership of the sdu */
return;
} else if (rc == 0) {
tester_rsp(BTP_SERVICE_ID_L2CAP, L2CAP_SEND_DATA, CONTROLLER_INDEX,
BTP_STATUS_SUCCESS);
return;
}
SYS_LOG_ERR("Unable to send data: %d", rc);
os_mbuf_free_chain(sdu_tx);
fail:
tester_rsp(BTP_SERVICE_ID_L2CAP, L2CAP_SEND_DATA, CONTROLLER_INDEX,
BTP_STATUS_FAILED);
}
static void listen(u8_t *data, u16_t len)
{
const struct l2cap_listen_cmd *cmd = (void *) data;
int rc;
SYS_LOG_DBG("");
/* TODO: Handle cmd->transport flag */
rc = ble_l2cap_create_server(cmd->psm, TESTER_COC_MTU,
tester_l2cap_event, NULL);
if (rc) {
goto fail;
}
tester_rsp(BTP_SERVICE_ID_L2CAP, L2CAP_LISTEN, CONTROLLER_INDEX,
BTP_STATUS_SUCCESS);
return;
fail:
tester_rsp(BTP_SERVICE_ID_L2CAP, L2CAP_LISTEN, CONTROLLER_INDEX,
BTP_STATUS_FAILED);
}
static void supported_commands(u8_t *data, u16_t len)
{
u8_t cmds[1];
struct l2cap_read_supported_commands_rp *rp = (void *) cmds;
memset(cmds, 0, sizeof(cmds));
tester_set_bit(cmds, L2CAP_READ_SUPPORTED_COMMANDS);
tester_set_bit(cmds, L2CAP_CONNECT);
tester_set_bit(cmds, L2CAP_DISCONNECT);
tester_set_bit(cmds, L2CAP_LISTEN);
tester_set_bit(cmds, L2CAP_SEND_DATA);
tester_send(BTP_SERVICE_ID_L2CAP, L2CAP_READ_SUPPORTED_COMMANDS,
CONTROLLER_INDEX, (u8_t *) rp, sizeof(cmds));
}
void tester_handle_l2cap(u8_t opcode, u8_t index, u8_t *data,
u16_t len)
{
switch (opcode) {
case L2CAP_READ_SUPPORTED_COMMANDS:
supported_commands(data, len);
return;
case L2CAP_CONNECT:
connect(data, len);
return;
case L2CAP_DISCONNECT:
disconnect(data, len);
return;
case L2CAP_SEND_DATA:
send_data(data, len);
return;
case L2CAP_LISTEN:
listen(data, len);
return;
default:
tester_rsp(BTP_SERVICE_ID_L2CAP, opcode, index,
BTP_STATUS_UNKNOWN_CMD);
return;
}
}
u8_t tester_init_l2cap(void)
{
int rc;
/* For testing we want to support all the available channels */
rc = os_mempool_init(&sdu_coc_mbuf_mempool, TESTER_COC_BUF_COUNT,
TESTER_COC_MTU, tester_sdu_coc_mem,
"tester_coc_sdu_pool");
assert(rc == 0);
rc = os_mbuf_pool_init(&sdu_os_mbuf_pool, &sdu_coc_mbuf_mempool,
TESTER_COC_MTU, TESTER_COC_BUF_COUNT);
assert(rc == 0);
return BTP_STATUS_SUCCESS;
}
u8_t tester_unregister_l2cap(void)
{
return BTP_STATUS_SUCCESS;
}
#endif

View File

@@ -0,0 +1,72 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
/* main.c - Application main entry point */
/*
* Copyright (c) 2015-2016 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "sysinit/sysinit.h"
#include "modlog/modlog.h"
#include "host/ble_uuid.h"
#include "host/ble_hs.h"
#include "bttester.h"
static void on_reset(int reason)
{
MODLOG_DFLT(ERROR, "Resetting state; reason=%d\n", reason);
}
static void on_sync(void)
{
MODLOG_DFLT(INFO, "Bluetooth initialized\n");
tester_init();
}
int main(int argc, char **argv)
{
int rc;
#ifdef ARCH_sim
mcu_sim_parse_args(argc, argv);
#endif
/* Initialize OS */
sysinit();
/* Initialize the NimBLE host configuration. */
ble_hs_cfg.reset_cb = on_reset;
ble_hs_cfg.sync_cb = on_sync;
ble_hs_cfg.gatts_register_cb = gatt_svr_register_cb,
ble_hs_cfg.store_status_cb = ble_store_util_status_rr;
rc = gatt_svr_init();
assert(rc == 0);
while (1) {
os_eventq_run(os_eventq_dflt_get());
}
return 0;
}

View File

@@ -0,0 +1,969 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
/* mesh.c - Bluetooth Mesh Tester */
/*
* Copyright (c) 2017 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "syscfg/syscfg.h"
#if MYNEWT_VAL(BLE_MESH)
#include <errno.h>
#include "mesh/mesh.h"
#include "mesh/glue.h"
#include "mesh/testing.h"
#include "console/console.h"
#include "bttester.h"
extern u8_t own_addr_type;
#define CONTROLLER_INDEX 0
#define CID_LOCAL 0xffff
/* Health server data */
#define CUR_FAULTS_MAX 4
#define HEALTH_TEST_ID 0x00
static u8_t cur_faults[CUR_FAULTS_MAX];
static u8_t reg_faults[CUR_FAULTS_MAX * 2];
/* Provision node data */
static u8_t net_key[16];
static u16_t net_key_idx;
static u8_t flags;
static u32_t iv_index;
static u16_t addr;
static u8_t dev_key[16];
static u8_t input_size;
/* Configured provisioning data */
static u8_t dev_uuid[16];
static u8_t static_auth[16];
/* Vendor Model data */
#define VND_MODEL_ID_1 0x1234
/* Model send data */
#define MODEL_BOUNDS_MAX 2
static struct model_data {
struct bt_mesh_model *model;
u16_t addr;
u16_t appkey_idx;
} model_bound[MODEL_BOUNDS_MAX];
static struct {
u16_t local;
u16_t dst;
u16_t net_idx;
} net = {
.local = BT_MESH_ADDR_UNASSIGNED,
.dst = BT_MESH_ADDR_UNASSIGNED,
};
static void supported_commands(u8_t *data, u16_t len)
{
struct os_mbuf *buf = NET_BUF_SIMPLE(BTP_DATA_MAX_SIZE);
net_buf_simple_init(buf, 0);
/* 1st octet */
memset(net_buf_simple_add(buf, 1), 0, 1);
tester_set_bit(buf->om_data, MESH_READ_SUPPORTED_COMMANDS);
tester_set_bit(buf->om_data, MESH_CONFIG_PROVISIONING);
tester_set_bit(buf->om_data, MESH_PROVISION_NODE);
tester_set_bit(buf->om_data, MESH_INIT);
tester_set_bit(buf->om_data, MESH_RESET);
tester_set_bit(buf->om_data, MESH_INPUT_NUMBER);
tester_set_bit(buf->om_data, MESH_INPUT_STRING);
/* 2nd octet */
tester_set_bit(buf->om_data, MESH_IVU_TEST_MODE);
tester_set_bit(buf->om_data, MESH_IVU_TOGGLE_STATE);
tester_set_bit(buf->om_data, MESH_NET_SEND);
tester_set_bit(buf->om_data, MESH_HEALTH_GENERATE_FAULTS);
tester_set_bit(buf->om_data, MESH_HEALTH_CLEAR_FAULTS);
tester_set_bit(buf->om_data, MESH_LPN);
tester_set_bit(buf->om_data, MESH_LPN_POLL);
tester_set_bit(buf->om_data, MESH_MODEL_SEND);
/* 3rd octet */
memset(net_buf_simple_add(buf, 1), 0, 1);
#if MYNEWT_VAL(BLE_MESH_TESTING)
tester_set_bit(buf->om_data, MESH_LPN_SUBSCRIBE);
tester_set_bit(buf->om_data, MESH_LPN_UNSUBSCRIBE);
tester_set_bit(buf->om_data, MESH_RPL_CLEAR);
#endif /* CONFIG_BT_TESTING */
tester_set_bit(buf->om_data, MESH_PROXY_IDENTITY);
tester_send(BTP_SERVICE_ID_MESH, MESH_READ_SUPPORTED_COMMANDS,
CONTROLLER_INDEX, buf->om_data, buf->om_len);
}
static struct bt_mesh_cfg_srv cfg_srv = {
.relay = BT_MESH_RELAY_ENABLED,
.beacon = BT_MESH_BEACON_ENABLED,
#if MYNEWT_VAL(BLE_MESH_FRIEND)
.frnd = BT_MESH_FRIEND_DISABLED,
#else
.frnd = BT_MESH_FRIEND_NOT_SUPPORTED,
#endif
#if MYNEWT_VAL(BLE_MESH_GATT_PROXY)
.gatt_proxy = BT_MESH_GATT_PROXY_ENABLED,
#else
.gatt_proxy = BT_MESH_GATT_PROXY_NOT_SUPPORTED,
#endif
.default_ttl = 7,
/* 3 transmissions with 20ms interval */
.net_transmit = BT_MESH_TRANSMIT(2, 20),
.relay_retransmit = BT_MESH_TRANSMIT(2, 20),
};
static void get_faults(u8_t *faults, u8_t faults_size, u8_t *dst, u8_t *count)
{
u8_t i, limit = *count;
for (i = 0, *count = 0; i < faults_size && *count < limit; i++) {
if (faults[i]) {
*dst++ = faults[i];
(*count)++;
}
}
}
static int fault_get_cur(struct bt_mesh_model *model, u8_t *test_id,
u16_t *company_id, u8_t *faults, u8_t *fault_count)
{
SYS_LOG_DBG("");
*test_id = HEALTH_TEST_ID;
*company_id = CID_LOCAL;
get_faults(cur_faults, sizeof(cur_faults), faults, fault_count);
return 0;
}
static int fault_get_reg(struct bt_mesh_model *model, u16_t company_id,
u8_t *test_id, u8_t *faults, u8_t *fault_count)
{
SYS_LOG_DBG("company_id 0x%04x", company_id);
if (company_id != CID_LOCAL) {
return -EINVAL;
}
*test_id = HEALTH_TEST_ID;
get_faults(reg_faults, sizeof(reg_faults), faults, fault_count);
return 0;
}
static int fault_clear(struct bt_mesh_model *model, uint16_t company_id)
{
SYS_LOG_DBG("company_id 0x%04x", company_id);
if (company_id != CID_LOCAL) {
return -EINVAL;
}
memset(reg_faults, 0, sizeof(reg_faults));
return 0;
}
static int fault_test(struct bt_mesh_model *model, uint8_t test_id,
uint16_t company_id)
{
SYS_LOG_DBG("test_id 0x%02x company_id 0x%04x", test_id, company_id);
if (company_id != CID_LOCAL || test_id != HEALTH_TEST_ID) {
return -EINVAL;
}
return 0;
}
static const struct bt_mesh_health_srv_cb health_srv_cb = {
.fault_get_cur = fault_get_cur,
.fault_get_reg = fault_get_reg,
.fault_clear = fault_clear,
.fault_test = fault_test,
};
static struct bt_mesh_health_srv health_srv = {
.cb = &health_srv_cb,
};
static struct bt_mesh_model_pub health_pub;
static void
health_pub_init(void)
{
health_pub.msg = BT_MESH_HEALTH_FAULT_MSG(CUR_FAULTS_MAX);
}
static struct bt_mesh_cfg_cli cfg_cli = {
};
void show_faults(u8_t test_id, u16_t cid, u8_t *faults, size_t fault_count)
{
size_t i;
if (!fault_count) {
SYS_LOG_DBG("Health Test ID 0x%02x Company ID 0x%04x: "
"no faults", test_id, cid);
return;
}
SYS_LOG_DBG("Health Test ID 0x%02x Company ID 0x%04x Fault Count %zu: ",
test_id, cid, fault_count);
for (i = 0; i < fault_count; i++) {
SYS_LOG_DBG("0x%02x", faults[i]);
}
}
static void health_current_status(struct bt_mesh_health_cli *cli, u16_t addr,
u8_t test_id, u16_t cid, u8_t *faults,
size_t fault_count)
{
SYS_LOG_DBG("Health Current Status from 0x%04x", addr);
show_faults(test_id, cid, faults, fault_count);
}
static struct bt_mesh_health_cli health_cli = {
.current_status = health_current_status,
};
static struct bt_mesh_model root_models[] = {
BT_MESH_MODEL_CFG_SRV(&cfg_srv),
BT_MESH_MODEL_CFG_CLI(&cfg_cli),
BT_MESH_MODEL_HEALTH_SRV(&health_srv, &health_pub),
BT_MESH_MODEL_HEALTH_CLI(&health_cli),
};
static struct bt_mesh_model vnd_models[] = {
BT_MESH_MODEL_VND(CID_LOCAL, VND_MODEL_ID_1, BT_MESH_MODEL_NO_OPS, NULL,
NULL),
};
static struct bt_mesh_elem elements[] = {
BT_MESH_ELEM(0, root_models, vnd_models),
};
static void link_open(bt_mesh_prov_bearer_t bearer)
{
struct mesh_prov_link_open_ev ev;
SYS_LOG_DBG("bearer 0x%02x", bearer);
switch (bearer) {
case BT_MESH_PROV_ADV:
ev.bearer = MESH_PROV_BEARER_PB_ADV;
break;
case BT_MESH_PROV_GATT:
ev.bearer = MESH_PROV_BEARER_PB_GATT;
break;
default:
SYS_LOG_ERR("Invalid bearer");
return;
}
tester_send(BTP_SERVICE_ID_MESH, MESH_EV_PROV_LINK_OPEN,
CONTROLLER_INDEX, (u8_t *) &ev, sizeof(ev));
}
static void link_close(bt_mesh_prov_bearer_t bearer)
{
struct mesh_prov_link_closed_ev ev;
SYS_LOG_DBG("bearer 0x%02x", bearer);
switch (bearer) {
case BT_MESH_PROV_ADV:
ev.bearer = MESH_PROV_BEARER_PB_ADV;
break;
case BT_MESH_PROV_GATT:
ev.bearer = MESH_PROV_BEARER_PB_GATT;
break;
default:
SYS_LOG_ERR("Invalid bearer");
return;
}
tester_send(BTP_SERVICE_ID_MESH, MESH_EV_PROV_LINK_CLOSED,
CONTROLLER_INDEX, (u8_t *) &ev, sizeof(ev));
}
static int output_number(bt_mesh_output_action_t action, u32_t number)
{
struct mesh_out_number_action_ev ev;
SYS_LOG_DBG("action 0x%04x number 0x%08lx", action, number);
ev.action = sys_cpu_to_le16(action);
ev.number = sys_cpu_to_le32(number);
tester_send(BTP_SERVICE_ID_MESH, MESH_EV_OUT_NUMBER_ACTION,
CONTROLLER_INDEX, (u8_t *) &ev, sizeof(ev));
return 0;
}
static int output_string(const char *str)
{
struct mesh_out_string_action_ev *ev;
struct os_mbuf *buf = NET_BUF_SIMPLE(BTP_DATA_MAX_SIZE);
SYS_LOG_DBG("str %s", str);
net_buf_simple_init(buf, 0);
ev = net_buf_simple_add(buf, sizeof(*ev));
ev->string_len = strlen(str);
net_buf_simple_add_mem(buf, str, ev->string_len);
tester_send(BTP_SERVICE_ID_MESH, MESH_EV_OUT_STRING_ACTION,
CONTROLLER_INDEX, buf->om_data, buf->om_len);
return 0;
}
static int input(bt_mesh_input_action_t action, u8_t size)
{
struct mesh_in_action_ev ev;
SYS_LOG_DBG("action 0x%04x number 0x%02x", action, size);
input_size = size;
ev.action = sys_cpu_to_le16(action);
ev.size = size;
tester_send(BTP_SERVICE_ID_MESH, MESH_EV_IN_ACTION, CONTROLLER_INDEX,
(u8_t *) &ev, sizeof(ev));
return 0;
}
static void prov_complete(u16_t net_idx, u16_t addr)
{
SYS_LOG_DBG("net_idx 0x%04x addr 0x%04x", net_idx, addr);
net.net_idx = net_idx,
net.local = addr;
net.dst = addr;
tester_send(BTP_SERVICE_ID_MESH, MESH_EV_PROVISIONED, CONTROLLER_INDEX,
NULL, 0);
}
static void prov_reset(void)
{
SYS_LOG_DBG("");
bt_mesh_prov_enable(BT_MESH_PROV_ADV | BT_MESH_PROV_GATT);
}
static const struct bt_mesh_comp comp = {
.cid = CID_LOCAL,
.elem = elements,
.elem_count = ARRAY_SIZE(elements),
};
static struct bt_mesh_prov prov = {
.uuid = dev_uuid,
.static_val = static_auth,
.static_val_len = sizeof(static_auth),
.output_number = output_number,
.output_string = output_string,
.input = input,
.link_open = link_open,
.link_close = link_close,
.complete = prov_complete,
.reset = prov_reset,
};
static void config_prov(u8_t *data, u16_t len)
{
const struct mesh_config_provisioning_cmd *cmd = (void *) data;
SYS_LOG_DBG("");
memcpy(dev_uuid, cmd->uuid, sizeof(dev_uuid));
memcpy(static_auth, cmd->static_auth, sizeof(static_auth));
prov.output_size = cmd->out_size;
prov.output_actions = sys_le16_to_cpu(cmd->out_actions);
prov.input_size = cmd->in_size;
prov.input_actions = sys_le16_to_cpu(cmd->in_actions);
tester_rsp(BTP_SERVICE_ID_MESH, MESH_CONFIG_PROVISIONING,
CONTROLLER_INDEX, BTP_STATUS_SUCCESS);
}
static void provision_node(u8_t *data, u16_t len)
{
const struct mesh_provision_node_cmd *cmd = (void *) data;
SYS_LOG_DBG("");
memcpy(dev_key, cmd->dev_key, sizeof(dev_key));
memcpy(net_key, cmd->net_key, sizeof(net_key));
addr = sys_le16_to_cpu(cmd->addr);
flags = cmd->flags;
iv_index = sys_le32_to_cpu(cmd->iv_index);
net_key_idx = sys_le16_to_cpu(cmd->net_key_idx);
tester_rsp(BTP_SERVICE_ID_MESH, MESH_PROVISION_NODE,
CONTROLLER_INDEX, BTP_STATUS_SUCCESS);
}
static void init(u8_t *data, u16_t len)
{
u8_t status = BTP_STATUS_SUCCESS;
int err;
SYS_LOG_DBG("");
err = bt_mesh_init(own_addr_type, &prov, &comp);
if (err) {
status = BTP_STATUS_FAILED;
goto rsp;
}
if (addr) {
err = bt_mesh_provision(net_key, net_key_idx, flags, iv_index,
addr, dev_key);
if (err) {
status = BTP_STATUS_FAILED;
}
} else {
err = bt_mesh_prov_enable(BT_MESH_PROV_ADV | BT_MESH_PROV_GATT);
if (err) {
status = BTP_STATUS_FAILED;
}
}
/* Set device key for vendor model */
vnd_models[0].keys[0] = BT_MESH_KEY_DEV;
rsp:
tester_rsp(BTP_SERVICE_ID_MESH, MESH_INIT, CONTROLLER_INDEX,
status);
}
static void reset(u8_t *data, u16_t len)
{
SYS_LOG_DBG("");
bt_mesh_reset();
tester_rsp(BTP_SERVICE_ID_MESH, MESH_RESET, CONTROLLER_INDEX,
BTP_STATUS_SUCCESS);
}
static void input_number(u8_t *data, u16_t len)
{
const struct mesh_input_number_cmd *cmd = (void *) data;
u8_t status = BTP_STATUS_SUCCESS;
u32_t number;
int err;
number = sys_le32_to_cpu(cmd->number);
SYS_LOG_DBG("number 0x%04lx", number);
err = bt_mesh_input_number(number);
if (err) {
status = BTP_STATUS_FAILED;
}
tester_rsp(BTP_SERVICE_ID_MESH, MESH_INPUT_NUMBER, CONTROLLER_INDEX,
status);
}
static void input_string(u8_t *data, u16_t len)
{
const struct mesh_input_string_cmd *cmd = (void *) data;
u8_t status = BTP_STATUS_SUCCESS;
u8_t str_auth[16];
int err;
SYS_LOG_DBG("");
if (cmd->string_len > sizeof(str_auth)) {
SYS_LOG_ERR("Too long input (%u chars required)", input_size);
status = BTP_STATUS_FAILED;
goto rsp;
} else if (cmd->string_len < input_size) {
SYS_LOG_ERR("Too short input (%u chars required)", input_size);
status = BTP_STATUS_FAILED;
goto rsp;
}
strncpy((char *)str_auth, (char *)cmd->string, cmd->string_len);
err = bt_mesh_input_string((char *)str_auth);
if (err) {
status = BTP_STATUS_FAILED;
}
rsp:
tester_rsp(BTP_SERVICE_ID_MESH, MESH_INPUT_STRING, CONTROLLER_INDEX,
status);
}
static void ivu_test_mode(u8_t *data, u16_t len)
{
const struct mesh_ivu_test_mode_cmd *cmd = (void *) data;
SYS_LOG_DBG("enable 0x%02x", cmd->enable);
bt_mesh_iv_update_test(cmd->enable ? true : false);
tester_rsp(BTP_SERVICE_ID_MESH, MESH_IVU_TEST_MODE, CONTROLLER_INDEX,
BTP_STATUS_SUCCESS);
}
static void ivu_toggle_state(u8_t *data, u16_t len)
{
bool result;
SYS_LOG_DBG("");
result = bt_mesh_iv_update();
if (!result) {
SYS_LOG_ERR("Failed to toggle the IV Update state");
}
tester_rsp(BTP_SERVICE_ID_MESH, MESH_IVU_TOGGLE_STATE, CONTROLLER_INDEX,
result ? BTP_STATUS_SUCCESS : BTP_STATUS_FAILED);
}
static void lpn(u8_t *data, u16_t len)
{
struct mesh_lpn_set_cmd *cmd = (void *) data;
bool enable;
int err;
SYS_LOG_DBG("enable 0x%02x", cmd->enable);
enable = cmd->enable ? true : false;
err = bt_mesh_lpn_set(enable);
if (err) {
SYS_LOG_ERR("Failed to toggle LPN (err %d)", err);
}
tester_rsp(BTP_SERVICE_ID_MESH, MESH_LPN, CONTROLLER_INDEX,
err ? BTP_STATUS_FAILED : BTP_STATUS_SUCCESS);
}
static void lpn_poll(u8_t *data, u16_t len)
{
int err;
SYS_LOG_DBG("");
err = bt_mesh_lpn_poll();
if (err) {
SYS_LOG_ERR("Failed to send poll msg (err %d)", err);
}
tester_rsp(BTP_SERVICE_ID_MESH, MESH_LPN_POLL, CONTROLLER_INDEX,
err ? BTP_STATUS_FAILED : BTP_STATUS_SUCCESS);
}
static void net_send(u8_t *data, u16_t len)
{
struct mesh_net_send_cmd *cmd = (void *) data;
struct os_mbuf *msg = NET_BUF_SIMPLE(UINT8_MAX);
struct bt_mesh_msg_ctx ctx = {
.net_idx = net.net_idx,
.app_idx = BT_MESH_KEY_DEV,
.addr = sys_le16_to_cpu(cmd->dst),
.send_ttl = cmd->ttl,
};
int err;
SYS_LOG_DBG("ttl 0x%02x dst 0x%04x payload_len %d", ctx.send_ttl,
ctx.addr, cmd->payload_len);
net_buf_simple_add_mem(msg, cmd->payload, cmd->payload_len);
err = bt_mesh_model_send(&vnd_models[0], &ctx, msg, NULL, NULL);
if (err) {
SYS_LOG_ERR("Failed to send (err %d)", err);
}
tester_rsp(BTP_SERVICE_ID_MESH, MESH_NET_SEND, CONTROLLER_INDEX,
err ? BTP_STATUS_FAILED : BTP_STATUS_SUCCESS);
os_mbuf_free_chain(msg);
}
static void health_generate_faults(u8_t *data, u16_t len)
{
struct mesh_health_generate_faults_rp *rp;
struct os_mbuf *buf = NET_BUF_SIMPLE(sizeof(*rp) + sizeof(cur_faults) +
sizeof(reg_faults));
u8_t some_faults[] = { 0x01, 0x02, 0x03, 0xff, 0x06 };
u8_t cur_faults_count, reg_faults_count;
rp = net_buf_simple_add(buf, sizeof(*rp));
cur_faults_count = min(sizeof(cur_faults), sizeof(some_faults));
memcpy(cur_faults, some_faults, cur_faults_count);
net_buf_simple_add_mem(buf, cur_faults, cur_faults_count);
rp->cur_faults_count = cur_faults_count;
reg_faults_count = min(sizeof(reg_faults), sizeof(some_faults));
memcpy(reg_faults, some_faults, reg_faults_count);
net_buf_simple_add_mem(buf, reg_faults, reg_faults_count);
rp->reg_faults_count = reg_faults_count;
bt_mesh_fault_update(&elements[0]);
tester_send(BTP_SERVICE_ID_MESH, MESH_HEALTH_GENERATE_FAULTS,
CONTROLLER_INDEX, buf->om_data, buf->om_len);
}
static void health_clear_faults(u8_t *data, u16_t len)
{
SYS_LOG_DBG("");
memset(cur_faults, 0, sizeof(cur_faults));
memset(reg_faults, 0, sizeof(reg_faults));
bt_mesh_fault_update(&elements[0]);
tester_rsp(BTP_SERVICE_ID_MESH, MESH_HEALTH_CLEAR_FAULTS,
CONTROLLER_INDEX, BTP_STATUS_SUCCESS);
}
static void model_send(u8_t *data, u16_t len)
{
struct mesh_model_send_cmd *cmd = (void *) data;
struct os_mbuf *msg = NET_BUF_SIMPLE(UINT8_MAX);
struct bt_mesh_msg_ctx ctx = {
.net_idx = net.net_idx,
.app_idx = BT_MESH_KEY_DEV,
.addr = sys_le16_to_cpu(cmd->dst),
.send_ttl = BT_MESH_TTL_DEFAULT,
};
struct bt_mesh_model *model = NULL;
int err, i;
u16_t src = sys_le16_to_cpu(cmd->src);
/* Lookup source address */
for (i = 0; i < ARRAY_SIZE(model_bound); i++) {
if (bt_mesh_model_elem(model_bound[i].model)->addr == src) {
model = model_bound[i].model;
ctx.app_idx = model_bound[i].appkey_idx;
break;
}
}
if (!model) {
SYS_LOG_ERR("Model not found");
err = -EINVAL;
goto fail;
}
SYS_LOG_DBG("src 0x%04x dst 0x%04x model %p payload_len %d", src,
ctx.addr, model, cmd->payload_len);
net_buf_simple_add_mem(msg, cmd->payload, cmd->payload_len);
err = bt_mesh_model_send(model, &ctx, msg, NULL, NULL);
if (err) {
SYS_LOG_ERR("Failed to send (err %d)", err);
}
fail:
tester_rsp(BTP_SERVICE_ID_MESH, MESH_MODEL_SEND, CONTROLLER_INDEX,
err ? BTP_STATUS_FAILED : BTP_STATUS_SUCCESS);
os_mbuf_free_chain(msg);
}
#if MYNEWT_VAL(BLE_MESH_TESTING)
static void lpn_subscribe(u8_t *data, u16_t len)
{
struct mesh_lpn_subscribe_cmd *cmd = (void *) data;
u16_t address = sys_le16_to_cpu(cmd->address);
int err;
SYS_LOG_DBG("address 0x%04x", address);
err = bt_test_mesh_lpn_group_add(address);
if (err) {
SYS_LOG_ERR("Failed to subscribe (err %d)", err);
}
tester_rsp(BTP_SERVICE_ID_MESH, MESH_LPN_SUBSCRIBE, CONTROLLER_INDEX,
err ? BTP_STATUS_FAILED : BTP_STATUS_SUCCESS);
}
static void lpn_unsubscribe(u8_t *data, u16_t len)
{
struct mesh_lpn_unsubscribe_cmd *cmd = (void *) data;
u16_t address = sys_le16_to_cpu(cmd->address);
int err;
SYS_LOG_DBG("address 0x%04x", address);
err = bt_test_mesh_lpn_group_remove(&address, 1);
if (err) {
SYS_LOG_ERR("Failed to unsubscribe (err %d)", err);
}
tester_rsp(BTP_SERVICE_ID_MESH, MESH_LPN_UNSUBSCRIBE, CONTROLLER_INDEX,
err ? BTP_STATUS_FAILED : BTP_STATUS_SUCCESS);
}
static void rpl_clear(u8_t *data, u16_t len)
{
int err;
SYS_LOG_DBG("");
err = bt_test_mesh_rpl_clear();
if (err) {
SYS_LOG_ERR("Failed to clear RPL (err %d)", err);
}
tester_rsp(BTP_SERVICE_ID_MESH, MESH_RPL_CLEAR, CONTROLLER_INDEX,
err ? BTP_STATUS_FAILED : BTP_STATUS_SUCCESS);
}
#endif /* MYNEWT_VAL(BLE_MESH_TESTING) */
static void proxy_identity_enable(u8_t *data, u16_t len)
{
int err;
SYS_LOG_DBG("");
err = bt_mesh_proxy_identity_enable();
if (err) {
SYS_LOG_ERR("Failed to enable proxy identity (err %d)", err);
}
tester_rsp(BTP_SERVICE_ID_MESH, MESH_PROXY_IDENTITY, CONTROLLER_INDEX,
err ? BTP_STATUS_FAILED : BTP_STATUS_SUCCESS);
}
void tester_handle_mesh(u8_t opcode, u8_t index, u8_t *data, u16_t len)
{
switch (opcode) {
case MESH_READ_SUPPORTED_COMMANDS:
supported_commands(data, len);
break;
case MESH_CONFIG_PROVISIONING:
config_prov(data, len);
break;
case MESH_PROVISION_NODE:
provision_node(data, len);
break;
case MESH_INIT:
init(data, len);
break;
case MESH_RESET:
reset(data, len);
break;
case MESH_INPUT_NUMBER:
input_number(data, len);
break;
case MESH_INPUT_STRING:
input_string(data, len);
break;
case MESH_IVU_TEST_MODE:
ivu_test_mode(data, len);
break;
case MESH_IVU_TOGGLE_STATE:
ivu_toggle_state(data, len);
break;
case MESH_LPN:
lpn(data, len);
break;
case MESH_LPN_POLL:
lpn_poll(data, len);
break;
case MESH_NET_SEND:
net_send(data, len);
break;
case MESH_HEALTH_GENERATE_FAULTS:
health_generate_faults(data, len);
break;
case MESH_HEALTH_CLEAR_FAULTS:
health_clear_faults(data, len);
break;
case MESH_MODEL_SEND:
model_send(data, len);
break;
#if MYNEWT_VAL(BLE_MESH_TESTING)
case MESH_LPN_SUBSCRIBE:
lpn_subscribe(data, len);
break;
case MESH_LPN_UNSUBSCRIBE:
lpn_unsubscribe(data, len);
break;
case MESH_RPL_CLEAR:
rpl_clear(data, len);
break;
#endif /* MYNEWT_VAL(BLE_MESH_TESTING) */
case MESH_PROXY_IDENTITY:
proxy_identity_enable(data, len);
break;
default:
tester_rsp(BTP_SERVICE_ID_MESH, opcode, index,
BTP_STATUS_UNKNOWN_CMD);
break;
}
}
void net_recv_ev(u8_t ttl, u8_t ctl, u16_t src, u16_t dst, const void *payload,
size_t payload_len)
{
struct os_mbuf *buf = NET_BUF_SIMPLE(UINT8_MAX);
struct mesh_net_recv_ev *ev;
SYS_LOG_DBG("ttl 0x%02x ctl 0x%02x src 0x%04x dst 0x%04x "
"payload_len %d", ttl, ctl, src, dst, payload_len);
if (payload_len > net_buf_simple_tailroom(buf)) {
SYS_LOG_ERR("Payload size exceeds buffer size");
goto done;
}
ev = net_buf_simple_add(buf, sizeof(*ev));
ev->ttl = ttl;
ev->ctl = ctl;
ev->src = sys_cpu_to_le16(src);
ev->dst = sys_cpu_to_le16(dst);
ev->payload_len = payload_len;
net_buf_simple_add_mem(buf, payload, payload_len);
tester_send(BTP_SERVICE_ID_MESH, MESH_EV_NET_RECV, CONTROLLER_INDEX,
buf->om_data, buf->om_len);
done:
os_mbuf_free_chain(buf);
}
static void model_bound_cb(u16_t addr, struct bt_mesh_model *model,
u16_t key_idx)
{
int i;
SYS_LOG_DBG("remote addr 0x%04x key_idx 0x%04x model %p",
addr, key_idx, model);
for (i = 0; i < ARRAY_SIZE(model_bound); i++) {
if (!model_bound[i].model) {
model_bound[i].model = model;
model_bound[i].addr = addr;
model_bound[i].appkey_idx = key_idx;
return;
}
}
SYS_LOG_ERR("model_bound is full");
}
static void model_unbound_cb(u16_t addr, struct bt_mesh_model *model,
u16_t key_idx)
{
int i;
SYS_LOG_DBG("remote addr 0x%04x key_idx 0x%04x model %p",
addr, key_idx, model);
for (i = 0; i < ARRAY_SIZE(model_bound); i++) {
if (model_bound[i].model == model) {
model_bound[i].model = NULL;
model_bound[i].addr = 0x0000;
model_bound[i].appkey_idx = BT_MESH_KEY_UNUSED;
return;
}
}
SYS_LOG_INF("model not found");
}
static void invalid_bearer_cb(u8_t opcode)
{
struct mesh_invalid_bearer_ev ev = {
.opcode = opcode,
};
SYS_LOG_DBG("opcode 0x%02x", opcode);
tester_send(BTP_SERVICE_ID_MESH, MESH_EV_INVALID_BEARER,
CONTROLLER_INDEX, (u8_t *) &ev, sizeof(ev));
}
static void incomp_timer_exp_cb(void)
{
tester_send(BTP_SERVICE_ID_MESH, MESH_EV_INCOMP_TIMER_EXP,
CONTROLLER_INDEX, NULL, 0);
}
static struct bt_test_cb bt_test_cb = {
.mesh_net_recv = net_recv_ev,
.mesh_model_bound = model_bound_cb,
.mesh_model_unbound = model_unbound_cb,
.mesh_prov_invalid_bearer = invalid_bearer_cb,
.mesh_trans_incomp_timer_exp = incomp_timer_exp_cb,
};
u8_t tester_init_mesh(void)
{
health_pub_init();
if (IS_ENABLED(CONFIG_BT_TESTING)) {
bt_test_cb_register(&bt_test_cb);
}
return BTP_STATUS_SUCCESS;
}
u8_t tester_unregister_mesh(void)
{
return BTP_STATUS_SUCCESS;
}
#endif /* MYNEWT_VAL(BLE_MESH) */

Some files were not shown because too many files have changed in this diff Show More