blob: eccb3e98828057cac15fb00f50c17f3976fb662a [file] [log] [blame]
/*
* 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;
}