/* * 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 #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; }