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 <assert.h>
21 #include <stdio.h>
22 #include <string.h>
23 #include <rtthread.h>
24 #include "host/ble_hs.h"
25 #include "host/ble_uuid.h"
26 #include "blecsc_sens.h"
27
28 #define CSC_ERR_CCC_DESC_IMPROPERLY_CONFIGURED 0x81
29
30 static const char *manuf_name = "Apache Mynewt";
31 static const char *model_num = "Mynewt CSC Sensor";
32
33 static const uint8_t csc_supported_sensor_locations[] = {
34 SENSOR_LOCATION_FRONT_WHEEL,
35 SENSOR_LOCATION_REAR_DROPOUT,
36 SENSOR_LOCATION_CHAINSTAY,
37 SENSOR_LOCATION_REAR_WHEEL
38 };
39
40 static uint8_t sensor_location = SENSOR_LOCATION_REAR_DROPOUT;
41 static struct ble_csc_measurement_state * measurement_state;
42 uint16_t csc_measurement_handle;
43 uint16_t csc_control_point_handle;
44 uint8_t csc_cp_indication_status;
45
46 static int
47 gatt_svr_chr_access_csc_measurement(uint16_t conn_handle,
48 uint16_t attr_handle,
49 struct ble_gatt_access_ctxt *ctxt,
50 void *arg);
51
52 static int
53 gatt_svr_chr_access_csc_feature(uint16_t conn_handle,
54 uint16_t attr_handle,
55 struct ble_gatt_access_ctxt *ctxt,
56 void *arg);
57
58 static int
59 gatt_svr_chr_access_sensor_location(uint16_t conn_handle,
60 uint16_t attr_handle,
61 struct ble_gatt_access_ctxt *ctxt,
62 void *arg);
63
64 static int
65 gatt_svr_chr_access_sc_control_point(uint16_t conn_handle,
66 uint16_t attr_handle,
67 struct ble_gatt_access_ctxt *ctxt,
68 void *arg);
69
70 static int
71 gatt_svr_chr_access_device_info(uint16_t conn_handle,
72 uint16_t attr_handle,
73 struct ble_gatt_access_ctxt *ctxt,
74 void *arg);
75
76 static const struct ble_gatt_svc_def gatt_svr_svcs[] = {
77 {
78 /* Service: Cycling Speed and Cadence */
79 .type = BLE_GATT_SVC_TYPE_PRIMARY,
80 .uuid = BLE_UUID16_DECLARE(GATT_CSC_UUID),
81 .characteristics = (struct ble_gatt_chr_def[]) { {
82 /* Characteristic: Cycling Speed and Cadence Measurement */
83 .uuid = BLE_UUID16_DECLARE(GATT_CSC_MEASUREMENT_UUID),
84 .access_cb = gatt_svr_chr_access_csc_measurement,
85 .val_handle = &csc_measurement_handle,
86 .flags = BLE_GATT_CHR_F_NOTIFY,
87 }, {
88 /* Characteristic: Cycling Speed and Cadence features */
89 .uuid = BLE_UUID16_DECLARE(GATT_CSC_FEATURE_UUID),
90 .access_cb = gatt_svr_chr_access_csc_feature,
91 .flags = BLE_GATT_CHR_F_READ,
92 }, {
93 /* Characteristic: Sensor Location */
94 .uuid = BLE_UUID16_DECLARE(GATT_SENSOR_LOCATION_UUID),
95 .access_cb = gatt_svr_chr_access_sensor_location,
96 .flags = BLE_GATT_CHR_F_READ,
97 }, {
98 /* Characteristic: SC Control Point*/
99 .uuid = BLE_UUID16_DECLARE(GATT_SC_CONTROL_POINT_UUID),
100 .access_cb = gatt_svr_chr_access_sc_control_point,
101 .val_handle = &csc_control_point_handle,
102 .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_INDICATE,
103 }, {
104 0, /* No more characteristics in this service */
105 }, }
106 },
107
108 {
109 /* Service: Device Information */
110 .type = BLE_GATT_SVC_TYPE_PRIMARY,
111 .uuid = BLE_UUID16_DECLARE(GATT_DEVICE_INFO_UUID),
112 .characteristics = (struct ble_gatt_chr_def[]) { {
113 /* Characteristic: * Manufacturer name */
114 .uuid = BLE_UUID16_DECLARE(GATT_MANUFACTURER_NAME_UUID),
115 .access_cb = gatt_svr_chr_access_device_info,
116 .flags = BLE_GATT_CHR_F_READ,
117 }, {
118 /* Characteristic: Model number string */
119 .uuid = BLE_UUID16_DECLARE(GATT_MODEL_NUMBER_UUID),
120 .access_cb = gatt_svr_chr_access_device_info,
121 .flags = BLE_GATT_CHR_F_READ,
122 }, {
123 0, /* No more characteristics in this service */
124 }, }
125 },
126
127 {
128 0, /* No more services */
129 },
130 };
131
132 static int
gatt_svr_chr_access_csc_measurement(uint16_t conn_handle,uint16_t attr_handle,struct ble_gatt_access_ctxt * ctxt,void * arg)133 gatt_svr_chr_access_csc_measurement(uint16_t conn_handle, uint16_t attr_handle,
134 struct ble_gatt_access_ctxt *ctxt, void *arg)
135 {
136 return BLE_ATT_ERR_READ_NOT_PERMITTED;
137 }
138
139 static int
gatt_svr_chr_access_csc_feature(uint16_t conn_handle,uint16_t attr_handle,struct ble_gatt_access_ctxt * ctxt,void * arg)140 gatt_svr_chr_access_csc_feature(uint16_t conn_handle, uint16_t attr_handle,
141 struct ble_gatt_access_ctxt *ctxt, void *arg)
142 {
143 static const uint16_t csc_feature = CSC_FEATURES;
144 int rc;
145
146 assert(ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR);
147 rc = os_mbuf_append(ctxt->om, &csc_feature, sizeof(csc_feature));
148
149 return (rc == 0) ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
150 }
151
152 static int
gatt_svr_chr_access_sensor_location(uint16_t conn_handle,uint16_t attr_handle,struct ble_gatt_access_ctxt * ctxt,void * arg)153 gatt_svr_chr_access_sensor_location(uint16_t conn_handle, uint16_t attr_handle,
154 struct ble_gatt_access_ctxt *ctxt, void *arg)
155 {
156 int rc;
157
158 assert(ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR);
159 rc = os_mbuf_append(ctxt->om, &sensor_location, sizeof(sensor_location));
160
161 return (rc == 0) ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
162 }
163
164 static int
gatt_svr_chr_access_sc_control_point(uint16_t conn_handle,uint16_t attr_handle,struct ble_gatt_access_ctxt * ctxt,void * arg)165 gatt_svr_chr_access_sc_control_point(uint16_t conn_handle,
166 uint16_t attr_handle,
167 struct ble_gatt_access_ctxt *ctxt,
168 void *arg)
169 {
170 uint8_t op_code;
171 uint8_t new_sensor_location;
172 uint8_t new_cumulative_wheel_rev_arr[4];
173 struct os_mbuf *om_indication;
174 uint8_t response = SC_CP_RESPONSE_OP_NOT_SUPPORTED;
175 int ii;
176 int rc;
177
178 assert(ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR);
179
180 if (!csc_cp_indication_status) {
181 MODLOG_DFLT(INFO, "SC Control Point; CCC descriptor "
182 "improperly configured");
183 return CSC_ERR_CCC_DESC_IMPROPERLY_CONFIGURED;
184 }
185
186 /* Read control point op code*/
187 rc = os_mbuf_copydata(ctxt->om, 0, sizeof(op_code), &op_code);
188 if (rc != 0){
189 return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
190 }
191 MODLOG_DFLT(INFO, "SC Control Point; opcode=%d\n", op_code);
192
193 /* Allocate response buffer */
194 om_indication = ble_hs_mbuf_att_pkt();
195
196 switch(op_code){
197 #if (CSC_FEATURES & CSC_FEATURE_WHEEL_REV_DATA)
198 case SC_CP_OP_SET_CUMULATIVE_VALUE:
199 /* Read new cumulative wheel revolutions value*/
200 rc = os_mbuf_copydata(ctxt->om, 1,
201 sizeof(new_cumulative_wheel_rev_arr),
202 new_cumulative_wheel_rev_arr);
203 if (rc != 0){
204 return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
205 }
206
207 measurement_state->cumulative_wheel_rev =
208 get_le32(new_cumulative_wheel_rev_arr);
209
210 MODLOG_DFLT(INFO, "SC Control Point; Set cumulative value = %d\n",
211 measurement_state->cumulative_wheel_rev);
212
213 response = SC_CP_RESPONSE_SUCCESS;
214 break;
215 #endif
216
217 #if (CSC_FEATURES & CSC_FEATURE_MULTIPLE_SENSOR_LOC)
218 case SC_CP_OP_UPDATE_SENSOR_LOCATION:
219 /* Read new sensor location value*/
220 rc = os_mbuf_copydata(ctxt->om, 1, 1, &new_sensor_location);
221 if (rc != 0){
222 return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
223 }
224
225 MODLOG_DFLT(INFO, "SC Control Point; Sensor location update = %d\n",
226 new_sensor_location);
227
228 /* Verify if requested new location is on supported locations list */
229 response = SC_CP_RESPONSE_INVALID_PARAM;
230 for (ii = 0; ii < sizeof(csc_supported_sensor_locations); ii++){
231 if (new_sensor_location == csc_supported_sensor_locations[ii]){
232 sensor_location = new_sensor_location;
233 response = SC_CP_RESPONSE_SUCCESS;
234 break;
235 }
236 }
237 break;
238
239 case SC_CP_OP_REQ_SUPPORTED_SENSOR_LOCATIONS:
240 response = SC_CP_RESPONSE_SUCCESS;
241 break;
242 #endif
243
244 default:
245 break;
246 }
247
248 /* Append response value */
249 rc = os_mbuf_append(om_indication, &response, sizeof(response));
250
251 if (rc != 0){
252 return BLE_ATT_ERR_INSUFFICIENT_RES;
253 }
254
255 #if (CSC_FEATURES & CSC_FEATURE_MULTIPLE_SENSOR_LOC)
256 /* In case of supported locations request append locations list */
257 if (op_code == SC_CP_OP_REQ_SUPPORTED_SENSOR_LOCATIONS){
258 rc = os_mbuf_append(om_indication, &csc_supported_sensor_locations,
259 sizeof(csc_supported_sensor_locations));
260 }
261
262 if (rc != 0){
263 return BLE_ATT_ERR_INSUFFICIENT_RES;
264 }
265 #endif
266
267 rc = ble_gattc_indicate_custom(conn_handle, csc_control_point_handle,
268 om_indication);
269
270 return rc;
271 }
272
273 static int
gatt_svr_chr_access_device_info(uint16_t conn_handle,uint16_t attr_handle,struct ble_gatt_access_ctxt * ctxt,void * arg)274 gatt_svr_chr_access_device_info(uint16_t conn_handle, uint16_t attr_handle,
275 struct ble_gatt_access_ctxt *ctxt, void *arg)
276 {
277 uint16_t uuid;
278 int rc;
279
280 uuid = ble_uuid_u16(ctxt->chr->uuid);
281
282 if (uuid == GATT_MODEL_NUMBER_UUID) {
283 rc = os_mbuf_append(ctxt->om, model_num, strlen(model_num));
284 return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
285 }
286
287 if (uuid == GATT_MANUFACTURER_NAME_UUID) {
288 rc = os_mbuf_append(ctxt->om, manuf_name, strlen(manuf_name));
289 return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
290 }
291
292 assert(0);
293 return BLE_ATT_ERR_UNLIKELY;
294 }
295
296 int
gatt_svr_chr_notify_csc_measurement(uint16_t conn_handle)297 gatt_svr_chr_notify_csc_measurement(uint16_t conn_handle)
298 {
299 int rc;
300 struct os_mbuf *om;
301 uint8_t data_buf[11];
302 uint8_t data_offset = 1;
303
304 memset(data_buf, 0, sizeof(data_buf));
305
306 #if (CSC_FEATURES & CSC_FEATURE_WHEEL_REV_DATA)
307 data_buf[0] |= CSC_MEASUREMENT_WHEEL_REV_PRESENT;
308 put_le16(&(data_buf[5]), measurement_state->last_wheel_evt_time);
309 put_le32(&(data_buf[1]), measurement_state->cumulative_wheel_rev);
310 data_offset += 6;
311 #endif
312
313 #if (CSC_FEATURES & CSC_FEATURE_CRANK_REV_DATA)
314 data_buf[0] |= CSC_MEASUREMENT_CRANK_REV_PRESENT;
315 put_le16(&(data_buf[data_offset]),
316 measurement_state->cumulative_crank_rev);
317 put_le16(&(data_buf[data_offset + 2]),
318 measurement_state->last_crank_evt_time);
319 data_offset += 4;
320 #endif
321
322 om = ble_hs_mbuf_from_flat(data_buf, data_offset);
323
324 rc = ble_gattc_notify_custom(conn_handle, csc_measurement_handle, om);
325 return rc;
326 }
327
328 void
gatt_svr_set_cp_indicate(uint8_t indication_status)329 gatt_svr_set_cp_indicate(uint8_t indication_status)
330 {
331 csc_cp_indication_status = indication_status;
332 }
333
334 void
gatt_svr_register_cb(struct ble_gatt_register_ctxt * ctxt,void * arg)335 gatt_svr_register_cb(struct ble_gatt_register_ctxt *ctxt, void *arg)
336 {
337 char buf[BLE_UUID_STR_LEN];
338
339 switch (ctxt->op) {
340 case BLE_GATT_REGISTER_OP_SVC:
341 MODLOG_DFLT(DEBUG, "registered service %s with handle=%d\n",
342 ble_uuid_to_str(ctxt->svc.svc_def->uuid, buf),
343 ctxt->svc.handle);
344 break;
345
346 case BLE_GATT_REGISTER_OP_CHR:
347 MODLOG_DFLT(DEBUG, "registering characteristic %s with "
348 "def_handle=%d val_handle=%d\n",
349 ble_uuid_to_str(ctxt->chr.chr_def->uuid, buf),
350 ctxt->chr.def_handle,
351 ctxt->chr.val_handle);
352 break;
353
354 case BLE_GATT_REGISTER_OP_DSC:
355 MODLOG_DFLT(DEBUG, "registering descriptor %s with handle=%d\n",
356 ble_uuid_to_str(ctxt->dsc.dsc_def->uuid, buf),
357 ctxt->dsc.handle);
358 break;
359
360 default:
361 assert(0);
362 break;
363 }
364 }
365
366 int
gatt_svr_init(struct ble_csc_measurement_state * csc_measurement_state)367 gatt_svr_init(struct ble_csc_measurement_state * csc_measurement_state)
368 {
369 int rc;
370
371 rc = ble_gatts_count_cfg(gatt_svr_svcs);
372 if (rc != 0) {
373 return rc;
374 }
375
376 rc = ble_gatts_add_svcs(gatt_svr_svcs);
377 if (rc != 0) {
378 return rc;
379 }
380
381 measurement_state = csc_measurement_state;
382
383 return 0;
384 }
385