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 <string.h>
22 #include "sysinit/sysinit.h"
23 #include "host/ble_hs.h"
24 #include "services/lls/ble_svc_lls.h"
25
26 /* Callback function */
27 static ble_svc_lls_event_fn *ble_svc_lls_cb_fn;
28
29 /* Alert level */
30 static uint8_t ble_svc_lls_alert_level;
31
32 /* Write characteristic function */
33 static int
34 ble_svc_lls_chr_write(struct os_mbuf *om, uint16_t min_len,
35 uint16_t max_len, void *dst,
36 uint16_t *len);
37
38 /* Access function */
39 static int
40 ble_svc_lls_access(uint16_t conn_handle, uint16_t attr_handle,
41 struct ble_gatt_access_ctxt *ctxt, void *arg);
42
43 static const struct ble_gatt_svc_def ble_svc_lls_defs[] = {
44 {
45 /*** Service: Link Loss Service (LLS). */
46 .type = BLE_GATT_SVC_TYPE_PRIMARY,
47 .uuid = BLE_UUID16_DECLARE(BLE_SVC_LLS_UUID16),
48 .characteristics = (struct ble_gatt_chr_def[]) { {
49 /*** Characteristic: Alert Level. */
50 .uuid = BLE_UUID16_DECLARE(BLE_SVC_LLS_CHR_UUID16_ALERT_LEVEL),
51 .access_cb = ble_svc_lls_access,
52 .flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_WRITE,
53 }, {
54 0, /* No more characteristics in this service. */
55 } },
56 },
57
58 {
59 0, /* No more services. */
60 },
61 };
62
63 /**
64 * Writes the received value from a characteristic write to
65 * the given destination.
66 */
67 static int
ble_svc_lls_chr_write(struct os_mbuf * om,uint16_t min_len,uint16_t max_len,void * dst,uint16_t * len)68 ble_svc_lls_chr_write(struct os_mbuf *om, uint16_t min_len,
69 uint16_t max_len, void *dst,
70 uint16_t *len)
71 {
72 uint16_t om_len;
73 int rc;
74
75 om_len = OS_MBUF_PKTLEN(om);
76 if (om_len < min_len || om_len > max_len) {
77 return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
78 }
79
80 rc = ble_hs_mbuf_to_flat(om, dst, max_len, len);
81 if (rc != 0) {
82 return BLE_ATT_ERR_UNLIKELY;
83 }
84
85 return 0;
86 }
87
88 /**
89 * Simple read/write access callback for the alert level
90 * characteristic.
91 */
92 static int
ble_svc_lls_access(uint16_t conn_handle,uint16_t attr_handle,struct ble_gatt_access_ctxt * ctxt,void * arg)93 ble_svc_lls_access(uint16_t conn_handle, uint16_t attr_handle,
94 struct ble_gatt_access_ctxt *ctxt, void *arg)
95 {
96 assert(ctxt->chr == &ble_svc_lls_defs[0].characteristics[0]);
97 int rc;
98 switch (ctxt->op) {
99 case BLE_GATT_ACCESS_OP_READ_CHR:
100 rc = os_mbuf_append(ctxt->om, &ble_svc_lls_alert_level,
101 sizeof ble_svc_lls_alert_level);
102 return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
103
104 case BLE_GATT_ACCESS_OP_WRITE_CHR:
105 rc = ble_svc_lls_chr_write(ctxt->om,
106 sizeof ble_svc_lls_alert_level,
107 sizeof ble_svc_lls_alert_level,
108 &ble_svc_lls_alert_level, NULL);
109 return rc;
110
111 default:
112 assert(0);
113 return BLE_ATT_ERR_UNLIKELY;
114 }
115
116 return 0;
117 }
118
119 /**
120 * This function is the crux of the link loss service. The application
121 * developer must call this function inside the gap event callback
122 * function when a BLE_GAP_EVENT_DISCONNECT event is received and
123 * pass the disconnect reason into this function.
124 *
125 * Here, we then check if the disconnect reason is due to a timout, and if
126 * so, we call the ble_svc_lls_event_fn callback with the current
127 * alert level. The actual alert implementation is left up to the
128 * developer.
129 *
130 * @param reason The reason attatched to the GAP disconnect
131 * event.
132 */
133 void
ble_svc_lls_on_gap_disconnect(int reason)134 ble_svc_lls_on_gap_disconnect(int reason)
135 {
136 if (reason == BLE_HS_HCI_ERR(BLE_ERR_CONN_SPVN_TMO)) {
137 ble_svc_lls_cb_fn(ble_svc_lls_alert_level);
138 }
139 }
140
141 /**
142 * Gets the current alert level.
143 *
144 * @return The current alert level
145 */
146 uint8_t
ble_svc_lls_alert_level_get(void)147 ble_svc_lls_alert_level_get(void)
148 {
149 return ble_svc_lls_alert_level;
150 }
151
152 /**
153 * Sets the current alert level.
154 *
155 * @return 0 on success, BLE_HS_EINVAL if the given alert level is not valid.
156 */
157 int
ble_svc_lls_alert_level_set(uint8_t alert_level)158 ble_svc_lls_alert_level_set(uint8_t alert_level)
159 {
160 if (alert_level > BLE_SVC_LLS_ALERT_LEVEL_HIGH_ALERT) {
161 return BLE_HS_EINVAL;
162 }
163
164 memcpy(&ble_svc_lls_alert_level, &alert_level,
165 sizeof alert_level);
166
167 return 0;
168 }
169
170 void
ble_svc_lls_set_cb(ble_svc_lls_event_fn * cb)171 ble_svc_lls_set_cb(ble_svc_lls_event_fn *cb)
172 {
173 ble_svc_lls_cb_fn = cb;
174 }
175
176 /**
177 * Initialize the IAS package.
178 */
179 void
ble_svc_lls_init(void)180 ble_svc_lls_init(void)
181 {
182 int rc;
183
184 /* Ensure this function only gets called by sysinit. */
185 SYSINIT_ASSERT_ACTIVE();
186
187 rc = ble_gatts_count_cfg(ble_svc_lls_defs);
188 SYSINIT_PANIC_ASSERT(rc == 0);
189
190 rc = ble_gatts_add_svcs(ble_svc_lls_defs);
191 SYSINIT_PANIC_ASSERT(rc == 0);
192 }
193