xref: /nrf52832-nimble/packages/NimBLE-latest/nimble/host/src/ble_eddystone.c (revision 042d53a763ad75cb1465103098bb88c245d95138)
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