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 "host/ble_eddystone.h"
22 #include "host/ble_hs_adv.h"
23 #include "ble_hs_priv.h"
24
25 #define BLE_EDDYSTONE_MAX_SVC_DATA_LEN 22
26 #define BLE_EDDYSTONE_SVC_DATA_BASE_SZ 3
27
28 #define BLE_EDDYSTONE_SERVICE_UUID 0xfeaa
29
30 #define BLE_EDDYSTONE_FRAME_TYPE_UID 0x00
31 #define BLE_EDDYSTONE_FRAME_TYPE_URL 0x10
32
33 static ble_uuid16_t ble_eddystone_uuids16[BLE_EDDYSTONE_MAX_UUIDS16 + 1];
34 static uint8_t ble_eddystone_svc_data[BLE_EDDYSTONE_MAX_SVC_DATA_LEN];
35
36 /**
37 * Writes an eddystone header to the global service data buffer.
38 *
39 * @param frame_type The eddystone frame type; one of the
40 * BLE_EDDYSTONE_FRAME_TYPE_[...] values.
41 *
42 * @return A pointer to where the service data payload
43 * should be written.
44 */
45 static void *
ble_eddystone_set_svc_data_base(uint8_t frame_type)46 ble_eddystone_set_svc_data_base(uint8_t frame_type)
47 {
48 put_le16(ble_eddystone_svc_data, BLE_EDDYSTONE_SERVICE_UUID);
49 ble_eddystone_svc_data[2] = frame_type;
50
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 */
69 static int
ble_eddystone_set_adv_data_gen(struct ble_hs_adv_fields * adv_fields,uint8_t svc_data_len)70 ble_eddystone_set_adv_data_gen(struct ble_hs_adv_fields *adv_fields,
71 uint8_t svc_data_len)
72 {
73 int rc;
74
75 if (adv_fields->num_uuids16 > BLE_EDDYSTONE_MAX_UUIDS16) {
76 return BLE_HS_EINVAL;
77 }
78 if (svc_data_len > BLE_EDDYSTONE_MAX_SVC_DATA_LEN) {
79 return BLE_HS_EINVAL;
80 }
81 if (adv_fields->num_uuids16 > 0 && !adv_fields->uuids16_is_complete) {
82 return BLE_HS_EINVAL;
83 }
84 if (adv_fields->svc_data_uuid16_len != 0) {
85 return BLE_HS_EINVAL;
86 }
87
88 ble_eddystone_uuids16[0] =
89 (ble_uuid16_t) BLE_UUID16_INIT(BLE_EDDYSTONE_SERVICE_UUID);
90 memcpy(ble_eddystone_uuids16 + 1, adv_fields->uuids16,
91 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
96 adv_fields->svc_data_uuid16 = ble_eddystone_svc_data;
97 adv_fields->svc_data_uuid16_len = svc_data_len +
98 BLE_EDDYSTONE_SVC_DATA_BASE_SZ;
99
100 rc = ble_gap_adv_set_fields(adv_fields);
101 if (rc != 0) {
102 return rc;
103 }
104
105 return 0;
106 }
107
108 int
ble_eddystone_set_adv_data_uid(struct ble_hs_adv_fields * adv_fields,void * uid,int8_t measured_power)109 ble_eddystone_set_adv_data_uid(struct ble_hs_adv_fields *adv_fields,
110 void *uid, int8_t measured_power)
111 {
112 uint8_t *svc_data;
113 int rc;
114
115 /* Eddystone UUID and frame type (0). */
116 svc_data = ble_eddystone_set_svc_data_base(BLE_EDDYSTONE_FRAME_TYPE_UID);
117
118 /* Measured Power ranging data (Calibrated tx power at 0 meters). */
119 if (measured_power < -100 || measured_power > 20) {
120 return BLE_HS_EINVAL;
121 }
122 svc_data[0] = measured_power;
123
124 /* UID. */
125 memcpy(svc_data + 1, uid, 16);
126
127 /* Reserved. */
128 svc_data[17] = 0x00;
129 svc_data[18] = 0x00;
130
131 rc = ble_eddystone_set_adv_data_gen(adv_fields, 19);
132 if (rc != 0) {
133 return rc;
134 }
135
136 return 0;
137 }
138
139 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)140 ble_eddystone_set_adv_data_url(struct ble_hs_adv_fields *adv_fields,
141 uint8_t url_scheme, char *url_body,
142 uint8_t url_body_len, uint8_t url_suffix,
143 int8_t measured_power)
144 {
145 uint8_t *svc_data;
146 int url_len;
147 int rc;
148
149 url_len = url_body_len;
150 if (url_suffix != BLE_EDDYSTONE_URL_SUFFIX_NONE) {
151 url_len++;
152 }
153 if (url_len > BLE_EDDYSTONE_URL_MAX_LEN) {
154 return BLE_HS_EINVAL;
155 }
156
157 svc_data = ble_eddystone_set_svc_data_base(BLE_EDDYSTONE_FRAME_TYPE_URL);
158
159 /* Measured Power ranging data (Calibrated tx power at 0 meters). */
160 if (measured_power < -100 || measured_power > 20) {
161 return BLE_HS_EINVAL;
162 }
163 svc_data[0] = measured_power;
164
165 svc_data[1] = url_scheme;
166 memcpy(svc_data + 2, url_body, url_body_len);
167 if (url_suffix != BLE_EDDYSTONE_URL_SUFFIX_NONE) {
168 svc_data[2 + url_body_len] = url_suffix;
169 }
170
171 rc = ble_eddystone_set_adv_data_gen(adv_fields, url_len + 2);
172 if (rc != 0) {
173 return rc;
174 }
175
176 return 0;
177 }
178