1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19
20 #include <string.h>
21 #include "securec.h"
22 #include "os/endian.h"
23 #include "host/ble_hs_adv.h"
24 #include "ble_hs_priv.h"
25 #include "host/ble_eddystone.h"
26
27 #define BLE_EDDYSTONE_MAX_SVC_DATA_LEN 22
28 #define BLE_EDDYSTONE_SVC_DATA_BASE_SZ 3
29
30 #define BLE_EDDYSTONE_SERVICE_UUID 0xfeaa
31
32 #define BLE_EDDYSTONE_FRAME_TYPE_UID 0x00
33 #define BLE_EDDYSTONE_FRAME_TYPE_URL 0x10
34
35 static ble_uuid16_t ble_eddystone_uuids16[BLE_EDDYSTONE_MAX_UUIDS16 + 1];
36 static uint8_t ble_eddystone_svc_data[BLE_EDDYSTONE_MAX_SVC_DATA_LEN];
37
38 /**
39 * Writes an eddystone header to the global service data buffer.
40 *
41 * @param frame_type The eddystone frame type; one of the
42 * BLE_EDDYSTONE_FRAME_TYPE_[...] values.
43 *
44 * @return A pointer to where the service data payload
45 * should be written.
46 */
ble_eddystone_set_svc_data_base(uint8_t frame_type)47 static void *ble_eddystone_set_svc_data_base(uint8_t frame_type)
48 {
49 put_le16(ble_eddystone_svc_data, BLE_EDDYSTONE_SERVICE_UUID);
50 ble_eddystone_svc_data[2] = frame_type; // 2:array element
51 return ble_eddystone_svc_data + BLE_EDDYSTONE_SVC_DATA_BASE_SZ;
52 }
53
54 /**
55 * Populates the supplied advertisement fields struct to represent an eddystone
56 * advertisement. Prior to calling this function, you must write the service
57 * data header and payload using the ble_eddystone_set_svc_data_base()
58 * function.
59 *
60 * @param adv_fields The base advertisement fields to transform into
61 * an eddystone beacon. All configured fields
62 * are preserved; you probably want to clear
63 * this struct before calling this function.
64 * @param svc_data_len The amount of data written to the global
65 * service data buffer.
66 *
67 * @return 0 on success; BLE_HS_E... on failure.
68 */
ble_eddystone_set_adv_data_gen(struct ble_hs_adv_fields * adv_fields,uint8_t svc_data_len)69 static int ble_eddystone_set_adv_data_gen(struct ble_hs_adv_fields *adv_fields, uint8_t svc_data_len)
70 {
71 int rc;
72
73 if (adv_fields->num_uuids16 > BLE_EDDYSTONE_MAX_UUIDS16) {
74 return BLE_HS_EINVAL;
75 }
76
77 if (svc_data_len > BLE_EDDYSTONE_MAX_SVC_DATA_LEN) {
78 return BLE_HS_EINVAL;
79 }
80
81 if (adv_fields->num_uuids16 > 0 && !adv_fields->uuids16_is_complete) {
82 return BLE_HS_EINVAL;
83 }
84
85 if (adv_fields->svc_data_uuid16_len != 0) {
86 return BLE_HS_EINVAL;
87 }
88
89 ble_eddystone_uuids16[0] = (ble_uuid16_t) BLE_UUID16_INIT(BLE_EDDYSTONE_SERVICE_UUID);
90 memcpy_s(ble_eddystone_uuids16 + 1, sizeof(ble_eddystone_uuids16 + 1),
91 adv_fields->uuids16, adv_fields->num_uuids16 * sizeof(ble_uuid16_t));
92 adv_fields->uuids16 = ble_eddystone_uuids16;
93 adv_fields->num_uuids16++;
94 adv_fields->uuids16_is_complete = 1;
95 adv_fields->svc_data_uuid16 = ble_eddystone_svc_data;
96 adv_fields->svc_data_uuid16_len = svc_data_len +
97 BLE_EDDYSTONE_SVC_DATA_BASE_SZ;
98 rc = ble_gap_adv_set_fields(adv_fields);
99 if (rc != 0) {
100 return rc;
101 }
102
103 return 0;
104 }
105
ble_eddystone_set_adv_data_uid(struct ble_hs_adv_fields * adv_fields,void * uid,int8_t measured_power)106 int ble_eddystone_set_adv_data_uid(struct ble_hs_adv_fields *adv_fields,
107 void *uid, int8_t measured_power)
108 {
109 uint8_t *svc_data;
110 int rc;
111 /* Eddystone UUID and frame type (0). */
112 svc_data = ble_eddystone_set_svc_data_base(BLE_EDDYSTONE_FRAME_TYPE_UID);
113
114 /* Measured Power ranging data (Calibrated tx power at 0 meters). */
115 if (measured_power < -100 || measured_power > 20) { // -100:Analyzing conditions, 20:Analyzing conditions
116 return BLE_HS_EINVAL;
117 }
118
119 svc_data[0] = measured_power;
120 /* UID. */
121 memcpy_s(svc_data + 1, sizeof(svc_data + 1), uid, 16); // 16:size
122 /* Reserved. */
123 svc_data[17] = 0x00; // 17:array element
124 svc_data[18] = 0x00; // 18:array element
125 rc = ble_eddystone_set_adv_data_gen(adv_fields, 19); // 19:svc_data_len
126 if (rc != 0) {
127 return rc;
128 }
129
130 return 0;
131 }
132
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)133 int ble_eddystone_set_adv_data_url(struct ble_hs_adv_fields *adv_fields,
134 uint8_t url_scheme, char *url_body,
135 uint8_t url_body_len, uint8_t url_suffix,
136 int8_t measured_power)
137 {
138 uint8_t *svc_data;
139 int url_len;
140 int rc;
141 url_len = url_body_len;
142
143 if (url_suffix != BLE_EDDYSTONE_URL_SUFFIX_NONE) {
144 url_len++;
145 }
146
147 if (url_len > BLE_EDDYSTONE_URL_MAX_LEN) {
148 return BLE_HS_EINVAL;
149 }
150
151 svc_data = ble_eddystone_set_svc_data_base(BLE_EDDYSTONE_FRAME_TYPE_URL);
152
153 /* Measured Power ranging data (Calibrated tx power at 0 meters). */
154 if (measured_power < -100 || measured_power > 20) { // -100:Analyzing conditions, 20:Analyzing conditions
155 return BLE_HS_EINVAL;
156 }
157
158 svc_data[0] = measured_power;
159 svc_data[1] = url_scheme;
160 memcpy_s(svc_data + 2, sizeof(svc_data + 2), url_body, url_body_len); // 2:byte alignment
161
162 if (url_suffix != BLE_EDDYSTONE_URL_SUFFIX_NONE) {
163 svc_data[2 + url_body_len] = url_suffix; // 2:byte alignment
164 }
165
166 rc = ble_eddystone_set_adv_data_gen(adv_fields, url_len + 2); // 2:byte alignment
167 if (rc != 0) {
168 return rc;
169 }
170
171 return 0;
172 }