/*
|
* 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 "os/endian.h"
|
#include "host/ble_eddystone.h"
|
#include "host/ble_hs_adv.h"
|
#include "ble_hs_priv.h"
|
|
#define BLE_EDDYSTONE_MAX_SVC_DATA_LEN 22
|
#define BLE_EDDYSTONE_SVC_DATA_BASE_SZ 3
|
|
#define BLE_EDDYSTONE_SERVICE_UUID 0xfeaa
|
|
#define BLE_EDDYSTONE_FRAME_TYPE_UID 0x00
|
#define BLE_EDDYSTONE_FRAME_TYPE_URL 0x10
|
|
static ble_uuid16_t ble_eddystone_uuids16[BLE_EDDYSTONE_MAX_UUIDS16 + 1];
|
static uint8_t ble_eddystone_svc_data[BLE_EDDYSTONE_MAX_SVC_DATA_LEN];
|
|
/**
|
* Writes an eddystone header to the global service data buffer.
|
*
|
* @param frame_type The eddystone frame type; one of the
|
* BLE_EDDYSTONE_FRAME_TYPE_[...] values.
|
*
|
* @return A pointer to where the service data payload
|
* should be written.
|
*/
|
static void *
|
ble_eddystone_set_svc_data_base(uint8_t frame_type)
|
{
|
put_le16(ble_eddystone_svc_data, BLE_EDDYSTONE_SERVICE_UUID);
|
ble_eddystone_svc_data[2] = frame_type;
|
|
return ble_eddystone_svc_data + BLE_EDDYSTONE_SVC_DATA_BASE_SZ;
|
}
|
|
/**
|
* Populates the supplied advertisement fields struct to represent an eddystone
|
* advertisement. Prior to calling this function, you must write the service
|
* data header and payload using the ble_eddystone_set_svc_data_base()
|
* function.
|
*
|
* @param adv_fields The base advertisement fields to transform into
|
* an eddystone beacon. All configured fields
|
* are preserved; you probably want to clear
|
* this struct before calling this function.
|
* @param svc_data_len The amount of data written to the global
|
* service data buffer.
|
*
|
* @return 0 on success; BLE_HS_E... on failure.
|
*/
|
static int
|
ble_eddystone_set_adv_data_gen(struct ble_hs_adv_fields *adv_fields,
|
uint8_t svc_data_len)
|
{
|
int rc;
|
|
if (adv_fields->num_uuids16 > BLE_EDDYSTONE_MAX_UUIDS16) {
|
return BLE_HS_EINVAL;
|
}
|
if (svc_data_len > (BLE_EDDYSTONE_MAX_SVC_DATA_LEN - BLE_EDDYSTONE_SVC_DATA_BASE_SZ)) {
|
return BLE_HS_EINVAL;
|
}
|
if (adv_fields->num_uuids16 > 0 && !adv_fields->uuids16_is_complete) {
|
return BLE_HS_EINVAL;
|
}
|
if (adv_fields->svc_data_uuid16_len != 0) {
|
return BLE_HS_EINVAL;
|
}
|
|
ble_eddystone_uuids16[0] =
|
(ble_uuid16_t) BLE_UUID16_INIT(BLE_EDDYSTONE_SERVICE_UUID);
|
memcpy(ble_eddystone_uuids16 + 1, adv_fields->uuids16,
|
adv_fields->num_uuids16 * sizeof(ble_uuid16_t));
|
adv_fields->uuids16 = ble_eddystone_uuids16;
|
adv_fields->num_uuids16++;
|
adv_fields->uuids16_is_complete = 1;
|
|
adv_fields->svc_data_uuid16 = ble_eddystone_svc_data;
|
adv_fields->svc_data_uuid16_len = svc_data_len +
|
BLE_EDDYSTONE_SVC_DATA_BASE_SZ;
|
|
rc = ble_gap_adv_set_fields(adv_fields);
|
if (rc != 0) {
|
return rc;
|
}
|
|
return 0;
|
}
|
|
int
|
ble_eddystone_set_adv_data_uid(struct ble_hs_adv_fields *adv_fields,
|
void *uid, int8_t measured_power)
|
{
|
uint8_t *svc_data;
|
int rc;
|
|
/* Eddystone UUID and frame type (0). */
|
svc_data = ble_eddystone_set_svc_data_base(BLE_EDDYSTONE_FRAME_TYPE_UID);
|
|
/* Measured Power ranging data (Calibrated tx power at 0 meters). */
|
if (measured_power < -100 || measured_power > 20) {
|
return BLE_HS_EINVAL;
|
}
|
svc_data[0] = measured_power;
|
|
/* UID. */
|
memcpy(svc_data + 1, uid, 16);
|
|
/* Reserved. */
|
svc_data[17] = 0x00;
|
svc_data[18] = 0x00;
|
|
rc = ble_eddystone_set_adv_data_gen(adv_fields, 19);
|
if (rc != 0) {
|
return rc;
|
}
|
|
return 0;
|
}
|
|
int
|
ble_eddystone_set_adv_data_url(struct ble_hs_adv_fields *adv_fields,
|
uint8_t url_scheme, char *url_body,
|
uint8_t url_body_len, uint8_t url_suffix,
|
int8_t measured_power)
|
{
|
uint8_t *svc_data;
|
int url_len;
|
int rc;
|
|
url_len = url_body_len;
|
if (url_suffix != BLE_EDDYSTONE_URL_SUFFIX_NONE) {
|
url_len++;
|
}
|
if (url_len > BLE_EDDYSTONE_URL_MAX_LEN) {
|
return BLE_HS_EINVAL;
|
}
|
|
svc_data = ble_eddystone_set_svc_data_base(BLE_EDDYSTONE_FRAME_TYPE_URL);
|
|
/* Measured Power ranging data (Calibrated tx power at 0 meters). */
|
if (measured_power < -100 || measured_power > 20) {
|
return BLE_HS_EINVAL;
|
}
|
svc_data[0] = measured_power;
|
|
svc_data[1] = url_scheme;
|
memcpy(svc_data + 2, url_body, url_body_len);
|
if (url_suffix != BLE_EDDYSTONE_URL_SUFFIX_NONE) {
|
svc_data[2 + url_body_len] = url_suffix;
|
}
|
|
rc = ble_eddystone_set_adv_data_gen(adv_fields, url_len + 2);
|
if (rc != 0) {
|
return rc;
|
}
|
|
return 0;
|
}
|