1 /******************************************************************************
2 *
3 * Copyright 1999-2013 Broadcom Corporation
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at:
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 *
17 ******************************************************************************/
18
19 #include <bluetooth/log.h>
20
21 #include "gatt_api.h"
22 #include "osi/include/allocator.h"
23 #include "osi/include/osi.h"
24 #include "srvc_dis_int.h"
25 #include "srvc_eng_int.h"
26 #include "stack/include/bt_uuid16.h"
27 #include "types/bluetooth/uuid.h"
28 #include "types/raw_address.h"
29
30 using namespace bluetooth;
31
32 static void srvc_eng_connect_cback(tGATT_IF /* gatt_if */, const RawAddress& bda, tCONN_ID conn_id,
33 bool connected, tGATT_DISCONN_REASON reason,
34 tBT_TRANSPORT transport);
35 static void srvc_eng_c_cmpl_cback(tCONN_ID conn_id, tGATTC_OPTYPE op, tGATT_STATUS status,
36 tGATT_CL_COMPLETE* p_data);
37
38 static tGATT_CBACK srvc_gatt_cback = {
39 .p_conn_cb = srvc_eng_connect_cback,
40 .p_cmpl_cb = srvc_eng_c_cmpl_cback,
41 .p_disc_res_cb = nullptr,
42 .p_disc_cmpl_cb = nullptr,
43 .p_req_cb = nullptr,
44 .p_enc_cmpl_cb = nullptr,
45 .p_congestion_cb = nullptr,
46 .p_phy_update_cb = nullptr,
47 .p_conn_update_cb = nullptr,
48 .p_subrate_chg_cb = nullptr,
49 };
50
51 /* type for action functions */
52 typedef void (*tSRVC_ENG_C_CMPL_ACTION)(tSRVC_CLCB* p_clcb, tGATTC_OPTYPE op, tGATT_STATUS status,
53 tGATT_CL_COMPLETE* p_data);
54
55 static const tSRVC_ENG_C_CMPL_ACTION srvc_eng_c_cmpl_act[SRVC_ID_MAX] = {
56 dis_c_cmpl_cback,
57 };
58
59 tSRVC_ENG_CB srvc_eng_cb;
60
61 /*******************************************************************************
62 *
63 * Function srvc_eng_find_clcb_by_bd_addr
64 *
65 * Description The function searches all LCBs with macthing bd address.
66 *
67 * Returns Pointer to the found link conenction control block.
68 *
69 ******************************************************************************/
srvc_eng_find_clcb_by_bd_addr(const RawAddress & bda)70 static tSRVC_CLCB* srvc_eng_find_clcb_by_bd_addr(const RawAddress& bda) {
71 uint8_t i_clcb;
72 tSRVC_CLCB* p_clcb = NULL;
73
74 for (i_clcb = 0, p_clcb = srvc_eng_cb.clcb; i_clcb < SRVC_MAX_APPS; i_clcb++, p_clcb++) {
75 if (p_clcb->in_use && p_clcb->connected && p_clcb->bda == bda) {
76 return p_clcb;
77 }
78 }
79
80 return NULL;
81 }
82 /*******************************************************************************
83 *
84 * Function srvc_eng_find_clcb_by_conn_id
85 *
86 * Description The function searches all LCBs with macthing connection ID.
87 *
88 * Returns Pointer to the found link conenction control block.
89 *
90 ******************************************************************************/
srvc_eng_find_clcb_by_conn_id(tCONN_ID conn_id)91 tSRVC_CLCB* srvc_eng_find_clcb_by_conn_id(tCONN_ID conn_id) {
92 uint8_t i_clcb;
93 tSRVC_CLCB* p_clcb = NULL;
94
95 for (i_clcb = 0, p_clcb = srvc_eng_cb.clcb; i_clcb < SRVC_MAX_APPS; i_clcb++, p_clcb++) {
96 if (p_clcb->in_use && p_clcb->connected && p_clcb->conn_id == conn_id) {
97 return p_clcb;
98 }
99 }
100
101 return NULL;
102 }
103 /*******************************************************************************
104 *
105 * Function srvc_eng_clcb_alloc
106 *
107 * Description Allocate a GATT profile connection link control block
108 *
109 * Returns NULL if not found. Otherwise pointer to the connection link
110 * block.
111 *
112 ******************************************************************************/
srvc_eng_clcb_alloc(tCONN_ID conn_id,const RawAddress & bda)113 static tSRVC_CLCB* srvc_eng_clcb_alloc(tCONN_ID conn_id, const RawAddress& bda) {
114 uint8_t i_clcb = 0;
115 tSRVC_CLCB* p_clcb = NULL;
116
117 for (i_clcb = 0, p_clcb = srvc_eng_cb.clcb; i_clcb < SRVC_MAX_APPS; i_clcb++, p_clcb++) {
118 if (!p_clcb->in_use) {
119 p_clcb->in_use = true;
120 p_clcb->conn_id = conn_id;
121 p_clcb->connected = true;
122 p_clcb->bda = bda;
123 break;
124 }
125 }
126 return p_clcb;
127 }
128 /*******************************************************************************
129 *
130 * Function srvc_eng_clcb_dealloc
131 *
132 * Description De-allocate a GATT profile connection link control block
133 *
134 * Returns True the deallocation is successful
135 *
136 ******************************************************************************/
srvc_eng_clcb_dealloc(tCONN_ID conn_id)137 static bool srvc_eng_clcb_dealloc(tCONN_ID conn_id) {
138 uint8_t i_clcb = 0;
139 tSRVC_CLCB* p_clcb = NULL;
140
141 for (i_clcb = 0, p_clcb = srvc_eng_cb.clcb; i_clcb < SRVC_MAX_APPS; i_clcb++, p_clcb++) {
142 if (p_clcb->in_use && p_clcb->connected && (p_clcb->conn_id == conn_id)) {
143 unsigned j;
144 for (j = 0; j < ARRAY_SIZE(p_clcb->dis_value.data_string); j++) {
145 osi_free(p_clcb->dis_value.data_string[j]);
146 }
147
148 memset(p_clcb, 0, sizeof(tSRVC_CLCB));
149 return true;
150 }
151 }
152 return false;
153 }
154
155 /*******************************************************************************
156 *
157 * Function srvc_eng_c_cmpl_cback
158 *
159 * Description Client operation complete callback.
160 *
161 * Returns void
162 *
163 ******************************************************************************/
srvc_eng_c_cmpl_cback(tCONN_ID conn_id,tGATTC_OPTYPE op,tGATT_STATUS status,tGATT_CL_COMPLETE * p_data)164 static void srvc_eng_c_cmpl_cback(tCONN_ID conn_id, tGATTC_OPTYPE op, tGATT_STATUS status,
165 tGATT_CL_COMPLETE* p_data) {
166 tSRVC_CLCB* p_clcb = srvc_eng_find_clcb_by_conn_id(conn_id);
167
168 log::verbose("srvc_eng_c_cmpl_cback() - op_code: 0x{:02x} status: 0x{:02x}", op, status);
169
170 if (p_clcb == NULL) {
171 log::error("received for unknown connection");
172 return;
173 }
174
175 if (p_clcb->cur_srvc_id != SRVC_ID_NONE && p_clcb->cur_srvc_id <= SRVC_ID_MAX) {
176 srvc_eng_c_cmpl_act[p_clcb->cur_srvc_id - 1](p_clcb, op, status, p_data);
177 }
178 }
179
180 /*******************************************************************************
181 *
182 * Function srvc_eng_connect_cback
183 *
184 * Description Gatt profile connection callback.
185 *
186 * Returns void
187 *
188 ******************************************************************************/
srvc_eng_connect_cback(tGATT_IF,const RawAddress & bda,tCONN_ID conn_id,bool connected,tGATT_DISCONN_REASON,tBT_TRANSPORT)189 static void srvc_eng_connect_cback(tGATT_IF /* gatt_if */, const RawAddress& bda, tCONN_ID conn_id,
190 bool connected, tGATT_DISCONN_REASON /* reason */,
191 tBT_TRANSPORT /* transport */) {
192 log::verbose("from {} connected:{} conn_id={}", bda, connected, conn_id);
193
194 if (connected) {
195 if (srvc_eng_clcb_alloc(conn_id, bda) == NULL) {
196 log::error("srvc_eng_connect_cback: no_resource");
197 return;
198 }
199 } else {
200 srvc_eng_clcb_dealloc(conn_id);
201 }
202 }
203 /*******************************************************************************
204 *
205 * Function srvc_eng_c_cmpl_cback
206 *
207 * Description Client operation complete callback.
208 *
209 * Returns void
210 *
211 ******************************************************************************/
srvc_eng_request_channel(const RawAddress & remote_bda,uint8_t srvc_id)212 bool srvc_eng_request_channel(const RawAddress& remote_bda, uint8_t srvc_id) {
213 bool set = true;
214 tSRVC_CLCB* p_clcb = srvc_eng_find_clcb_by_bd_addr(remote_bda);
215
216 if (p_clcb == NULL) {
217 p_clcb = srvc_eng_clcb_alloc(0, remote_bda);
218 }
219
220 if (p_clcb && p_clcb->cur_srvc_id == SRVC_ID_NONE) {
221 p_clcb->cur_srvc_id = srvc_id;
222 } else {
223 set = false;
224 }
225
226 return set;
227 }
228 /*******************************************************************************
229 *
230 * Function srvc_eng_release_channel
231 *
232 * Description Client operation complete callback.
233 *
234 * Returns void
235 *
236 ******************************************************************************/
srvc_eng_release_channel(tCONN_ID conn_id)237 void srvc_eng_release_channel(tCONN_ID conn_id) {
238 tSRVC_CLCB* p_clcb = srvc_eng_find_clcb_by_conn_id(conn_id);
239
240 if (p_clcb == NULL) {
241 log::error("invalid connection id {}", conn_id);
242 return;
243 }
244
245 p_clcb->cur_srvc_id = SRVC_ID_NONE;
246
247 /* check pending request */
248 if (GATT_Disconnect(p_clcb->conn_id) != GATT_SUCCESS) {
249 log::warn("Unable to disconnect GATT conn_id:{}", p_clcb->conn_id);
250 }
251 }
252 /*******************************************************************************
253 *
254 * Function srvc_eng_init
255 *
256 * Description Initialize the GATT Service engine.
257 *
258 ******************************************************************************/
srvc_eng_init(void)259 tGATT_STATUS srvc_eng_init(void) {
260 if (srvc_eng_cb.enabled) {
261 log::verbose("DIS already initialized");
262 } else {
263 memset(&srvc_eng_cb, 0, sizeof(tSRVC_ENG_CB));
264
265 /* Create a GATT profile service */
266 bluetooth::Uuid app_uuid = bluetooth::Uuid::From16Bit(UUID_SERVCLASS_DEVICE_INFO);
267 srvc_eng_cb.gatt_if = GATT_Register(app_uuid, "GattServiceEngine", &srvc_gatt_cback, false);
268 GATT_StartIf(srvc_eng_cb.gatt_if);
269
270 log::verbose("Srvc_Init: gatt_if={}", srvc_eng_cb.gatt_if);
271
272 srvc_eng_cb.enabled = true;
273 dis_cb.dis_read_uuid_idx = 0xff;
274 }
275 return GATT_SUCCESS;
276 }
277