1 /**
2 ****************************************************************************************
3 *
4 * @file pass.c
5 *
6 * @brief Phone Alert Status Service implementation.
7 *
8 ****************************************************************************************
9 * @attention
10 #####Copyright (c) 2019 GOODIX
11 All rights reserved.
12
13 Redistribution and use in source and binary forms, with or without
14 modification, are permitted provided that the following conditions are met:
15 * Redistributions of source code must retain the above copyright
16 notice, this list of conditions and the following disclaimer.
17 * Redistributions in binary form must reproduce the above copyright
18 notice, this list of conditions and the following disclaimer in the
19 documentation and/or other materials provided with the distribution.
20 * Neither the name of GOODIX nor the names of its contributors may be used
21 to endorse or promote products derived from this software without
22 specific prior written permission.
23
24 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
25 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDERS AND CONTRIBUTORS BE
28 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
29 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
30 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
31 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
32 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
33 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34 POSSIBILITY OF SUCH DAMAGE.
35 *****************************************************************************************
36 */
37
38 /*
39 * INCLUDE FILES
40 ****************************************************************************************
41 */
42 #include "pass.h"
43 #include "ble_prf_types.h"
44 #include "ble_prf_utils.h"
45 #include "utility.h"
46
47 /*
48 * ENUMERATIONS
49 ****************************************************************************************
50 */
51 /**@brief Phone Alert Status Service Attributes Indexes. */
52 enum {
53 // Phone Alert Status Service
54 PASS_IDX_SVC,
55
56 // Alert Status
57 PASS_IDX_ALERT_STATUS_CHAR,
58 PASS_IDX_ALERT_STATUS_VAL,
59 PASS_IDX_ALERT_STATUS_NTF_CFG,
60
61 // Ringer Setting
62 PASS_IDX_RINGER_SET_CHAR,
63 PASS_IDX_RINGER_SET_VAL,
64 PASS_IDX_RINGER_SET_NTF_CFG,
65
66 // Ringer Control Point
67 PASS_IDX_RINGER_CTRL_PT_CHAR,
68 PASS_IDX_RINGER_CTRL_PT_VAL,
69
70 PASS_IDX_NB
71 };
72
73 /*
74 * STRUCTURES
75 *****************************************************************************************
76 */
77 /**@brief Phone Alert Status Service environment variable. */
78 struct pass_env_t {
79 pass_init_t pass_init; /**< Phone Alert Status Service initialization variables. */
80 uint16_t start_hdl; /**< Phone Alert Status Service start handle. */
81 uint16_t
82 alert_status_ntf_cfg[PASS_CONNECTION_MAX]; /**< The configuration of Alert Status Notification
83 which is configured by the peer devices. */
84 uint16_t
85 ringer_setting_ntf_cfg[PASS_CONNECTION_MAX]; /**< The configuration of Ringer Setting Notification
86 which is configured by the peer devices. */
87 };
88
89 /*
90 * LOCAL FUNCTION DECLARATION
91 *****************************************************************************************
92 */
93 static sdk_err_t pass_init(void);
94 static void pass_read_att_cb(uint8_t conn_idx, const gatts_read_req_cb_t *p_param);
95 static void pass_write_att_cb(uint8_t conn_idx, const gatts_write_req_cb_t *p_param);
96 static void pass_cccd_set_cb(uint8_t conn_idx, uint16_t handle, uint16_t cccd_value);
97
98
99 /*
100 * LOCAL VARIABLE DEFINITIONS
101 *****************************************************************************************
102 */
103 static struct pass_env_t s_pass_env;
104
105 /**@brief Full PASS Database Description - Used to add attributes into the database. */
106 static const attm_desc_t pass_attr_tab[PASS_IDX_NB] = {
107 // Phone Alert Status Service Declaration
108 [PASS_IDX_SVC] = {BLE_ATT_DECL_PRIMARY_SERVICE, READ_PERM_UNSEC, 0, 0},
109
110 // Alert Status Characteristic - Declaration
111 [PASS_IDX_ALERT_STATUS_CHAR] = {BLE_ATT_DECL_CHARACTERISTIC, READ_PERM_UNSEC, 0, 0},
112 // Alert Status Characteristic - Value
113 [PASS_IDX_ALERT_STATUS_VAL] = {
114 BLE_ATT_CHAR_ALERT_STATUS,
115 NOTIFY_PERM_UNSEC | READ_PERM_UNSEC,
116 ATT_VAL_LOC_USER,
117 PASS_ALERT_STATUS_VAL_LEN
118 },
119 // Alert Status Characteristic - Client Characteristic Configuration Descriptor
120 [PASS_IDX_ALERT_STATUS_NTF_CFG] = {BLE_ATT_DESC_CLIENT_CHAR_CFG, READ_PERM_UNSEC | WRITE_REQ_PERM_UNSEC, 0, 0},
121
122 // Ringer Setting Characteristic - Declaration
123 [PASS_IDX_RINGER_SET_CHAR] = {BLE_ATT_DECL_CHARACTERISTIC, READ_PERM_UNSEC, 0, 0},
124 // Ringer Setting Characteristic - Value
125 [PASS_IDX_RINGER_SET_VAL] = {
126 BLE_ATT_CHAR_RINGER_SETTING,
127 NOTIFY_PERM_UNSEC | READ_PERM_UNSEC,
128 ATT_VAL_LOC_USER,
129 PASS_RINGER_SET_VAL_LEN
130 },
131 // Ringer Setting Characteristic - Client Characteristic Configuration Descriptor
132 [PASS_IDX_RINGER_SET_NTF_CFG] = {BLE_ATT_DESC_CLIENT_CHAR_CFG, READ_PERM_UNSEC | WRITE_REQ_PERM_UNSEC, 0, 0},
133
134 // Ringer Setting Characteristic - Declaration
135 [PASS_IDX_RINGER_CTRL_PT_CHAR] = {BLE_ATT_DECL_CHARACTERISTIC, READ_PERM_UNSEC, 0, 0},
136 // Ringer Setting Characteristic - Value
137 [PASS_IDX_RINGER_CTRL_PT_VAL] = {
138 BLE_ATT_CHAR_RINGER_CNTL_POINT,
139 WRITE_CMD_PERM_UNSEC,
140 ATT_VAL_LOC_USER,
141 PASS_RINGER_CTRL_PT_VAL_LEN
142 },
143 };
144
145 /**@brief PASS Task interface required by profile manager. */
146 static ble_prf_manager_cbs_t pass_task_cbs = {
147 (prf_init_func_t) pass_init,
148 NULL,
149 NULL,
150 };
151
152 /**@brief PASS Task Callbacks. */
153 static gatts_prf_cbs_t pass_cb_func = {
154 pass_read_att_cb,
155 pass_write_att_cb,
156 NULL,
157 NULL,
158 pass_cccd_set_cb
159 };
160
161 /**@brief PASS Information. */
162 static const prf_server_info_t pass_prf_info = {
163 .max_connection_nb = PASS_CONNECTION_MAX,
164 .manager_cbs = &pass_task_cbs,
165 .gatts_prf_cbs = &pass_cb_func,
166 };
167
168 /*
169 * LOCAL FUNCTION DEFINITIONS
170 *****************************************************************************************
171 */
172 /**
173 *****************************************************************************************
174 * @brief Initialize Phone Alert Status Service and create db in att
175 *
176 * @return Error code to know if profile initialization succeed or not.
177 *****************************************************************************************
178 */
pass_init(void)179 static sdk_err_t pass_init(void)
180 {
181 // The start hanlde must be set with PRF_INVALID_HANDLE to be allocated automatically by BLE Stack.
182 uint16_t start_hdl = PRF_INVALID_HANDLE;
183 const uint8_t pass_svc_uuid[] = BLE_ATT_16_TO_16_ARRAY(BLE_ATT_SVC_PHONE_ALERT_STATUS);
184 sdk_err_t error_code;
185 gatts_create_db_t gatts_db;
186
187 error_code = memset_s(&gatts_db, sizeof(gatts_db), 0, sizeof(gatts_db));
188 if (error_code < 0) {
189 return error_code;
190 }
191
192 gatts_db.shdl = &start_hdl;
193 gatts_db.uuid = pass_svc_uuid;
194 gatts_db.attr_tab_cfg = (uint8_t *) & (s_pass_env.pass_init.char_mask);
195 gatts_db.max_nb_attr = PASS_IDX_NB;
196 gatts_db.srvc_perm = 0;
197 gatts_db.attr_tab_type = SERVICE_TABLE_TYPE_16;
198 gatts_db.attr_tab.attr_tab_16 = pass_attr_tab;
199
200 error_code = ble_gatts_srvc_db_create(&gatts_db);
201 if (SDK_SUCCESS == error_code) {
202 s_pass_env.start_hdl = *gatts_db.shdl;
203 }
204
205 return error_code;
206 }
207
208 /**
209 *****************************************************************************************
210 * @brief Handles reception of the attribute info request message.
211 *
212 * @param[in] conn_idx: Connection index
213 * @param[in] p_param: The parameters of the read request.
214 *****************************************************************************************
215 */
pass_read_att_cb(uint8_t conn_idx,const gatts_read_req_cb_t * p_param)216 static void pass_read_att_cb(uint8_t conn_idx, const gatts_read_req_cb_t *p_param)
217 {
218 gatts_read_cfm_t cfm;
219 uint8_t handle = p_param->handle;
220 uint8_t tab_index = prf_find_idx_by_handle(handle,
221 s_pass_env.start_hdl,
222 PASS_IDX_NB,
223 (uint8_t *)&s_pass_env.pass_init.char_mask);
224 cfm.handle = handle;
225 cfm.status = BLE_SUCCESS;
226
227 switch (tab_index) {
228 case PASS_IDX_ALERT_STATUS_VAL:
229 cfm.length = PASS_ALERT_STATUS_VAL_LEN;
230 cfm.value = &s_pass_env.pass_init.alert_status;
231 break;
232
233 case PASS_IDX_ALERT_STATUS_NTF_CFG:
234 cfm.length = sizeof(uint16_t);
235 cfm.value = (uint8_t *)&s_pass_env.alert_status_ntf_cfg[conn_idx];
236 break;
237
238 case PASS_IDX_RINGER_SET_VAL:
239 cfm.length = PASS_RINGER_SET_VAL_LEN;
240 cfm.value = &s_pass_env.pass_init.ringer_setting;
241 break;
242
243 case PASS_IDX_RINGER_SET_NTF_CFG:
244 cfm.length = sizeof(uint16_t);
245 cfm.value = (uint8_t *)&s_pass_env.ringer_setting_ntf_cfg[conn_idx];
246 break;
247
248 default:
249 cfm.length = 0;
250 cfm.status = BLE_ATT_ERR_INVALID_HANDLE;
251 break;
252 }
253
254 ble_gatts_read_cfm(conn_idx, &cfm);
255 }
256
257 /**
258 *****************************************************************************************
259 * @brief Handles reception of the write request.
260 *
261 * @param[in]: conn_idx: Connection index
262 * @param[in]: p_param: The parameters of the write request.
263 *****************************************************************************************
264 */
pass_write_att_cb(uint8_t conn_idx,const gatts_write_req_cb_t * p_param)265 static void pass_write_att_cb(uint8_t conn_idx, const gatts_write_req_cb_t *p_param)
266 {
267 uint16_t handle = p_param->handle;
268 uint16_t tab_index = 0;
269 uint16_t cccd_value = 0;
270 pass_evt_t event;
271 gatts_write_cfm_t cfm;
272
273 tab_index = prf_find_idx_by_handle(handle,
274 s_pass_env.start_hdl,
275 PASS_IDX_NB,
276 (uint8_t *)&s_pass_env.pass_init.char_mask);
277 cfm.handle = handle;
278 cfm.status = BLE_SUCCESS;
279 event.evt_type = PASS_EVT_INVALID;
280 event.conn_idx = conn_idx;
281
282 switch (tab_index) {
283 case PASS_IDX_ALERT_STATUS_NTF_CFG:
284 cccd_value = le16toh(&p_param->value[0]);
285 event.evt_type = ((PRF_CLI_START_NTF == cccd_value) ? \
286 PASS_EVT_ALERT_STATUS_NTF_ENABLE : \
287 PASS_EVT_ALERT_STATUS_NTF_DISABLE);
288 s_pass_env.alert_status_ntf_cfg[conn_idx] = cccd_value;
289 break;
290
291 case PASS_IDX_RINGER_SET_NTF_CFG:
292 cccd_value = le16toh(&p_param->value[0]);
293 event.evt_type = ((PRF_CLI_START_NTF == cccd_value) ? \
294 PASS_EVT_RINGER_SET_NTF_ENABLE : \
295 PASS_EVT_RINGER_SET_NTF_DISABLE);
296 s_pass_env.ringer_setting_ntf_cfg[conn_idx] = cccd_value;
297 break;
298
299 case PASS_IDX_RINGER_CTRL_PT_VAL: {
300 switch (p_param->value[0]) {
301 case PASS_CTRL_PT_SILENT_MODE:
302 if ((PASS_RINGER_SET_NORMAL == s_pass_env.pass_init.ringer_setting) && \
303 (PASS_RINGER_ACTIVE & s_pass_env.pass_init.alert_status)) {
304 event.evt_type = PASS_EVT_SILENT_MODE_SET;
305 }
306 break;
307
308 case PASS_CTRL_PT_MUTE_ONCE:
309 event.evt_type = PASS_EVT_MUTE_ONCE_SET;
310 break;
311
312 case PASS_CTRL_PT_CANCEL_SLIENT_MODE:
313 if ((PASS_RINGER_SET_SILENT == s_pass_env.pass_init.ringer_setting) && \
314 (PASS_RINGER_ACTIVE & s_pass_env.pass_init.alert_status)) {
315 event.evt_type = PASS_EVT_SILENT_MODE_CANCEL;
316 }
317 break;
318
319 default:
320 break;
321 }
322 }
323 break;
324
325 default:
326 cfm.status = BLE_ATT_ERR_INVALID_HANDLE;
327 break;
328 }
329
330 ble_gatts_write_cfm(conn_idx, &cfm);
331
332 if (BLE_ATT_ERR_INVALID_HANDLE != cfm.status && PASS_EVT_INVALID != event.evt_type
333 && s_pass_env.pass_init.evt_handler) {
334 s_pass_env.pass_init.evt_handler(&event);
335 }
336 }
337
338 /**
339 *****************************************************************************************
340 * @brief Handles reception of the cccd recover request.
341 *
342 * @param[in]: conn_idx: Connection index
343 * @param[in]: handle: The handle of cccd attribute.
344 * @param[in]: cccd_value: The value of cccd attribute.
345 *****************************************************************************************
346 */
pass_cccd_set_cb(uint8_t conn_idx,uint16_t handle,uint16_t cccd_value)347 static void pass_cccd_set_cb(uint8_t conn_idx, uint16_t handle, uint16_t cccd_value)
348 {
349 uint16_t tab_index = 0;
350 pass_evt_t event;
351
352 if (!prf_is_cccd_value_valid(cccd_value)) {
353 return;
354 }
355
356 tab_index = prf_find_idx_by_handle(handle,
357 s_pass_env.start_hdl,
358 PASS_IDX_NB,
359 (uint8_t *)&s_pass_env.pass_init.char_mask);
360
361 event.evt_type = PASS_EVT_INVALID;
362 event.conn_idx = conn_idx;
363
364 switch (tab_index) {
365 case PASS_IDX_ALERT_STATUS_NTF_CFG:
366 event.evt_type = ((PRF_CLI_START_NTF == cccd_value) ? \
367 PASS_EVT_ALERT_STATUS_NTF_ENABLE : \
368 PASS_EVT_ALERT_STATUS_NTF_DISABLE);
369 s_pass_env.alert_status_ntf_cfg[conn_idx] = cccd_value;
370 break;
371
372 case PASS_IDX_RINGER_SET_NTF_CFG:
373 event.evt_type = ((PRF_CLI_START_NTF == cccd_value) ? \
374 PASS_EVT_RINGER_SET_NTF_ENABLE : \
375 PASS_EVT_RINGER_SET_NTF_DISABLE);
376 s_pass_env.ringer_setting_ntf_cfg[conn_idx] = cccd_value;
377 break;
378
379 default:
380 break;
381 }
382
383 if (PASS_EVT_INVALID != event.evt_type && s_pass_env.pass_init.evt_handler) {
384 s_pass_env.pass_init.evt_handler(&event);
385 }
386 }
387
388 /**
389 *****************************************************************************************
390 * @brief Notify Alert Status.
391
392 * @param[in] conn_idx: Connnection index.
393 *****************************************************************************************
394 */
pass_alert_status_send(uint8_t conn_idx)395 static sdk_err_t pass_alert_status_send(uint8_t conn_idx)
396 {
397 sdk_err_t error_code = SDK_ERR_NTF_DISABLED;
398 gatts_noti_ind_t alert_status_ntf;
399
400 if (PRF_CLI_START_NTF == s_pass_env.alert_status_ntf_cfg[conn_idx]) {
401 alert_status_ntf.type = BLE_GATT_NOTIFICATION;
402 alert_status_ntf.handle = prf_find_handle_by_idx(PASS_IDX_ALERT_STATUS_VAL,
403 s_pass_env.start_hdl,
404 (uint8_t *)&s_pass_env.pass_init.char_mask);
405 alert_status_ntf.length = PASS_ALERT_STATUS_VAL_LEN;
406 alert_status_ntf.value = &s_pass_env.pass_init.alert_status;
407 error_code = ble_gatts_noti_ind(conn_idx, &alert_status_ntf);
408 }
409
410 return error_code;
411 }
412
413 /**
414 *****************************************************************************************
415 * @brief Notify Ringer Setting.
416 *
417 * @param[in] conn_idx: Connnection index.
418 *****************************************************************************************
419 */
pass_ringer_set_send(uint8_t conn_idx)420 sdk_err_t pass_ringer_set_send(uint8_t conn_idx)
421 {
422 sdk_err_t error_code = SDK_ERR_NTF_DISABLED;
423 gatts_noti_ind_t ringer_set_ntf;
424
425 if (PRF_CLI_START_NTF == s_pass_env.ringer_setting_ntf_cfg[conn_idx]) {
426 ringer_set_ntf.type = BLE_GATT_NOTIFICATION;
427 ringer_set_ntf.handle = prf_find_handle_by_idx(PASS_IDX_RINGER_SET_VAL,
428 s_pass_env.start_hdl,
429 (uint8_t *)&s_pass_env.pass_init.char_mask);
430 ringer_set_ntf.length = PASS_ALERT_STATUS_VAL_LEN;
431 ringer_set_ntf.value = &s_pass_env.pass_init.ringer_setting;
432 error_code = ble_gatts_noti_ind(conn_idx, &ringer_set_ntf);
433 }
434
435 return error_code;
436 }
437
438 /*
439 * GLOBAL FUNCTION DEFINITIONS
440 *****************************************************************************************
441 */
pass_ringer_setting_get(void)442 uint8_t pass_ringer_setting_get(void)
443 {
444 return s_pass_env.pass_init.ringer_setting;
445 }
446
pass_alert_status_set(uint8_t conn_idx,uint8_t new_status)447 void pass_alert_status_set(uint8_t conn_idx, uint8_t new_status)
448 {
449 if (new_status != s_pass_env.pass_init.alert_status) {
450 s_pass_env.pass_init.alert_status = new_status;
451 pass_alert_status_send(conn_idx);
452 }
453 }
454
pass_ringer_setting_set(uint8_t conn_idx,uint8_t new_setting)455 void pass_ringer_setting_set(uint8_t conn_idx, uint8_t new_setting)
456 {
457 if (new_setting != s_pass_env.pass_init.ringer_setting) {
458 s_pass_env.pass_init.ringer_setting = new_setting;
459 pass_ringer_set_send(conn_idx);
460 }
461 }
462
pass_service_init(pass_init_t * p_pass_init)463 sdk_err_t pass_service_init(pass_init_t *p_pass_init)
464 {
465 sdk_err_t ret;
466 if (p_pass_init == NULL) {
467 return SDK_ERR_POINTER_NULL;
468 }
469
470 ret = memcpy_s(&s_pass_env.pass_init, sizeof(pass_init_t), p_pass_init, sizeof(pass_init_t));
471 if (ret < 0) {
472 return ret;
473 }
474
475 return ble_server_prf_add(&pass_prf_info);
476 }
477
478