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 <inttypes.h>
21 #include <errno.h>
22
23 #include "host/ble_hs_mbuf.h"
24 #include "host/ble_gap.h"
25 #include "services/gatt/ble_svc_gatt.h"
26 #include "btshell.h"
27 #include "cmd.h"
28 #include "cmd_gatt.h"
29
30 #include "nimble/npl_shell.h"
31
32 #define CMD_BUF_SZ 256
33 static bssnz_t uint8_t cmd_buf[CMD_BUF_SZ];
34
35 /*****************************************************************************
36 * $gatt-discover *
37 *****************************************************************************/
38
39 int
cmd_gatt_discover_characteristic(int argc,char ** argv)40 cmd_gatt_discover_characteristic(int argc, char **argv)
41 {
42 uint16_t start_handle;
43 uint16_t conn_handle;
44 uint16_t end_handle;
45 ble_uuid_any_t uuid;
46 int rc;
47
48 rc = parse_arg_all(argc - 1, argv + 1);
49 if (rc != 0) {
50 return rc;
51 }
52
53 rc = cmd_parse_conn_start_end(&conn_handle, &start_handle, &end_handle);
54 if (rc != 0) {
55 console_printf("invalid 'conn start end' parameter\n");
56 return rc;
57 }
58
59 rc = parse_arg_uuid("uuid", &uuid);
60 if (rc == 0) {
61 rc = btshell_disc_chrs_by_uuid(conn_handle, start_handle, end_handle,
62 &uuid.u);
63 } else if (rc == ENOENT) {
64 rc = btshell_disc_all_chrs(conn_handle, start_handle, end_handle);
65 } else {
66 console_printf("invalid 'uuid' parameter\n");
67 return rc;
68 }
69 if (rc != 0) {
70 console_printf("error discovering characteristics; rc=%d\n", rc);
71 return rc;
72 }
73
74 return 0;
75 }
76
77 int
cmd_gatt_discover_descriptor(int argc,char ** argv)78 cmd_gatt_discover_descriptor(int argc, char **argv)
79 {
80 uint16_t start_handle;
81 uint16_t conn_handle;
82 uint16_t end_handle;
83 int rc;
84
85 rc = parse_arg_all(argc - 1, argv + 1);
86 if (rc != 0) {
87 return rc;
88 }
89
90 rc = cmd_parse_conn_start_end(&conn_handle, &start_handle, &end_handle);
91 if (rc != 0) {
92 console_printf("invalid 'conn start end' parameter\n");
93 return rc;
94 }
95
96 rc = btshell_disc_all_dscs(conn_handle, start_handle, end_handle);
97 if (rc != 0) {
98 console_printf("error discovering descriptors; rc=%d\n", rc);
99 return rc;
100 }
101
102 return 0;
103 }
104
105 int
cmd_gatt_discover_service(int argc,char ** argv)106 cmd_gatt_discover_service(int argc, char **argv)
107 {
108 ble_uuid_any_t uuid;
109 int conn_handle;
110 int rc;
111
112 rc = parse_arg_all(argc - 1, argv + 1);
113 if (rc != 0) {
114 return rc;
115 }
116
117 conn_handle = parse_arg_uint16("conn", &rc);
118 if (rc != 0) {
119 console_printf("invalid 'conn' parameter\n");
120 return rc;
121 }
122
123 rc = parse_arg_uuid("uuid", &uuid);
124 if (rc == 0) {
125 rc = btshell_disc_svc_by_uuid(conn_handle, &uuid.u);
126 } else if (rc == ENOENT) {
127 rc = btshell_disc_svcs(conn_handle);
128 } else {
129 console_printf("invalid 'uuid' parameter\n");
130 return rc;
131 }
132
133 if (rc != 0) {
134 console_printf("error discovering services; rc=%d\n", rc);
135 return rc;
136 }
137
138 return 0;
139 }
140
141 int
cmd_gatt_discover_full(int argc,char ** argv)142 cmd_gatt_discover_full(int argc, char **argv)
143 {
144 int conn_handle;
145 int rc;
146
147 rc = parse_arg_all(argc - 1, argv + 1);
148 if (rc != 0) {
149 return rc;
150 }
151
152 conn_handle = parse_arg_uint16("conn", &rc);
153 if (rc != 0) {
154 console_printf("invalid 'conn' parameter\n");
155 return rc;
156 }
157
158 rc = btshell_disc_full(conn_handle);
159 if (rc != 0) {
160 console_printf("error discovering all; rc=%d\n", rc);
161 return rc;
162 }
163
164 return 0;
165 }
166
167 /*****************************************************************************
168 * $gatt-exchange-mtu *
169 *****************************************************************************/
170
171 int
cmd_gatt_exchange_mtu(int argc,char ** argv)172 cmd_gatt_exchange_mtu(int argc, char **argv)
173 {
174 uint16_t conn_handle;
175 int rc;
176
177 rc = parse_arg_all(argc - 1, argv + 1);
178 if (rc != 0) {
179 return rc;
180 }
181
182 conn_handle = parse_arg_uint16("conn", &rc);
183 if (rc != 0) {
184 console_printf("invalid 'conn' parameter\n");
185 return rc;
186 }
187
188 rc = btshell_exchange_mtu(conn_handle);
189 if (rc != 0) {
190 console_printf("error exchanging mtu; rc=%d\n", rc);
191 return rc;
192 }
193
194 return 0;
195 }
196
197 /*****************************************************************************
198 * $gatt-notify *
199 *****************************************************************************/
200
201 int
cmd_gatt_notify(int argc,char ** argv)202 cmd_gatt_notify(int argc, char **argv)
203 {
204 uint16_t attr_handle;
205 int rc;
206
207 rc = parse_arg_all(argc - 1, argv + 1);
208 if (rc != 0) {
209 return rc;
210 }
211
212 attr_handle = parse_arg_uint16("attr", &rc);
213 if (rc != 0) {
214 console_printf("invalid 'attr' parameter\n");
215 return rc;
216 }
217
218 btshell_notify(attr_handle);
219
220 return 0;
221 }
222
223 /*****************************************************************************
224 * $gatt-read *
225 *****************************************************************************/
226
227 #define CMD_READ_MAX_ATTRS 8
228
229 int
cmd_gatt_read(int argc,char ** argv)230 cmd_gatt_read(int argc, char **argv)
231 {
232 static uint16_t attr_handles[CMD_READ_MAX_ATTRS];
233 uint16_t conn_handle;
234 uint16_t start;
235 uint16_t end;
236 uint16_t offset;
237 ble_uuid_any_t uuid;
238 uint8_t num_attr_handles;
239 int is_uuid;
240 int is_long;
241 int rc;
242
243 rc = parse_arg_all(argc - 1, argv + 1);
244 if (rc != 0) {
245 return rc;
246 }
247
248 conn_handle = parse_arg_uint16("conn", &rc);
249 if (rc != 0) {
250 console_printf("invalid 'conn' parameter\n");
251 return rc;
252 }
253
254 is_long = parse_arg_long("long", &rc);
255 if (rc == ENOENT) {
256 is_long = 0;
257 } else if (rc != 0) {
258 console_printf("invalid 'long' parameter\n");
259 return rc;
260 }
261
262 for (num_attr_handles = 0;
263 num_attr_handles < CMD_READ_MAX_ATTRS;
264 num_attr_handles++) {
265
266 attr_handles[num_attr_handles] = parse_arg_uint16("attr", &rc);
267 if (rc == ENOENT) {
268 break;
269 } else if (rc != 0) {
270 console_printf("invalid 'attr' parameter\n");
271 return rc;
272 }
273 }
274
275 rc = parse_arg_uuid("uuid", &uuid);
276 if (rc == ENOENT) {
277 is_uuid = 0;
278 } else if (rc == 0) {
279 is_uuid = 1;
280 } else {
281 console_printf("invalid 'uuid' parameter\n");
282 return rc;
283 }
284
285 start = parse_arg_uint16("start", &rc);
286 if (rc == ENOENT) {
287 start = 0;
288 } else if (rc != 0) {
289 console_printf("invalid 'start' parameter\n");
290 return rc;
291 }
292
293 end = parse_arg_uint16("end", &rc);
294 if (rc == ENOENT) {
295 end = 0;
296 } else if (rc != 0) {
297 console_printf("invalid 'end' parameter\n");
298 return rc;
299 }
300
301 offset = parse_arg_uint16("offset", &rc);
302 if (rc == ENOENT) {
303 offset = 0;
304 } else if (rc != 0) {
305 console_printf("invalid 'offset' parameter\n");
306 return rc;
307 }
308
309 if (num_attr_handles == 1) {
310 if (is_long) {
311 rc = btshell_read_long(conn_handle, attr_handles[0], offset);
312 } else {
313 rc = btshell_read(conn_handle, attr_handles[0]);
314 }
315 } else if (num_attr_handles > 1) {
316 rc = btshell_read_mult(conn_handle, attr_handles, num_attr_handles);
317 } else if (is_uuid) {
318 if (start == 0 || end == 0) {
319 rc = EINVAL;
320 } else {
321 rc = btshell_read_by_uuid(conn_handle, start, end, &uuid.u);
322 }
323 } else {
324 rc = EINVAL;
325 }
326
327 if (rc != 0) {
328 console_printf("error reading characteristic; rc=%d\n", rc);
329 return rc;
330 }
331
332 return 0;
333 }
334
335
336 /*****************************************************************************
337 * $gatt-service-changed *
338 *****************************************************************************/
339
340 int
cmd_gatt_service_changed(int argc,char ** argv)341 cmd_gatt_service_changed(int argc, char **argv)
342 {
343 uint16_t start;
344 uint16_t end;
345 int rc;
346
347 rc = parse_arg_all(argc - 1, argv + 1);
348 if (rc != 0) {
349 return rc;
350 }
351
352 start = parse_arg_uint16("start", &rc);
353 if (rc != 0) {
354 console_printf("invalid 'start' parameter\n");
355 return rc;
356 }
357
358 end = parse_arg_uint16("end", &rc);
359 if (rc != 0) {
360 console_printf("invalid 'end' parameter\n");
361 return rc;
362 }
363
364 ble_svc_gatt_changed(start, end);
365
366 return 0;
367 }
368
369 /*****************************************************************************
370 * $gatt-service-visibility *
371 *****************************************************************************/
372
373 int
cmd_gatt_service_visibility(int argc,char ** argv)374 cmd_gatt_service_visibility(int argc, char **argv)
375 {
376 uint16_t handle;
377 bool vis;
378 int rc;
379
380 rc = parse_arg_all(argc - 1, argv + 1);
381 if (rc != 0) {
382 return rc;
383 }
384
385 handle = parse_arg_uint16("handle", &rc);
386 if (rc != 0) {
387 console_printf("invalid 'handle' parameter\n");
388 return rc;
389 }
390
391 vis = parse_arg_bool("visibility", &rc);
392 if (rc != 0) {
393 console_printf("invalid 'visibility' parameter\n");
394 return rc;
395 }
396
397 ble_gatts_svc_set_visibility(handle, vis);
398
399 return 0;
400 }
401
402 /*****************************************************************************
403 * $gatt-find-included-services *
404 *****************************************************************************/
405
406 int
cmd_gatt_find_included_services(int argc,char ** argv)407 cmd_gatt_find_included_services(int argc, char **argv)
408 {
409 uint16_t start_handle;
410 uint16_t conn_handle;
411 uint16_t end_handle;
412 int rc;
413
414 rc = parse_arg_all(argc - 1, argv + 1);
415 if (rc != 0) {
416 return rc;
417 }
418
419 rc = cmd_parse_conn_start_end(&conn_handle, &start_handle, &end_handle);
420 if (rc != 0) {
421 console_printf("invalid 'conn start end' parameter\n");
422 return rc;
423 }
424
425 rc = btshell_find_inc_svcs(conn_handle, start_handle, end_handle);
426 if (rc != 0) {
427 console_printf("error finding included services; rc=%d\n", rc);
428 return rc;
429 }
430
431 return 0;
432 }
433
434 /*****************************************************************************
435 * $gatt-show *
436 *****************************************************************************/
437
438 int
cmd_gatt_show(int argc,char ** argv)439 cmd_gatt_show(int argc, char **argv)
440 {
441 struct btshell_conn *conn;
442 struct btshell_svc *svc;
443 int i;
444
445 for (i = 0; i < btshell_num_conns; i++) {
446 conn = btshell_conns + i;
447
448 console_printf("CONNECTION: handle=%d\n", conn->handle);
449
450 SLIST_FOREACH(svc, &conn->svcs, next) {
451 print_svc(svc);
452 }
453 }
454
455 return 0;
456 }
457
458 int
cmd_gatt_show_local(int argc,char ** argv)459 cmd_gatt_show_local(int argc, char **argv)
460 {
461 gatt_svr_print_svcs();
462 return 0;
463 }
464
465 /*****************************************************************************
466 * $gatt-write *
467 *****************************************************************************/
468
469 int
cmd_gatt_write(int argc,char ** argv)470 cmd_gatt_write(int argc, char **argv)
471 {
472 struct ble_gatt_attr attrs[MYNEWT_VAL(BLE_GATT_WRITE_MAX_ATTRS)] = { { 0 } };
473 uint16_t attr_handle;
474 uint16_t conn_handle;
475 uint16_t offset;
476 int total_attr_len;
477 int num_attrs;
478 int attr_len;
479 int is_long;
480 int no_rsp;
481 int rc;
482 int i;
483
484 rc = parse_arg_all(argc - 1, argv + 1);
485 if (rc != 0) {
486 return rc;
487 }
488
489 conn_handle = parse_arg_uint16("conn", &rc);
490 if (rc != 0) {
491 console_printf("invalid 'conn' parameter\n");
492 return rc;
493 }
494
495 no_rsp = parse_arg_bool_dflt("no_rsp", 0, &rc);
496 if (rc != 0) {
497 console_printf("invalid 'no_rsp' parameter\n");
498 return rc;
499 }
500
501 is_long = parse_arg_bool_dflt("long", 0, &rc);
502 if (rc != 0) {
503 console_printf("invalid 'long' parameter\n");
504 return rc;
505 }
506
507 total_attr_len = 0;
508 num_attrs = 0;
509 while (1) {
510 attr_handle = parse_arg_uint16("attr", &rc);
511 if (rc == ENOENT) {
512 break;
513 } else if (rc != 0) {
514 rc = -rc;
515 console_printf("invalid 'attr' parameter\n");
516 goto done;
517 }
518
519 rc = parse_arg_byte_stream("value", sizeof cmd_buf - total_attr_len,
520 cmd_buf + total_attr_len, &attr_len);
521 if (rc == ENOENT) {
522 break;
523 } else if (rc != 0) {
524 console_printf("invalid 'value' parameter\n");
525 goto done;
526 }
527
528 offset = parse_arg_uint16("offset", &rc);
529 if (rc == ENOENT) {
530 offset = 0;
531 } else if (rc != 0) {
532 console_printf("invalid 'offset' parameter\n");
533 return rc;
534 }
535
536 if (num_attrs >= sizeof attrs / sizeof attrs[0]) {
537 rc = -EINVAL;
538 goto done;
539 }
540
541 attrs[num_attrs].handle = attr_handle;
542 attrs[num_attrs].offset = offset;
543 attrs[num_attrs].om = ble_hs_mbuf_from_flat(cmd_buf + total_attr_len,
544 attr_len);
545 if (attrs[num_attrs].om == NULL) {
546 goto done;
547 }
548
549 total_attr_len += attr_len;
550 num_attrs++;
551 }
552
553 if (no_rsp) {
554 if (num_attrs != 1) {
555 rc = -EINVAL;
556 goto done;
557 }
558 rc = btshell_write_no_rsp(conn_handle, attrs[0].handle, attrs[0].om);
559 attrs[0].om = NULL;
560 } else if (is_long) {
561 if (num_attrs != 1) {
562 rc = -EINVAL;
563 goto done;
564 }
565 rc = btshell_write_long(conn_handle, attrs[0].handle,
566 attrs[0].offset, attrs[0].om);
567 attrs[0].om = NULL;
568 } else if (num_attrs > 1) {
569 rc = btshell_write_reliable(conn_handle, attrs, num_attrs);
570 } else if (num_attrs == 1) {
571 rc = btshell_write(conn_handle, attrs[0].handle, attrs[0].om);
572 attrs[0].om = NULL;
573 } else {
574 rc = -EINVAL;
575 }
576
577 done:
578 for (i = 0; i < sizeof attrs / sizeof attrs[0]; i++) {
579 os_mbuf_free_chain(attrs[i].om);
580 }
581
582 if (rc != 0) {
583 console_printf("error writing characteristic; rc=%d\n", rc);
584 }
585
586 return rc;
587 }
588