1 /*
2 * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include "string.h"
8 #include "esp_gatt_defs.h"
9 #include "esp_gatts_api.h"
10 #include "esp_bt_main.h"
11 #include "btc/btc_manage.h"
12 #include "btc_gatts.h"
13 #include "btc_gatt_util.h"
14 #include "common/bt_target.h"
15 #include "stack/l2cdefs.h"
16 #include "stack/l2c_api.h"
17 #include "gatt_int.h"
18
19 #if (GATTS_INCLUDED == TRUE)
20 #define COPY_TO_GATTS_ARGS(_gatt_args, _arg, _arg_type) memcpy(_gatt_args, _arg, sizeof(_arg_type))
21
22 static esp_err_t esp_ble_gatts_add_char_desc_param_check(esp_attr_value_t *char_val, esp_attr_control_t *control);
23
24
esp_ble_gatts_register_callback(esp_gatts_cb_t callback)25 esp_err_t esp_ble_gatts_register_callback(esp_gatts_cb_t callback)
26 {
27 ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED);
28
29 return (btc_profile_cb_set(BTC_PID_GATTS, callback) == 0 ? ESP_OK : ESP_FAIL);
30 }
31
esp_ble_gatts_get_callback(void)32 esp_gatts_cb_t esp_ble_gatts_get_callback(void)
33 {
34 return (esp_gatts_cb_t) btc_profile_cb_get(BTC_PID_GATTS);
35 }
36
esp_ble_gatts_app_register(uint16_t app_id)37 esp_err_t esp_ble_gatts_app_register(uint16_t app_id)
38 {
39 btc_msg_t msg = {0};
40 btc_ble_gatts_args_t arg;
41
42 ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED);
43
44 //if (app_id < ESP_APP_ID_MIN || app_id > ESP_APP_ID_MAX) {
45 if (app_id > ESP_APP_ID_MAX) {
46 return ESP_ERR_INVALID_ARG;
47 }
48
49 msg.sig = BTC_SIG_API_CALL;
50 msg.pid = BTC_PID_GATTS;
51 msg.act = BTC_GATTS_ACT_APP_REGISTER;
52 arg.app_reg.app_id = app_id;
53
54 return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gatts_args_t), NULL, NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL);
55 }
56
57
esp_ble_gatts_app_unregister(esp_gatt_if_t gatts_if)58 esp_err_t esp_ble_gatts_app_unregister(esp_gatt_if_t gatts_if)
59 {
60 btc_msg_t msg = {0};
61 btc_ble_gatts_args_t arg;
62
63 ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED);
64
65 msg.sig = BTC_SIG_API_CALL;
66 msg.pid = BTC_PID_GATTS;
67 msg.act = BTC_GATTS_ACT_APP_UNREGISTER;
68 arg.app_unreg.gatts_if = gatts_if;
69
70 return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gatts_args_t), NULL, NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL);
71 }
72
esp_ble_gatts_create_service(esp_gatt_if_t gatts_if,esp_gatt_srvc_id_t * service_id,uint16_t num_handle)73 esp_err_t esp_ble_gatts_create_service(esp_gatt_if_t gatts_if,
74 esp_gatt_srvc_id_t *service_id, uint16_t num_handle)
75 {
76 btc_msg_t msg = {0};
77 btc_ble_gatts_args_t arg;
78
79 ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED);
80
81 msg.sig = BTC_SIG_API_CALL;
82 msg.pid = BTC_PID_GATTS;
83 msg.act = BTC_GATTS_ACT_CREATE_SERVICE;
84 arg.create_srvc.gatts_if = gatts_if;
85 arg.create_srvc.num_handle = num_handle;
86 memcpy(&arg.create_srvc.service_id, service_id, sizeof(esp_gatt_srvc_id_t));
87
88 return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gatts_args_t), NULL, NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL);
89 }
90
esp_ble_gatts_create_attr_tab(const esp_gatts_attr_db_t * gatts_attr_db,esp_gatt_if_t gatts_if,uint16_t max_nb_attr,uint8_t srvc_inst_id)91 esp_err_t esp_ble_gatts_create_attr_tab(const esp_gatts_attr_db_t *gatts_attr_db,
92 esp_gatt_if_t gatts_if,
93 uint16_t max_nb_attr,
94 uint8_t srvc_inst_id)
95 {
96 btc_msg_t msg = {0};
97 btc_ble_gatts_args_t arg;
98
99 ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED);
100
101 if (max_nb_attr > ESP_GATT_ATTR_HANDLE_MAX) {
102 LOG_ERROR("The number of attribute should not be greater than CONFIG_BT_GATT_MAX_SR_ATTRIBUTES\n");
103 return ESP_ERR_INVALID_ARG;
104 }
105
106 msg.sig = BTC_SIG_API_CALL;
107 msg.pid = BTC_PID_GATTS;
108 msg.act = BTC_GATTS_ACT_CREATE_ATTR_TAB;
109 arg.create_attr_tab.gatts_if = gatts_if;
110 arg.create_attr_tab.max_nb_attr = max_nb_attr;
111 arg.create_attr_tab.srvc_inst_id = srvc_inst_id;
112 arg.create_attr_tab.gatts_attr_db = (esp_gatts_attr_db_t *)gatts_attr_db;
113
114 return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gatts_args_t), btc_gatts_arg_deep_copy,
115 btc_gatts_arg_deep_free) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL);
116 }
117
118
esp_ble_gatts_add_included_service(uint16_t service_handle,uint16_t included_service_handle)119 esp_err_t esp_ble_gatts_add_included_service(uint16_t service_handle, uint16_t included_service_handle)
120 {
121 btc_msg_t msg = {0};
122 btc_ble_gatts_args_t arg;
123
124 ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED);
125
126 msg.sig = BTC_SIG_API_CALL;
127 msg.pid = BTC_PID_GATTS;
128 msg.act = BTC_GATTS_ACT_ADD_INCLUDE_SERVICE;
129 arg.add_incl_srvc.service_handle = service_handle;
130 arg.add_incl_srvc.included_service_handle = included_service_handle;
131
132 return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gatts_args_t), NULL, NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL);
133 }
134
135
esp_ble_gatts_add_char(uint16_t service_handle,esp_bt_uuid_t * char_uuid,esp_gatt_perm_t perm,esp_gatt_char_prop_t property,esp_attr_value_t * char_val,esp_attr_control_t * control)136 esp_err_t esp_ble_gatts_add_char(uint16_t service_handle, esp_bt_uuid_t *char_uuid,
137 esp_gatt_perm_t perm, esp_gatt_char_prop_t property, esp_attr_value_t *char_val,
138 esp_attr_control_t *control)
139 {
140 btc_msg_t msg = {0};
141 btc_ble_gatts_args_t arg;
142 esp_err_t status;
143
144 ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED);
145
146 /* parameter validation check */
147 status = esp_ble_gatts_add_char_desc_param_check(char_val, control);
148 if (status != ESP_OK){
149 return status;
150 }
151
152 memset(&arg, 0, sizeof(btc_ble_gatts_args_t));
153 msg.sig = BTC_SIG_API_CALL;
154 msg.pid = BTC_PID_GATTS;
155 msg.act = BTC_GATTS_ACT_ADD_CHAR;
156 arg.add_char.service_handle = service_handle;
157 arg.add_char.perm = perm;
158 arg.add_char.property = property;
159 if (char_val != NULL) {
160 arg.add_char.char_val.attr_max_len = char_val->attr_max_len;
161 arg.add_char.char_val.attr_len = char_val->attr_len;
162 arg.add_char.char_val.attr_value = char_val->attr_value;
163 }
164
165 if (control != NULL) {
166 arg.add_char.attr_control.auto_rsp = control->auto_rsp;
167 }
168 memcpy(&arg.add_char.char_uuid, char_uuid, sizeof(esp_bt_uuid_t));
169
170 return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gatts_args_t), btc_gatts_arg_deep_copy,
171 btc_gatts_arg_deep_free) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL);
172 }
173
174
esp_ble_gatts_add_char_descr(uint16_t service_handle,esp_bt_uuid_t * descr_uuid,esp_gatt_perm_t perm,esp_attr_value_t * char_descr_val,esp_attr_control_t * control)175 esp_err_t esp_ble_gatts_add_char_descr (uint16_t service_handle,
176 esp_bt_uuid_t *descr_uuid,
177 esp_gatt_perm_t perm, esp_attr_value_t *char_descr_val,
178 esp_attr_control_t *control)
179 {
180 btc_msg_t msg = {0};
181 btc_ble_gatts_args_t arg;
182 esp_err_t status;
183
184 ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED);
185
186 /* parameter validation check */
187 status = esp_ble_gatts_add_char_desc_param_check(char_descr_val, control);
188 if (status != ESP_OK){
189 return status;
190 }
191
192 memset(&arg, 0, sizeof(btc_ble_gatts_args_t));
193 msg.sig = BTC_SIG_API_CALL;
194 msg.pid = BTC_PID_GATTS;
195 msg.act = BTC_GATTS_ACT_ADD_CHAR_DESCR;
196 arg.add_descr.service_handle = service_handle;
197 arg.add_descr.perm = perm;
198
199 if (char_descr_val != NULL) {
200 arg.add_descr.descr_val.attr_max_len = char_descr_val->attr_max_len;
201 arg.add_descr.descr_val.attr_len = char_descr_val->attr_len;
202 arg.add_descr.descr_val.attr_value = char_descr_val->attr_value;
203 }
204
205 if (control != NULL) {
206 arg.add_descr.attr_control.auto_rsp = control->auto_rsp;
207 }
208 memcpy(&arg.add_descr.descr_uuid, descr_uuid, sizeof(esp_bt_uuid_t));
209
210 return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gatts_args_t), btc_gatts_arg_deep_copy,
211 btc_gatts_arg_deep_free) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL);
212 }
213
esp_ble_gatts_delete_service(uint16_t service_handle)214 esp_err_t esp_ble_gatts_delete_service(uint16_t service_handle)
215 {
216 btc_msg_t msg = {0};
217 btc_ble_gatts_args_t arg;
218
219 ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED);
220
221 msg.sig = BTC_SIG_API_CALL;
222 msg.pid = BTC_PID_GATTS;
223 msg.act = BTC_GATTS_ACT_DELETE_SERVICE;
224 arg.delete_srvc.service_handle = service_handle;
225
226 return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gatts_args_t), NULL, NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL);
227 }
228
esp_ble_gatts_start_service(uint16_t service_handle)229 esp_err_t esp_ble_gatts_start_service(uint16_t service_handle)
230 {
231 btc_msg_t msg = {0};
232 btc_ble_gatts_args_t arg;
233
234 ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED);
235
236 msg.sig = BTC_SIG_API_CALL;
237 msg.pid = BTC_PID_GATTS;
238 msg.act = BTC_GATTS_ACT_START_SERVICE;
239 arg.start_srvc.service_handle = service_handle;
240
241 return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gatts_args_t), NULL, NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL);
242 }
243
esp_ble_gatts_stop_service(uint16_t service_handle)244 esp_err_t esp_ble_gatts_stop_service(uint16_t service_handle)
245 {
246 btc_msg_t msg = {0};
247 btc_ble_gatts_args_t arg;
248
249 ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED);
250
251 msg.sig = BTC_SIG_API_CALL;
252 msg.pid = BTC_PID_GATTS;
253 msg.act = BTC_GATTS_ACT_STOP_SERVICE;
254 arg.stop_srvc.service_handle = service_handle;
255
256 return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gatts_args_t), NULL, NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL);
257 }
258
259
esp_ble_gatts_send_indicate(esp_gatt_if_t gatts_if,uint16_t conn_id,uint16_t attr_handle,uint16_t value_len,uint8_t * value,bool need_confirm)260 esp_err_t esp_ble_gatts_send_indicate(esp_gatt_if_t gatts_if, uint16_t conn_id, uint16_t attr_handle,
261 uint16_t value_len, uint8_t *value, bool need_confirm)
262 {
263 btc_msg_t msg = {0};
264 btc_ble_gatts_args_t arg;
265
266 ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED);
267
268 tGATT_TCB *p_tcb = gatt_get_tcb_by_idx(conn_id);
269 if (!gatt_check_connection_state_by_tcb(p_tcb)) {
270 LOG_WARN("%s, The connection not created.", __func__);
271 return ESP_ERR_INVALID_STATE;
272 }
273
274 if (L2CA_CheckIsCongest(L2CAP_ATT_CID, p_tcb->peer_bda)) {
275 LOG_DEBUG("%s, the l2cap chanel is congest.", __func__);
276 return ESP_FAIL;
277 }
278
279 msg.sig = BTC_SIG_API_CALL;
280 msg.pid = BTC_PID_GATTS;
281 msg.act = BTC_GATTS_ACT_SEND_INDICATE;
282 arg.send_ind.conn_id = BTC_GATT_CREATE_CONN_ID(gatts_if, conn_id);
283 arg.send_ind.attr_handle = attr_handle;
284 arg.send_ind.need_confirm = need_confirm;
285 arg.send_ind.value_len = value_len;
286 arg.send_ind.value = value;
287 if(need_confirm == false){
288 l2ble_update_att_acl_pkt_num(L2CA_ADD_BTC_NUM, NULL);
289 }
290 return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gatts_args_t), btc_gatts_arg_deep_copy,
291 btc_gatts_arg_deep_free) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL);
292 }
293
esp_ble_gatts_send_response(esp_gatt_if_t gatts_if,uint16_t conn_id,uint32_t trans_id,esp_gatt_status_t status,esp_gatt_rsp_t * rsp)294 esp_err_t esp_ble_gatts_send_response(esp_gatt_if_t gatts_if, uint16_t conn_id, uint32_t trans_id,
295 esp_gatt_status_t status, esp_gatt_rsp_t *rsp)
296 {
297 btc_msg_t msg = {0};
298 btc_ble_gatts_args_t arg;
299
300 ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED);
301
302 tGATT_TCB *p_tcb = gatt_get_tcb_by_idx(conn_id);
303 if (!gatt_check_connection_state_by_tcb(p_tcb)) {
304 LOG_WARN("%s, The connection not created.", __func__);
305 return ESP_ERR_INVALID_STATE;
306 }
307
308 msg.sig = BTC_SIG_API_CALL;
309 msg.pid = BTC_PID_GATTS;
310 msg.act = BTC_GATTS_ACT_SEND_RESPONSE;
311 arg.send_rsp.conn_id = BTC_GATT_CREATE_CONN_ID(gatts_if, conn_id);
312 arg.send_rsp.trans_id = trans_id;
313 arg.send_rsp.status = status;
314 arg.send_rsp.rsp = rsp;
315
316 return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gatts_args_t), btc_gatts_arg_deep_copy,
317 btc_gatts_arg_deep_free) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL);
318 }
319
esp_ble_gatts_set_attr_value(uint16_t attr_handle,uint16_t length,const uint8_t * value)320 esp_err_t esp_ble_gatts_set_attr_value(uint16_t attr_handle, uint16_t length, const uint8_t *value)
321 {
322 btc_msg_t msg = {0};
323 btc_ble_gatts_args_t arg;
324
325 ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED);
326
327 msg.sig = BTC_SIG_API_CALL;
328 msg.pid = BTC_PID_GATTS;
329 msg.act = BTC_GATTS_ACT_SET_ATTR_VALUE;
330 arg.set_attr_val.handle = attr_handle;
331 arg.set_attr_val.length = length;
332 arg.set_attr_val.value = (uint8_t *)value;
333
334 return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gatts_args_t), btc_gatts_arg_deep_copy,
335 btc_gatts_arg_deep_free) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL);
336 }
337
esp_ble_gatts_get_attr_value(uint16_t attr_handle,uint16_t * length,const uint8_t ** value)338 esp_gatt_status_t esp_ble_gatts_get_attr_value(uint16_t attr_handle, uint16_t *length, const uint8_t **value)
339 {
340 ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED);
341
342 if (attr_handle == ESP_GATT_ILLEGAL_HANDLE) {
343 *length = 0;
344 return ESP_GATT_INVALID_HANDLE;
345 }
346
347 return btc_gatts_get_attr_value(attr_handle, length, (uint8_t **)value);
348 }
349
esp_ble_gatts_open(esp_gatt_if_t gatts_if,esp_bd_addr_t remote_bda,bool is_direct)350 esp_err_t esp_ble_gatts_open(esp_gatt_if_t gatts_if, esp_bd_addr_t remote_bda, bool is_direct)
351 {
352 btc_msg_t msg = {0};
353 btc_ble_gatts_args_t arg;
354
355 ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED);
356
357 msg.sig = BTC_SIG_API_CALL;
358 msg.pid = BTC_PID_GATTS;
359 msg.act = BTC_GATTS_ACT_OPEN;
360 arg.open.gatts_if = gatts_if;
361 arg.open.is_direct = is_direct;
362 memcpy(&arg.open.remote_bda, remote_bda, sizeof(esp_bd_addr_t));
363
364 return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gatts_args_t), NULL, NULL)
365 == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL);
366 }
367
esp_ble_gatts_close(esp_gatt_if_t gatts_if,uint16_t conn_id)368 esp_err_t esp_ble_gatts_close(esp_gatt_if_t gatts_if, uint16_t conn_id)
369 {
370 btc_msg_t msg = {0};
371 btc_ble_gatts_args_t arg;
372
373 ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED);
374
375 msg.sig = BTC_SIG_API_CALL;
376 msg.pid = BTC_PID_GATTS;
377 msg.act = BTC_GATTS_ACT_CLOSE;
378 arg.close.conn_id = BTC_GATT_CREATE_CONN_ID(gatts_if, conn_id);
379
380 return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gatts_args_t), NULL, NULL)
381 == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL);
382 }
383
esp_ble_gatts_send_service_change_indication(esp_gatt_if_t gatts_if,esp_bd_addr_t remote_bda)384 esp_err_t esp_ble_gatts_send_service_change_indication(esp_gatt_if_t gatts_if, esp_bd_addr_t remote_bda)
385 {
386 btc_msg_t msg = {0};
387 btc_ble_gatts_args_t arg;
388
389 ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED);
390
391 msg.sig = BTC_SIG_API_CALL;
392 msg.pid = BTC_PID_GATTS;
393 msg.act = BTC_GATTS_ACT_SEND_SERVICE_CHANGE;
394 arg.send_service_change.gatts_if = gatts_if;
395 if(remote_bda) {
396 memcpy(&arg.send_service_change.remote_bda, remote_bda, sizeof(esp_bd_addr_t));
397 } else {
398 memset(arg.send_service_change.remote_bda, 0, sizeof(esp_bd_addr_t));
399 }
400
401
402 return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gatts_args_t), NULL, NULL)
403 == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL);
404 }
405
esp_ble_gatts_add_char_desc_param_check(esp_attr_value_t * char_val,esp_attr_control_t * control)406 static esp_err_t esp_ble_gatts_add_char_desc_param_check(esp_attr_value_t *char_val, esp_attr_control_t *control)
407 {
408 if ((control != NULL) && ((control->auto_rsp != ESP_GATT_AUTO_RSP) && (control->auto_rsp != ESP_GATT_RSP_BY_APP))){
409 LOG_ERROR("Error in %s, line=%d, control->auto_rsp should be set to ESP_GATT_AUTO_RSP or ESP_GATT_RSP_BY_APP\n",\
410 __func__, __LINE__);
411 return ESP_ERR_INVALID_ARG;
412 }
413
414 if ((control != NULL) && (control->auto_rsp == ESP_GATT_AUTO_RSP)){
415 if (char_val == NULL){
416 LOG_ERROR("Error in %s, line=%d, for stack respond attribute, char_val should not be NULL here\n",\
417 __func__, __LINE__);
418 return ESP_ERR_INVALID_ARG;
419 } else if (char_val->attr_max_len == 0){
420 LOG_ERROR("Error in %s, line=%d, for stack respond attribute, attribute max length should not be 0\n",\
421 __func__, __LINE__);
422 return ESP_ERR_INVALID_ARG;
423 }
424 }
425
426 return ESP_OK;
427 }
428
esp_ble_gatts_show_local_database(void)429 esp_err_t esp_ble_gatts_show_local_database(void)
430 {
431 btc_msg_t msg = {0};
432
433 ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED);
434
435 msg.sig = BTC_SIG_API_CALL;
436 msg.pid = BTC_PID_GATTS;
437 msg.act = BTC_GATTS_ACT_SHOW_LOCAL_DATABASE;
438
439 return (btc_transfer_context(&msg, NULL, 0, NULL, NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL);
440 }
441
442 #endif ///GATTS_INCLUDED
443