| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| |
| #include <inttypes.h> |
| #include <errno.h> |
| |
| #include "bsp/bsp.h" |
| #include "host/ble_hs_mbuf.h" |
| #include "host/ble_gap.h" |
| #include "services/gatt/ble_svc_gatt.h" |
| #include "console/console.h" |
| #include "btshell.h" |
| #include "cmd.h" |
| #include "cmd_gatt.h" |
| |
| #define CMD_BUF_SZ 256 |
| static bssnz_t uint8_t cmd_buf[CMD_BUF_SZ]; |
| |
| /***************************************************************************** |
| * $gatt-discover * |
| *****************************************************************************/ |
| |
| int |
| cmd_gatt_discover_characteristic(int argc, char **argv) |
| { |
| uint16_t start_handle; |
| uint16_t conn_handle; |
| uint16_t end_handle; |
| ble_uuid_any_t uuid; |
| int rc; |
| |
| rc = parse_arg_all(argc - 1, argv + 1); |
| if (rc != 0) { |
| return rc; |
| } |
| |
| rc = cmd_parse_conn_start_end(&conn_handle, &start_handle, &end_handle); |
| if (rc != 0) { |
| console_printf("invalid 'conn start end' parameter\n"); |
| return rc; |
| } |
| |
| rc = parse_arg_uuid("uuid", &uuid); |
| if (rc == 0) { |
| rc = btshell_disc_chrs_by_uuid(conn_handle, start_handle, end_handle, |
| &uuid.u); |
| } else if (rc == ENOENT) { |
| rc = btshell_disc_all_chrs(conn_handle, start_handle, end_handle); |
| } else { |
| console_printf("invalid 'uuid' parameter\n"); |
| return rc; |
| } |
| if (rc != 0) { |
| console_printf("error discovering characteristics; rc=%d\n", rc); |
| return rc; |
| } |
| |
| return 0; |
| } |
| |
| int |
| cmd_gatt_discover_descriptor(int argc, char **argv) |
| { |
| uint16_t start_handle; |
| uint16_t conn_handle; |
| uint16_t end_handle; |
| int rc; |
| |
| rc = parse_arg_all(argc - 1, argv + 1); |
| if (rc != 0) { |
| return rc; |
| } |
| |
| rc = cmd_parse_conn_start_end(&conn_handle, &start_handle, &end_handle); |
| if (rc != 0) { |
| console_printf("invalid 'conn start end' parameter\n"); |
| return rc; |
| } |
| |
| rc = btshell_disc_all_dscs(conn_handle, start_handle, end_handle); |
| if (rc != 0) { |
| console_printf("error discovering descriptors; rc=%d\n", rc); |
| return rc; |
| } |
| |
| return 0; |
| } |
| |
| int |
| cmd_gatt_discover_service(int argc, char **argv) |
| { |
| ble_uuid_any_t uuid; |
| int conn_handle; |
| int rc; |
| |
| rc = parse_arg_all(argc - 1, argv + 1); |
| if (rc != 0) { |
| return rc; |
| } |
| |
| conn_handle = parse_arg_uint16("conn", &rc); |
| if (rc != 0) { |
| console_printf("invalid 'conn' parameter\n"); |
| return rc; |
| } |
| |
| rc = parse_arg_uuid("uuid", &uuid); |
| if (rc == 0) { |
| rc = btshell_disc_svc_by_uuid(conn_handle, &uuid.u); |
| } else if (rc == ENOENT) { |
| rc = btshell_disc_svcs(conn_handle); |
| } else { |
| console_printf("invalid 'uuid' parameter\n"); |
| return rc; |
| } |
| |
| if (rc != 0) { |
| console_printf("error discovering services; rc=%d\n", rc); |
| return rc; |
| } |
| |
| return 0; |
| } |
| |
| int |
| cmd_gatt_discover_full(int argc, char **argv) |
| { |
| int conn_handle; |
| int rc; |
| |
| rc = parse_arg_all(argc - 1, argv + 1); |
| if (rc != 0) { |
| return rc; |
| } |
| |
| conn_handle = parse_arg_uint16("conn", &rc); |
| if (rc != 0) { |
| console_printf("invalid 'conn' parameter\n"); |
| return rc; |
| } |
| |
| rc = btshell_disc_full(conn_handle); |
| if (rc != 0) { |
| console_printf("error discovering all; rc=%d\n", rc); |
| return rc; |
| } |
| |
| return 0; |
| } |
| |
| /***************************************************************************** |
| * $gatt-exchange-mtu * |
| *****************************************************************************/ |
| |
| int |
| cmd_gatt_exchange_mtu(int argc, char **argv) |
| { |
| uint16_t conn_handle; |
| int rc; |
| |
| rc = parse_arg_all(argc - 1, argv + 1); |
| if (rc != 0) { |
| return rc; |
| } |
| |
| conn_handle = parse_arg_uint16("conn", &rc); |
| if (rc != 0) { |
| console_printf("invalid 'conn' parameter\n"); |
| return rc; |
| } |
| |
| rc = btshell_exchange_mtu(conn_handle); |
| if (rc != 0) { |
| console_printf("error exchanging mtu; rc=%d\n", rc); |
| return rc; |
| } |
| |
| return 0; |
| } |
| |
| /***************************************************************************** |
| * $gatt-notify * |
| *****************************************************************************/ |
| |
| int |
| cmd_gatt_notify(int argc, char **argv) |
| { |
| uint16_t attr_handle; |
| int rc; |
| |
| rc = parse_arg_all(argc - 1, argv + 1); |
| if (rc != 0) { |
| return rc; |
| } |
| |
| attr_handle = parse_arg_uint16("attr", &rc); |
| if (rc != 0) { |
| console_printf("invalid 'attr' parameter\n"); |
| return rc; |
| } |
| |
| btshell_notify(attr_handle); |
| |
| return 0; |
| } |
| |
| /***************************************************************************** |
| * $gatt-read * |
| *****************************************************************************/ |
| |
| #define CMD_READ_MAX_ATTRS 8 |
| |
| int |
| cmd_gatt_read(int argc, char **argv) |
| { |
| static uint16_t attr_handles[CMD_READ_MAX_ATTRS]; |
| uint16_t conn_handle; |
| uint16_t start; |
| uint16_t end; |
| uint16_t offset; |
| ble_uuid_any_t uuid; |
| uint8_t num_attr_handles; |
| int is_uuid; |
| int is_long; |
| int rc; |
| |
| rc = parse_arg_all(argc - 1, argv + 1); |
| if (rc != 0) { |
| return rc; |
| } |
| |
| conn_handle = parse_arg_uint16("conn", &rc); |
| if (rc != 0) { |
| console_printf("invalid 'conn' parameter\n"); |
| return rc; |
| } |
| |
| is_long = parse_arg_long("long", &rc); |
| if (rc == ENOENT) { |
| is_long = 0; |
| } else if (rc != 0) { |
| console_printf("invalid 'long' parameter\n"); |
| return rc; |
| } |
| |
| for (num_attr_handles = 0; |
| num_attr_handles < CMD_READ_MAX_ATTRS; |
| num_attr_handles++) { |
| |
| attr_handles[num_attr_handles] = parse_arg_uint16("attr", &rc); |
| if (rc == ENOENT) { |
| break; |
| } else if (rc != 0) { |
| console_printf("invalid 'attr' parameter\n"); |
| return rc; |
| } |
| } |
| |
| rc = parse_arg_uuid("uuid", &uuid); |
| if (rc == ENOENT) { |
| is_uuid = 0; |
| } else if (rc == 0) { |
| is_uuid = 1; |
| } else { |
| console_printf("invalid 'uuid' parameter\n"); |
| return rc; |
| } |
| |
| start = parse_arg_uint16("start", &rc); |
| if (rc == ENOENT) { |
| start = 0; |
| } else if (rc != 0) { |
| console_printf("invalid 'start' parameter\n"); |
| return rc; |
| } |
| |
| end = parse_arg_uint16("end", &rc); |
| if (rc == ENOENT) { |
| end = 0; |
| } else if (rc != 0) { |
| console_printf("invalid 'end' parameter\n"); |
| return rc; |
| } |
| |
| offset = parse_arg_uint16("offset", &rc); |
| if (rc == ENOENT) { |
| offset = 0; |
| } else if (rc != 0) { |
| console_printf("invalid 'offset' parameter\n"); |
| return rc; |
| } |
| |
| if (num_attr_handles == 1) { |
| if (is_long) { |
| rc = btshell_read_long(conn_handle, attr_handles[0], offset); |
| } else { |
| rc = btshell_read(conn_handle, attr_handles[0]); |
| } |
| } else if (num_attr_handles > 1) { |
| rc = btshell_read_mult(conn_handle, attr_handles, num_attr_handles); |
| } else if (is_uuid) { |
| if (start == 0 || end == 0) { |
| rc = EINVAL; |
| } else { |
| rc = btshell_read_by_uuid(conn_handle, start, end, &uuid.u); |
| } |
| } else { |
| rc = EINVAL; |
| } |
| |
| if (rc != 0) { |
| console_printf("error reading characteristic; rc=%d\n", rc); |
| return rc; |
| } |
| |
| return 0; |
| } |
| |
| |
| /***************************************************************************** |
| * $gatt-service-changed * |
| *****************************************************************************/ |
| |
| int |
| cmd_gatt_service_changed(int argc, char **argv) |
| { |
| uint16_t start; |
| uint16_t end; |
| int rc; |
| |
| rc = parse_arg_all(argc - 1, argv + 1); |
| if (rc != 0) { |
| return rc; |
| } |
| |
| start = parse_arg_uint16("start", &rc); |
| if (rc != 0) { |
| console_printf("invalid 'start' parameter\n"); |
| return rc; |
| } |
| |
| end = parse_arg_uint16("end", &rc); |
| if (rc != 0) { |
| console_printf("invalid 'end' parameter\n"); |
| return rc; |
| } |
| |
| ble_svc_gatt_changed(start, end); |
| |
| return 0; |
| } |
| |
| /***************************************************************************** |
| * $gatt-service-visibility * |
| *****************************************************************************/ |
| |
| int |
| cmd_gatt_service_visibility(int argc, char **argv) |
| { |
| uint16_t handle; |
| bool vis; |
| int rc; |
| |
| rc = parse_arg_all(argc - 1, argv + 1); |
| if (rc != 0) { |
| return rc; |
| } |
| |
| handle = parse_arg_uint16("handle", &rc); |
| if (rc != 0) { |
| console_printf("invalid 'handle' parameter\n"); |
| return rc; |
| } |
| |
| vis = parse_arg_bool("visibility", &rc); |
| if (rc != 0) { |
| console_printf("invalid 'visibility' parameter\n"); |
| return rc; |
| } |
| |
| ble_gatts_svc_set_visibility(handle, vis); |
| |
| return 0; |
| } |
| |
| /***************************************************************************** |
| * $gatt-find-included-services * |
| *****************************************************************************/ |
| |
| int |
| cmd_gatt_find_included_services(int argc, char **argv) |
| { |
| uint16_t start_handle; |
| uint16_t conn_handle; |
| uint16_t end_handle; |
| int rc; |
| |
| rc = parse_arg_all(argc - 1, argv + 1); |
| if (rc != 0) { |
| return rc; |
| } |
| |
| rc = cmd_parse_conn_start_end(&conn_handle, &start_handle, &end_handle); |
| if (rc != 0) { |
| console_printf("invalid 'conn start end' parameter\n"); |
| return rc; |
| } |
| |
| rc = btshell_find_inc_svcs(conn_handle, start_handle, end_handle); |
| if (rc != 0) { |
| console_printf("error finding included services; rc=%d\n", rc); |
| return rc; |
| } |
| |
| return 0; |
| } |
| |
| /***************************************************************************** |
| * $gatt-show * |
| *****************************************************************************/ |
| |
| int |
| cmd_gatt_show(int argc, char **argv) |
| { |
| struct btshell_conn *conn; |
| struct btshell_svc *svc; |
| int i; |
| |
| for (i = 0; i < btshell_num_conns; i++) { |
| conn = btshell_conns + i; |
| |
| console_printf("CONNECTION: handle=%d\n", conn->handle); |
| |
| SLIST_FOREACH(svc, &conn->svcs, next) { |
| print_svc(svc); |
| } |
| } |
| |
| return 0; |
| } |
| |
| int |
| cmd_gatt_show_local(int argc, char **argv) |
| { |
| gatt_svr_print_svcs(); |
| return 0; |
| } |
| |
| /***************************************************************************** |
| * $gatt-write * |
| *****************************************************************************/ |
| |
| int |
| cmd_gatt_write(int argc, char **argv) |
| { |
| struct ble_gatt_attr attrs[MYNEWT_VAL(BLE_GATT_WRITE_MAX_ATTRS)] = { { 0 } }; |
| uint16_t attr_handle; |
| uint16_t conn_handle; |
| uint16_t offset; |
| int total_attr_len; |
| int num_attrs; |
| int attr_len; |
| int is_long; |
| int no_rsp; |
| int rc; |
| int i; |
| |
| rc = parse_arg_all(argc - 1, argv + 1); |
| if (rc != 0) { |
| return rc; |
| } |
| |
| conn_handle = parse_arg_uint16("conn", &rc); |
| if (rc != 0) { |
| console_printf("invalid 'conn' parameter\n"); |
| return rc; |
| } |
| |
| no_rsp = parse_arg_bool_dflt("no_rsp", 0, &rc); |
| if (rc != 0) { |
| console_printf("invalid 'no_rsp' parameter\n"); |
| return rc; |
| } |
| |
| is_long = parse_arg_bool_dflt("long", 0, &rc); |
| if (rc != 0) { |
| console_printf("invalid 'long' parameter\n"); |
| return rc; |
| } |
| |
| total_attr_len = 0; |
| num_attrs = 0; |
| while (1) { |
| attr_handle = parse_arg_uint16("attr", &rc); |
| if (rc == ENOENT) { |
| break; |
| } else if (rc != 0) { |
| rc = -rc; |
| console_printf("invalid 'attr' parameter\n"); |
| goto done; |
| } |
| |
| rc = parse_arg_byte_stream("value", sizeof cmd_buf - total_attr_len, |
| cmd_buf + total_attr_len, &attr_len); |
| if (rc == ENOENT) { |
| break; |
| } else if (rc != 0) { |
| console_printf("invalid 'value' parameter\n"); |
| goto done; |
| } |
| |
| offset = parse_arg_uint16("offset", &rc); |
| if (rc == ENOENT) { |
| offset = 0; |
| } else if (rc != 0) { |
| console_printf("invalid 'offset' parameter\n"); |
| return rc; |
| } |
| |
| if (num_attrs >= sizeof attrs / sizeof attrs[0]) { |
| rc = -EINVAL; |
| goto done; |
| } |
| |
| attrs[num_attrs].handle = attr_handle; |
| attrs[num_attrs].offset = offset; |
| attrs[num_attrs].om = ble_hs_mbuf_from_flat(cmd_buf + total_attr_len, |
| attr_len); |
| if (attrs[num_attrs].om == NULL) { |
| goto done; |
| } |
| |
| total_attr_len += attr_len; |
| num_attrs++; |
| } |
| |
| if (no_rsp) { |
| if (num_attrs != 1) { |
| rc = -EINVAL; |
| goto done; |
| } |
| rc = btshell_write_no_rsp(conn_handle, attrs[0].handle, attrs[0].om); |
| attrs[0].om = NULL; |
| } else if (is_long) { |
| if (num_attrs != 1) { |
| rc = -EINVAL; |
| goto done; |
| } |
| rc = btshell_write_long(conn_handle, attrs[0].handle, |
| attrs[0].offset, attrs[0].om); |
| attrs[0].om = NULL; |
| } else if (num_attrs > 1) { |
| rc = btshell_write_reliable(conn_handle, attrs, num_attrs); |
| } else if (num_attrs == 1) { |
| rc = btshell_write(conn_handle, attrs[0].handle, attrs[0].om); |
| attrs[0].om = NULL; |
| } else { |
| rc = -EINVAL; |
| } |
| |
| done: |
| for (i = 0; i < sizeof attrs / sizeof attrs[0]; i++) { |
| os_mbuf_free_chain(attrs[i].om); |
| } |
| |
| if (rc != 0) { |
| console_printf("error writing characteristic; rc=%d\n", rc); |
| } |
| |
| return rc; |
| } |