2017-12-11 14:58:36

by Steve Brown

[permalink] [raw]
Subject: [PATCH 0/3] mesh: Add configuration commands to meshctl

From: Steve Brown <[email protected]>

This series adds additional configuration commands to meshctl.

It also fixes a bug that was exposed by the commands.

This work was done in conjunction with the Zephyr pull request.
https://github.com/zephyrproject-rtos/zephyr/pull/5322

For testing, that patch is available at
https://github.com/zephyrproject-rtos/zephyr/pull/5322.patch

Steve Brown (3):
mesh: Segmentation fails in gatt.c:pipe_write()
mesh: meshctl: Add commands
mesh: meshctl: Add support for subscriptions in node and database

mesh/config-client.c | 461 ++++++++++++++++++++++++++++++++++++++++++++++++---
mesh/gatt.c | 141 ++--------------
mesh/net.c | 20 ++-
mesh/node.c | 32 ++++
mesh/node.h | 3 +
mesh/prov-db.c | 66 ++++++++
mesh/prov-db.h | 2 +
7 files changed, 580 insertions(+), 145 deletions(-)

--
2.11.0



2017-12-12 11:54:51

by Luiz Augusto von Dentz

[permalink] [raw]
Subject: Re: [PATCH 1/3] mesh: Segmentation fails in gatt.c:pipe_write()

Hi Steve,

On Mon, Dec 11, 2017 at 12:58 PM, <[email protected]> wrote:
> From: Steve Brown <[email protected]>
>
> If the first command output in a new connection exceeds 20 bytes,
> mesh_gatt_write sets the SAR to FIRST as the write_mtu is initially 0
> and the default is GATT_MTU-3 (20).
>
> When pipe_write gets called, a new larger write_mtu has been set, but
> the SAR is still set to FIRST. It's assumed that data->gatt_len >
> max_len. However, it's not which causes lots of bogus output.
>
> ---
>
> At Luiz' suggestion.
>
> 1. The WriteValue code has been removed
> 2. The SAR logic has been moved to pipe_write
>
> It was tested against the zephyr mesh_shell and a small mtu to test the
> segmentation logic.
> ---
> mesh/gatt.c | 141 ++++++++----------------------------------------------------
> 1 file changed, 18 insertions(+), 123 deletions(-)
>
> diff --git a/mesh/gatt.c b/mesh/gatt.c
> index 197291e67..9116a9de1 100644
> --- a/mesh/gatt.c
> +++ b/mesh/gatt.c
> @@ -102,27 +102,6 @@ static void write_data_free(void *user_data)
> free(data);
> }
>
> -static void write_setup(DBusMessageIter *iter, void *user_data)
> -{
> - struct write_data *data = user_data;
> - struct iovec *iov = &data->iov;
> - DBusMessageIter array, dict;
> -
> - dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "y", &array);
> - dbus_message_iter_append_fixed_array(&array, DBUS_TYPE_BYTE,
> - &iov->iov_base, iov->iov_len);
> - dbus_message_iter_close_container(iter, &array);
> -
> - dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
> - DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
> - DBUS_TYPE_STRING_AS_STRING
> - DBUS_TYPE_VARIANT_AS_STRING
> - DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
> - &dict);
> -
> - dbus_message_iter_close_container(iter, &dict);
> -}
> -
> uint16_t mesh_gatt_sar(uint8_t **pkt, uint16_t size)
> {
> const uint8_t *data = *pkt;
> @@ -192,11 +171,12 @@ static bool pipe_write(struct io *io, void *user_data)
> struct write_data *data = user_data;
> struct iovec iov[2];
> uint8_t sar;
> - uint8_t max_len = write_mtu - 4;
> + uint8_t max_len;
>
> if (data == NULL)
> return true;
>
> + max_len = write_mtu ? write_mtu - 3 - 1 : GATT_MTU - 3 - 1;
> print_byte_array("GATT-TX:\t", data->gatt_data, data->gatt_len);
>
> sar = data->gatt_data[0];
> @@ -204,6 +184,14 @@ static bool pipe_write(struct io *io, void *user_data)
> data->iov.iov_base = data->gatt_data + 1;
> data->iov.iov_len--;
>
> + sar = data->gatt_data[0] & GATT_TYPE_MASK;
> + data->gatt_len--;
> +
> + if (data->gatt_len > max_len) {
> + sar |= GATT_SAR_FIRST;
> + data->iov.iov_len = max_len;
> + }
> +
> iov[0].iov_base = &sar;
> iov[0].iov_len = sizeof(sar);
>
> @@ -245,73 +233,6 @@ static bool pipe_write(struct io *io, void *user_data)
> }
> }
>
> -static void write_reply(DBusMessage *message, void *user_data)
> -{
> - struct write_data *data = user_data;
> - struct write_data *tmp;
> - uint8_t *dptr = data->gatt_data;
> - uint8_t max_len = GATT_MTU - 3;
> - uint8_t max_seg = GATT_MTU - 4;
> - DBusError error;
> -
> - dbus_error_init(&error);
> -
> - if (dbus_set_error_from_message(&error, message) == TRUE) {
> - bt_shell_printf("Failed to write: %s\n", error.name);
> - dbus_error_free(&error);
> - return;
> - }
> -
> - if (data == NULL)
> - return;
> -
> - switch (data->gatt_data[0] & GATT_SAR_MASK) {
> - case GATT_SAR_FIRST:
> - case GATT_SAR_CONTINUE:
> - tmp = g_new0(struct write_data, 1);
> - if (!data)
> - return;
> -
> - *tmp = *data;
> - tmp->gatt_data = g_malloc(data->gatt_len);
> -
> - if (!tmp->gatt_data) {
> - g_free(tmp);
> - return;
> - }
> -
> - tmp->gatt_data[0] = dptr[0];
> - data = tmp;
> - memcpy(data->gatt_data + 1, dptr + max_len,
> - data->gatt_len - max_seg);
> - data->gatt_len -= max_seg;
> - data->gatt_data[0] &= GATT_TYPE_MASK;
> - data->iov.iov_base = data->gatt_data;
> - if (max_len < data->gatt_len) {
> - data->iov.iov_len = max_len;
> - data->gatt_data[0] |= GATT_SAR_CONTINUE;
> - } else {
> - data->iov.iov_len = data->gatt_len;
> - data->gatt_data[0] |= GATT_SAR_LAST;
> - }
> -
> - break;
> -
> - default:
> - if(data->cb)
> - data->cb(message, data->user_data);
> - return;
> - }
> -
> - if (g_dbus_proxy_method_call(data->proxy, "WriteValue", write_setup,
> - write_reply, data, write_data_free) == FALSE) {
> - bt_shell_printf("Failed to write\n");
> - write_data_free(data);
> - return;
> - }
> -
> -}
> -
> static void write_io_destroy(void)
> {
> io_destroy(write_io);
> @@ -361,12 +282,8 @@ static void acquire_write_reply(DBusMessage *message, void *user_data)
>
> if (dbus_set_error_from_message(&error, message) == TRUE) {
> dbus_error_free(&error);
> - if (g_dbus_proxy_method_call(data->proxy, "WriteValue",
> - write_setup, write_reply, data,
> - write_data_free) == FALSE) {
> - bt_shell_printf("Failed to write\n");
> - write_data_free(data);
> - }
> + bt_shell_printf("Failed to write\n");
> + write_data_free(data);
> return;
> }
>
> @@ -402,8 +319,6 @@ bool mesh_gatt_write(GDBusProxy *proxy, uint8_t *buf, uint16_t len,
> GDBusReturnFunction cb, void *user_data)
> {
> struct write_data *data;
> - DBusMessageIter iter;
> - uint8_t max_len;
>
> if (!buf || !len)
> return false;
> @@ -415,17 +330,11 @@ bool mesh_gatt_write(GDBusProxy *proxy, uint8_t *buf, uint16_t len,
> if (!data)
> return false;
>
> - max_len = write_mtu ? write_mtu - 3 : GATT_MTU - 3;
> -
> /* TODO: should keep in queue in case we need to cancel write? */
>
> data->gatt_len = len;
> data->gatt_data = g_memdup(buf, len);
> data->gatt_data[0] &= GATT_TYPE_MASK;
> - if (max_len < len) {
> - len = max_len;
> - data->gatt_data[0] |= GATT_SAR_FIRST;
> - }
> data->iov.iov_base = data->gatt_data;
> data->iov.iov_len = len;
> data->proxy = proxy;
> @@ -435,27 +344,13 @@ bool mesh_gatt_write(GDBusProxy *proxy, uint8_t *buf, uint16_t len,
> if (write_io)
> return pipe_write(write_io, data);
>
> - if (g_dbus_proxy_get_property(proxy, "WriteAcquired", &iter)) {
> - if (g_dbus_proxy_method_call(proxy, "AcquireWrite",
> - acquire_setup, acquire_write_reply,
> - data, NULL) == FALSE) {
> - bt_shell_printf("Failed to AcquireWrite\n");
> - write_data_free(data);
> - return false;
> - }
> - } else {
> - if (g_dbus_proxy_method_call(data->proxy, "WriteValue",
> - write_setup, write_reply, data,
> - write_data_free) == FALSE) {
> - bt_shell_printf("Failed to write\n");
> - write_data_free(data);
> - return false;
> - }
> - print_byte_array("GATT-TX: ", buf, len);
> - bt_shell_printf("Attempting to write %s\n",
> - g_dbus_proxy_get_path(proxy));
> + if (g_dbus_proxy_method_call(proxy, "AcquireWrite",
> + acquire_setup, acquire_write_reply,
> + data, NULL) == FALSE) {
> + bt_shell_printf("Failed to AcquireWrite\n");
> + write_data_free(data);
> + return false;
> }
> -
> return true;
> }
>
> --
> 2.11.0

Applied just this one.

--
Luiz Augusto von Dentz

2017-12-11 22:13:25

by Steve Brown

[permalink] [raw]
Subject: Re: [PATCH 2/3] mesh: meshctl: Add commands

Hi Luiz,

On Mon, 2017-12-11 at 13:12 -0200, Luiz Augusto von Dentz wrote:
> Hi Steve,
>
> On Mon, Dec 11, 2017 at 12:58 PM, <[email protected]> wrote:
> > From: Steve Brown <[email protected]>
> >
> > Get/Set Proxy
> > Get/Set Ident
> > Get/Set Relay
> > Set Heartbeat
> > Get Publication
> > Get/Set Subscription
>
> Ive split these into individual patches for command and then add in
> the description what the expected output, etc. Btw I think it would
> be
> better to switch from get-set style to cmd [value], so if there is no
> arguments then it just read the value, that way reduce the amount of
> commands and also make the autocomplete a lot more useful since the
> commands shall start with something other than set/get.
>

I have the individual command patches ready to go along with the
examples and sample output.

Can you review patch 1/3 gatt.c patch?

It's required for the command patches to work.

I'll follow up with a separate patch to add the subscriptions to the
node and json database.

Steve


2017-12-11 16:14:44

by Steve Brown

[permalink] [raw]
Subject: Re: [PATCH 2/3] mesh: meshctl: Add commands

Hi Luiz,

On Mon, 2017-12-11 at 13:49 -0200, Luiz Augusto von Dentz wrote:
> Hi Steve,
>
> On Mon, Dec 11, 2017 at 1:40 PM, Steve Brown <[email protected]>
> wrote:
> > Hi Luiz,
> >
> > On Mon, 2017-12-11 at 13:12 -0200, Luiz Augusto von Dentz wrote:
> > > Hi Steve,
> > >
> > > On Mon, Dec 11, 2017 at 12:58 PM, <[email protected]> wrote:
> > > > From: Steve Brown <[email protected]>
> > > >
> > > > Get/Set Proxy
> > > > Get/Set Ident
> > > > Get/Set Relay
> > > > Set Heartbeat
> > > > Get Publication
> > > > Get/Set Subscription
> > >
> > > Ive split these into individual patches for command and then add
> > > in
> > > the description what the expected output, etc. Btw I think it
> > > would
> > > be
> > > better to switch from get-set style to cmd [value], so if there
> > > is no
> > > arguments then it just read the value, that way reduce the amount
> > > of
> > > commands and also make the autocomplete a lot more useful since
> > > the
> > > commands shall start with something other than set/get.
> > >
> >
> > OK, I'll make the changes.
> >
> > This would then affect all the meshctl commands.
> >
> > I'll have to rearrange the parameter sequence in some of the
> > commands
> > so I can distinguish between a get and a set.
> >
> > So, where the publish commands currently are
> > set-pub <ele-addr> <pub-addr> <app-idx> .... and
> > get-pub <ele-addr> <model>
> >
> > They would become
> > pub <ele-addr> <model> <pub-addr> .... for set and
> > pub <ele-addr> <model> for get
> >
> > Do I understand this correctly?
>
> Yep, but note that due to shell parsing the .arg string you may need
> to add some parameters to set as optional otherwise the command for
> get will never succeed since it will require all mandatory arguments
> to be given as in the set version. If that becomes impractical and
> cause a lot more code to detect what mode the command shall operate
> then perhaps leave as it is and just revert to pub-set/pub-get to
> make
> autocomplete a little more useful.
>
> > I left the subscription node and database patch separate. I'm on
> > less
> > solid ground here. I basically cribbed the code for bind.
> >
> > I added the UUID to the database as later I'd like to try to re-
> > provision a known unprovisioned node from the database.
> >
> > Steve
> >

I'm not sure I understand the problem.

Some of the get commands require parameters. The same parameters are
also required in the set commands, only more. Currently, they aren't in
the same order. I was going to rearrange the parameters so the set and
get commands have the same parameter sequence. The set command would
just have more. That way I can distinguish between them by the number
of parameters.

Steve


2017-12-11 15:49:14

by Luiz Augusto von Dentz

[permalink] [raw]
Subject: Re: [PATCH 2/3] mesh: meshctl: Add commands

Hi Steve,

On Mon, Dec 11, 2017 at 1:40 PM, Steve Brown <[email protected]> wrote:
> Hi Luiz,
>
> On Mon, 2017-12-11 at 13:12 -0200, Luiz Augusto von Dentz wrote:
>> Hi Steve,
>>
>> On Mon, Dec 11, 2017 at 12:58 PM, <[email protected]> wrote:
>> > From: Steve Brown <[email protected]>
>> >
>> > Get/Set Proxy
>> > Get/Set Ident
>> > Get/Set Relay
>> > Set Heartbeat
>> > Get Publication
>> > Get/Set Subscription
>>
>> Ive split these into individual patches for command and then add in
>> the description what the expected output, etc. Btw I think it would
>> be
>> better to switch from get-set style to cmd [value], so if there is no
>> arguments then it just read the value, that way reduce the amount of
>> commands and also make the autocomplete a lot more useful since the
>> commands shall start with something other than set/get.
>>
>
> OK, I'll make the changes.
>
> This would then affect all the meshctl commands.
>
> I'll have to rearrange the parameter sequence in some of the commands
> so I can distinguish between a get and a set.
>
> So, where the publish commands currently are
> set-pub <ele-addr> <pub-addr> <app-idx> .... and
> get-pub <ele-addr> <model>
>
> They would become
> pub <ele-addr> <model> <pub-addr> .... for set and
> pub <ele-addr> <model> for get
>
> Do I understand this correctly?

Yep, but note that due to shell parsing the .arg string you may need
to add some parameters to set as optional otherwise the command for
get will never succeed since it will require all mandatory arguments
to be given as in the set version. If that becomes impractical and
cause a lot more code to detect what mode the command shall operate
then perhaps leave as it is and just revert to pub-set/pub-get to make
autocomplete a little more useful.

> I left the subscription node and database patch separate. I'm on less
> solid ground here. I basically cribbed the code for bind.
>
> I added the UUID to the database as later I'd like to try to re-
> provision a known unprovisioned node from the database.
>
> Steve
>



--
Luiz Augusto von Dentz

2017-12-11 15:40:28

by Steve Brown

[permalink] [raw]
Subject: Re: [PATCH 2/3] mesh: meshctl: Add commands

Hi Luiz,

On Mon, 2017-12-11 at 13:12 -0200, Luiz Augusto von Dentz wrote:
> Hi Steve,
>
> On Mon, Dec 11, 2017 at 12:58 PM, <[email protected]> wrote:
> > From: Steve Brown <[email protected]>
> >
> > Get/Set Proxy
> > Get/Set Ident
> > Get/Set Relay
> > Set Heartbeat
> > Get Publication
> > Get/Set Subscription
>
> Ive split these into individual patches for command and then add in
> the description what the expected output, etc. Btw I think it would
> be
> better to switch from get-set style to cmd [value], so if there is no
> arguments then it just read the value, that way reduce the amount of
> commands and also make the autocomplete a lot more useful since the
> commands shall start with something other than set/get.
>

OK, I'll make the changes.

This would then affect all the meshctl commands.

I'll have to rearrange the parameter sequence in some of the commands
so I can distinguish between a get and a set.

So, where the publish commands currently are
set-pub <ele-addr> <pub-addr> <app-idx> .... and
get-pub <ele-addr> <model>

They would become
pub <ele-addr> <model> <pub-addr> .... for set and
pub <ele-addr> <model> for get

Do I understand this correctly?

I left the subscription node and database patch separate. I'm on less
solid ground here. I basically cribbed the code for bind.

I added the UUID to the database as later I'd like to try to re-
provision a known unprovisioned node from the database.

Steve


2017-12-11 15:12:42

by Luiz Augusto von Dentz

[permalink] [raw]
Subject: Re: [PATCH 2/3] mesh: meshctl: Add commands

Hi Steve,

On Mon, Dec 11, 2017 at 12:58 PM, <[email protected]> wrote:
> From: Steve Brown <[email protected]>
>
> Get/Set Proxy
> Get/Set Ident
> Get/Set Relay
> Set Heartbeat
> Get Publication
> Get/Set Subscription

Ive split these into individual patches for command and then add in
the description what the expected output, etc. Btw I think it would be
better to switch from get-set style to cmd [value], so if there is no
arguments then it just read the value, that way reduce the amount of
commands and also make the autocomplete a lot more useful since the
commands shall start with something other than set/get.

> The json database is not updated (WIP)
> Get App
> ---
> mesh/config-client.c | 446 ++++++++++++++++++++++++++++++++++++++++++++++++---
> mesh/net.c | 20 ++-
> 2 files changed, 444 insertions(+), 22 deletions(-)
>
> diff --git a/mesh/config-client.c b/mesh/config-client.c
> index 3d618b6a6..35ccbff45 100644
> --- a/mesh/config-client.c
> +++ b/mesh/config-client.c
> @@ -61,6 +61,7 @@ static bool client_msg_recvd(uint16_t src, uint8_t *data,
> uint8_t ele_idx;
> struct mesh_publication pub;
> int n;
> + uint16_t i;
>
> if (mesh_opcode_get(data, len, &opcode, &n)) {
> len -= n;
> @@ -158,6 +159,29 @@ static bool client_msg_recvd(uint16_t src, uint8_t *data,
> prov_db_add_binding(node, addr - src, mod_id, app_idx);
> break;
>
> + case OP_NODE_IDENTITY_STATUS:
> + if (len != 4)
> + return true;
> + bt_shell_printf("Network index 0x%04x has "
> + "Node Identity state 0x%02x %s\n",
> + get_le16(data + 1), data[3],
> + mesh_status_str(data[0]));
> +
> + case OP_CONFIG_RELAY_STATUS:
> + if (len != 2)
> + return true;
> + bt_shell_printf("Node %4.4x Relay state: 0x%02x"
> + " count: %d steps: %d\n",
> + src, data[0], data[1]>>5, data[1] & 0x1f);
> + break;
> +
> + case OP_CONFIG_PROXY_STATUS:
> + if (len != 1)
> + return true;
> + bt_shell_printf("Node %4.4x Proxy state: 0x%02x\n",
> + src, data[0]);
> + break;
> +
> case OP_CONFIG_DEFAULT_TTL_STATUS:
> if (len != 1)
> return true;
> @@ -170,9 +194,9 @@ static bool client_msg_recvd(uint16_t src, uint8_t *data,
> if (len != 12 && len != 14)
> return true;
>
> - bt_shell_printf("\nSet publication for node %4.4x status: %s\n", src,
> - data[0] == MESH_STATUS_SUCCESS ? "Success" :
> - mesh_status_str(data[0]));
> + bt_shell_printf("\nSet publication for node %4.4x status: %s\n",
> + src, data[0] == MESH_STATUS_SUCCESS ?
> + "Success" : mesh_status_str(data[0]));
>
> if (data[0] != MESH_STATUS_SUCCESS)
> return true;
> @@ -189,6 +213,7 @@ static bool client_msg_recvd(uint16_t src, uint8_t *data,
> pub.ttl = data[7];
> pub.period = data[8];
> n = (data[8] & 0x3f);
> + bt_shell_printf("Publication address: 0x%04x\n", pub.u.addr16);
> switch (data[8] >> 6) {
> case 0:
> bt_shell_printf("Period: %d ms\n", n * 100);
> @@ -206,7 +231,8 @@ static bool client_msg_recvd(uint16_t src, uint8_t *data,
>
> pub.retransmit = data[9];
> bt_shell_printf("Retransmit count: %d\n", data[9] >> 5);
> - bt_shell_printf("Retransmit Interval Steps: %d\n", data[9] & 0x1f);
> + bt_shell_printf("Retransmit Interval Steps: %d\n",
> + data[9] & 0x1f);
>
> ele_idx = ele_addr - node_get_primary(node);
>
> @@ -218,7 +244,81 @@ static bool client_msg_recvd(uint16_t src, uint8_t *data,
> prov_db_node_set_model_pub(node, ele_idx, mod_id,
> node_model_pub_get(node, ele_idx, mod_id));
> break;
> +
> + /* Per Mesh Profile 4.3.2.19 */
> + case OP_CONFIG_MODEL_SUB_STATUS:
> + bt_shell_printf("\nSubscription changed"
> + " for node %4.4x status: %s\n", src,
> + data[0] == MESH_STATUS_SUCCESS ? "Success" :
> + mesh_status_str(data[0]));
> +
> + if (data[0] != MESH_STATUS_SUCCESS)
> + return true;
> +
> + bt_shell_printf("Element Addr:\t%4.4x\n", get_le16(data + 1));
> + bt_shell_printf("Subscr Addr:\t%4.4x\n", get_le16(data + 3));
> + bt_shell_printf("Model ID:\t%4.4x\n", get_le16(data + 5));
> + break;
> +
> + /* TODO */
> + /* Save subscription info in database */
> +
> + /* Per Mesh Profile 4.3.2.27 */
> + case OP_CONFIG_MODEL_SUB_LIST:
> +
> + bt_shell_printf("\nSubscription list for node %4.4x "
> + "length: %u status: %s\n", src, len,
> + data[0] == MESH_STATUS_SUCCESS ? "Success" :
> + mesh_status_str(data[0]));
> +
> + if (data[0] != MESH_STATUS_SUCCESS)
> + return true;
> +
> + bt_shell_printf("Element Addr:\t%4.4x\n", get_le16(data + 1));
> + bt_shell_printf("Model ID:\t%4.4x\n", get_le16(data + 3));
> +
> + for (i = 5; i < len; i += 2)
> + bt_shell_printf("Subscr Addr:\t%4.4x\n",
> + get_le16(data + i));
> + break;
> +
> + /* Per Mesh Profile 4.3.2.50 */
> + case OP_MODEL_APP_LIST:
> + bt_shell_printf("\nModel App Key list for node %4.4x "
> + "length: %u status: %s\n", src, len,
> + data[0] == MESH_STATUS_SUCCESS ? "Success" :
> + mesh_status_str(data[0]));
> +
> + if (data[0] != MESH_STATUS_SUCCESS)
> + return true;
> +
> + bt_shell_printf("Element Addr:\t%4.4x\n", get_le16(data + 1));
> + bt_shell_printf("Model ID:\t%4.4x\n", get_le16(data + 3));
> +
> + for (i = 5; i < len; i += 2)
> + bt_shell_printf("Model App Key:\t%4.4x\n",
> + get_le16(data + i));
> + break;
> +
> + /* Per Mesh Profile 4.3.2.63 */
> + case OP_CONFIG_HEARTBEAT_PUB_STATUS:
> + bt_shell_printf("\nSet heartbeat for node %4.4x status: %s\n",
> + src,
> + data[0] == MESH_STATUS_SUCCESS ? "Success" :
> + mesh_status_str(data[0]));
> +
> + if (data[0] != MESH_STATUS_SUCCESS)
> + return true;
> +
> + bt_shell_printf("Destination:\t%4.4x\n", get_le16(data + 1));
> + bt_shell_printf("Count:\t\t%2.2x\n", data[3]);
> + bt_shell_printf("Period:\t\t%2.2x\n", data[4]);
> + bt_shell_printf("TTL:\t\t%2.2x\n", data[5]);
> + bt_shell_printf("Features:\t%4.4x\n", get_le16(data + 6));
> + bt_shell_printf("Net_Idx:\t%4.4x\n", get_le16(data + 8));
> + break;
> }
> +
> return true;
> }
>
> @@ -287,6 +387,23 @@ static bool config_send(uint8_t *buf, uint16_t len)
>
> }
>
> +static void cmd_default(uint32_t opcode)
> +{
> + uint16_t n;
> + uint8_t msg[32];
> +
> + if (IS_UNASSIGNED(target)) {
> + bt_shell_printf("Destination not set\n");
> + return;
> + }
> +
> + n = mesh_opcode_set(opcode, msg);
> +
> + if (!config_send(msg, n))
> + bt_shell_printf("Failed to send command (opcode 0x%x)\n",
> + opcode);
> +}
> +
> static void cmd_get_composition(int argc, char *argv[])
> {
> uint16_t n;
> @@ -518,6 +635,113 @@ static void cmd_bind(int argc, char *argv[])
> bt_shell_printf("Failed to send \"MODEL APP BIND\"\n");
> }
>
> +static void cmd_set_ident(int argc, char *argv[])
> +{
> + uint16_t n;
> + uint8_t msg[2 + 3 + 4];
> + int parm_cnt;
> +
> + if (!verify_config_target(target))
> + return;
> +
> + n = mesh_opcode_set(OP_NODE_IDENTITY_SET, msg);
> +
> + parm_cnt = read_input_parameters(argc, argv);
> + if (parm_cnt != 2) {
> + bt_shell_printf("bad arguments\n");
> + return;
> + }
> +
> + put_le16(parms[0], msg + n);
> + n += 2;
> + msg[n++] = parms[1];
> +
> + if (!config_send(msg, n))
> + bt_shell_printf("Failed to send \"SET IDENTITY\"\n");
> +}
> +
> +static void cmd_get_ident(int argc, char *argv[])
> +{
> + uint16_t n;
> + uint8_t msg[2 + 2 + 4];
> + int parm_cnt;
> +
> + if (!verify_config_target(target))
> + return;
> +
> + n = mesh_opcode_set(OP_NODE_IDENTITY_GET, msg);
> +
> + parm_cnt = read_input_parameters(argc, argv);
> + if (parm_cnt != 1) {
> + bt_shell_printf("bad arguments\n");
> + return;
> + }
> +
> + put_le16(parms[0], msg + n);
> + n += 2;
> +
> + if (!config_send(msg, n))
> + bt_shell_printf("Failed to send \"GET IDENTITY\"\n");
> +}
> +
> +static void cmd_set_proxy(int argc, char *argv[])
> +{
> + uint16_t n;
> + uint8_t msg[2 + 1 + 4];
> + int parm_cnt;
> +
> + if (!verify_config_target(target))
> + return;
> +
> + n = mesh_opcode_set(OP_CONFIG_PROXY_SET, msg);
> +
> + parm_cnt = read_input_parameters(argc, argv);
> + if (parm_cnt != 1) {
> + bt_shell_printf("bad arguments");
> + return;
> + }
> +
> + msg[n++] = parms[0];
> + msg[n++] = parms[1];
> +
> + if (!config_send(msg, n))
> + bt_shell_printf("Failed to send \"SET PROXY\"\n");
> +}
> +
> +static void cmd_get_proxy(int argc, char *argv[])
> +{
> + cmd_default(OP_CONFIG_PROXY_GET);
> +}
> +
> +static void cmd_set_relay(int argc, char *argv[])
> +{
> + uint16_t n;
> + uint8_t msg[2 + 2 + 4];
> + int parm_cnt;
> +
> + if (!verify_config_target(target))
> + return;
> +
> + n = mesh_opcode_set(OP_CONFIG_RELAY_SET, msg);
> +
> + parm_cnt = read_input_parameters(argc, argv);
> + if (parm_cnt != 3) {
> + bt_shell_printf("bad arguments\n");
> + return;
> + }
> +
> + msg[n++] = parms[0];
> + msg[n++] = (parms[1] << 5) | parms[2];
> +
> + if (!config_send(msg, n))
> + bt_shell_printf("Failed to send \"SET RELAY\"\n");
> +}
> +
> +static void cmd_get_relay(int argc, char *argv[])
> +{
> + cmd_default(OP_CONFIG_RELAY_GET);
> +}
> +
> static void cmd_set_ttl(int argc, char *argv[])
> {
> uint16_t n;
> @@ -556,7 +780,7 @@ static void cmd_set_pub(int argc, char *argv[])
> n = mesh_opcode_set(OP_CONFIG_MODEL_PUB_SET, msg);
>
> parm_cnt = read_input_parameters(argc, argv);
> - if (parm_cnt != 5) {
> + if (parm_cnt != 6) {
> bt_shell_printf("Bad arguments\n");
> return;
> }
> @@ -574,14 +798,14 @@ static void cmd_set_pub(int argc, char *argv[])
> /* Publish period step count and step resolution */
> msg[n++] = parms[3];
> /* Publish retransmit count & interval steps */
> - msg[n++] = (1 << 5) + 2;
> + msg[n++] = parms[4];
> /* Model Id */
> - if (parms[4] > 0xffff) {
> - put_le16(parms[4] >> 16, msg + n);
> - put_le16(parms[4], msg + n + 2);
> + if (parms[5] > 0xffff) {
> + put_le16(parms[5] >> 16, msg + n);
> + put_le16(parms[5], msg + n + 2);
> n += 4;
> } else {
> - put_le16(parms[4], msg + n);
> + put_le16(parms[5], msg + n);
> n += 2;
> }
>
> @@ -589,21 +813,176 @@ static void cmd_set_pub(int argc, char *argv[])
> bt_shell_printf("Failed to send \"SET MODEL PUBLICATION\"\n");
> }
>
> -static void cmd_default(uint32_t opcode)
> +static void cmd_get_pub(int argc, char *argv[])
> {
> uint16_t n;
> uint8_t msg[32];
> + int parm_cnt;
>
> if (IS_UNASSIGNED(target)) {
> bt_shell_printf("Destination not set\n");
> return;
> }
>
> - n = mesh_opcode_set(opcode, msg);
> + n = mesh_opcode_set(OP_CONFIG_MODEL_PUB_GET, msg);
> +
> + parm_cnt = read_input_parameters(argc, argv);
> + if (parm_cnt != 2) {
> + bt_shell_printf("Bad arguments: %s\n", argv[1]);
> + return;
> + }
> +
> + /* Element Address */
> + put_le16(parms[0], msg + n);
> + n += 2;
> + /* Model Id */
> + if (parms[1] > 0xffff) {
> + put_le16(parms[1] >> 16, msg + n);
> + put_le16(parms[1], msg + n + 2);
> + n += 4;
> + } else {
> + put_le16(parms[1], msg + n);
> + n += 2;
> + }
>
> if (!config_send(msg, n))
> - bt_shell_printf("Failed to send command (opcode 0x%x)\n",
> - opcode);
> + bt_shell_printf("Failed to send \"GET MODEL PUBLICATION\"\n");
> +}
> +
> +static void cmd_sub_add(int argc, char *argv[])
> +{
> + uint16_t n;
> + uint8_t msg[32];
> + int parm_cnt;
> +
> + if (IS_UNASSIGNED(target)) {
> + bt_shell_printf("Destination not set\n");
> + return;
> + }
> +
> + n = mesh_opcode_set(OP_CONFIG_MODEL_SUB_ADD, msg);
> +
> + parm_cnt = read_input_parameters(argc, argv);
> + if (parm_cnt != 3) {
> + bt_shell_printf("Bad arguments: %s\n", argv[1]);
> + return;
> + }
> +
> + /* Per Mesh Profile 4.3.2.19 */
> + /* Element Address */
> + put_le16(parms[0], msg + n);
> + n += 2;
> + /* Subscription Address */
> + put_le16(parms[1], msg + n);
> + n += 2;
> + /* SIG Model ID */
> + put_le16(parms[2], msg + n);
> + n += 2;
> +
> + if (!config_send(msg, n))
> + bt_shell_printf("Failed to send \"ADD SUBSCRIPTION\"\n");
> +}
> +
> +static void cmd_sub_get(int argc, char *argv[])
> +{
> + uint16_t n;
> + uint8_t msg[32];
> + int parm_cnt;
> +
> + if (IS_UNASSIGNED(target)) {
> + bt_shell_printf("Destination not set\n");
> + return;
> + }
> +
> + n = mesh_opcode_set(OP_CONFIG_MODEL_SUB_GET, msg);
> +
> + parm_cnt = read_input_parameters(argc, argv);
> + if (parm_cnt != 2) {
> + bt_shell_printf("Bad arguments: %s\n", argv[1]);
> + return;
> + }
> +
> + /* Per Mesh Profile 4.3.2.27 */
> + /* Element Address */
> + put_le16(parms[0], msg + n);
> + n += 2;
> + /* Model ID */
> + put_le16(parms[1], msg + n);
> + n += 2;
> +
> + if (!config_send(msg, n))
> + bt_shell_printf("Failed to send \"GET SUB GET\"\n");
> +}
> +
> +static void cmd_get_app(int argc, char *argv[])
> +{
> + uint16_t n;
> + uint8_t msg[32];
> + int parm_cnt;
> +
> + if (IS_UNASSIGNED(target)) {
> + bt_shell_printf("Destination not set\n");
> + return;
> + }
> +
> + n = mesh_opcode_set(OP_MODEL_APP_GET, msg);
> +
> + parm_cnt = read_input_parameters(argc, argv);
> + if (parm_cnt != 2) {
> + bt_shell_printf("Bad arguments: %s\n", argv[1]);
> + return;
> + }
> +
> + /* Per Mesh Profile 4.3.2.49 */
> + /* Element Address */
> + put_le16(parms[0], msg + n);
> + n += 2;
> + /* Model ID */
> + put_le16(parms[1], msg + n);
> + n += 2;
> +
> + if (!config_send(msg, n))
> + bt_shell_printf("Failed to send \"GET APP GET\"\n");
> +}
> +
> +static void cmd_set_hb(int argc, char *argv[])
> +{
> + uint16_t n;
> + uint8_t msg[32];
> + int parm_cnt;
> +
> + if (IS_UNASSIGNED(target)) {
> + bt_shell_printf("Destination not set\n");
> + return;
> + }
> +
> + n = mesh_opcode_set(OP_CONFIG_HEARTBEAT_PUB_SET, msg);
> +
> + parm_cnt = read_input_parameters(argc, argv);
> + if (parm_cnt != 5) {
> + bt_shell_printf("Bad arguments: %s\n", argv[1]);
> + return;
> + }
> +
> + /* Per Mesh Profile 4.3.2.62 */
> + /* Publish address */
> + put_le16(parms[0], msg + n);
> + n += 2;
> + /* Count Log */
> + msg[n++] = parms[1];
> + /* Period Log */
> + msg[n++] = parms[2];
> + /* Heartbeat TTL */
> + msg[n++] = DEFAULT_TTL;
> + /* Features */
> + put_le16(parms[3], msg + n);
> + n += 2;
> + /* NetKey Index */
> + put_le16(parms[4], msg + n);
> + n += 2;
> +
> + if (!config_send(msg, n))
> + bt_shell_printf("Failed to send \"SET HEARTBEAT PUBLICATION\"\n");
> }
>
> static void cmd_get_ttl(int argc, char *argv[])
> @@ -614,27 +993,52 @@ static void cmd_get_ttl(int argc, char *argv[])
> static const struct bt_shell_menu cfg_menu = {
> .name = "config",
> .entries = {
> - {"target", "<unicast>", cmd_set_node,
> + {"target", "<unicast>", cmd_set_node,
> "Set target node to configure"},
> {"get-composition", "[<page_num>]", cmd_get_composition,
> "Get Composition Data"},
> - {"add-netkey", "<net_idx>", cmd_add_net_key,
> + {"add-netkey", "<net_idx>", cmd_add_net_key,
> "Add network key"},
> - {"del-netkey", "<net_idx>", cmd_del_net_key,
> + {"del-netkey", "<net_idx>", cmd_del_net_key,
> "Delete network key"},
> - {"add-appkey", "<app_idx>", cmd_add_app_key,
> + {"add-appkey", "<app_idx>", cmd_add_app_key,
> "Add application key"},
> - {"del-appkey", "<app_idx>", cmd_del_app_key,
> + {"del-appkey", "<app_idx>", cmd_del_app_key,
> "Delete application key"},
> {"bind", "<ele_idx> <app_idx> <mod_id> [cid]",
> cmd_bind, "Bind app key to a model"},
> - {"set-ttl", "<ttl>", cmd_set_ttl,
> + {"set-ttl", "<ttl>", cmd_set_ttl,
> "Set default TTL"},
> + {"set-proxy", "<proxy>", cmd_set_proxy,
> + "Set proxy state"},
> + {"get-proxy", NULL, cmd_get_proxy,
> + "Get proxy state"},
> + {"set-ident", "<net_idx> <state>", cmd_set_ident,
> + "Set node identity state"},
> + {"get-ident", "<net_idx>", cmd_get_ident,
> + "Get node identity state"},
> + {"set-relay", "<relay> <rexmt count> <rexmt steps>",
> + cmd_set_relay,
> + "Set relay"},
> + {"get-relay", NULL, cmd_get_relay,
> + "Get relay"},
> {"get-ttl", NULL, cmd_get_ttl,
> "Get default TTL"},
> {"set-pub", "<ele_addr> <pub_addr> <app_idx> "
> - "<period (step|res)> <model>",
> + "<period (step|res)> <re-xmt (count|per)> <model>",
> cmd_set_pub, "Set publication"},
> + {"get-pub", "<ele_addr> <model>", cmd_get_pub,
> + "Get publication"},
> + {"set-hb", "<pub_addr> <count> <period> <features> <net_idx>",
> + cmd_set_hb, "Set heartbeat"},
> + {"add-sub", "<ele_addr> <sub_addr> <model id>",
> + cmd_sub_add, "Subscription add"},
> + {"get-sub", "<ele_addr> <model id>",
> + cmd_sub_get, "Subscription get"},
> +
> + {"get-app", "<ele_addr> <model id>",
> + cmd_get_app, "Get App Keys"},
> +
> {} },
> };
>
> diff --git a/mesh/net.c b/mesh/net.c
> index 421dc6955..20dfcb8a8 100644
> --- a/mesh/net.c
> +++ b/mesh/net.c
> @@ -1399,6 +1399,24 @@ static bool ctl_rxed(uint16_t net_idx, uint32_t iv_index,
> uint8_t *trans, uint16_t len)
> {
> /* TODO: Handle control messages */
> +
> + /* Per Mesh Profile 3.6.5.10 */
> + if (trans[0] == NET_OP_HEARTBEAT) {
> + uint16_t feat = get_be16(trans + 2);
> +
> + bt_shell_printf("HEARTBEAT src: %4.4x dst: %4.4x \
> + TTL: %2.2x feat: %s%s%s%s\n",
> + src, dst, trans[1],
> + (feat & MESH_FEATURE_RELAY) ? "relay " : "",
> + (feat & MESH_FEATURE_PROXY) ? "proxy " : "",
> + (feat & MESH_FEATURE_FRIEND) ? "friend " : "",
> + (feat & MESH_FEATURE_LPN) ? "lpn" : "");
> + return true;
> + }
> +
> + bt_shell_printf("unrecognized control message src:%4.4x dst:%4.4x len:%d\n",
> + src, dst, len);
> + print_byte_array("msg: ", trans, len);
> return false;
> }
>
> @@ -2098,7 +2116,7 @@ bool net_access_layer_send(uint8_t ttl, uint16_t src, uint32_t dst,
> if (!result)
> return false;
>
> - segN = SEG_MAX(len + sizeof(uint32_t));
> + segN = SEG_MAX(len + sizeof(mic32));
>
> /* Only one ACK required SAR message per destination at a time */
> if (segN && IS_UNICAST(dst)) {
> --
> 2.11.0
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-bluetooth" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html



--
Luiz Augusto von Dentz

2017-12-11 14:58:39

by Steve Brown

[permalink] [raw]
Subject: [PATCH 3/3] mesh: meshctl: Add support for subscriptions in node and database

From: Steve Brown <[email protected]>

This populates the GList in the mesh_models structure and
updates the json database.

Add a function to return the device UUID.

Add device UUID to database for possible future restoring
of a nodes configuration.
---
mesh/config-client.c | 27 ++++++++++++++++-----
mesh/node.c | 32 +++++++++++++++++++++++++
mesh/node.h | 3 +++
mesh/prov-db.c | 66 ++++++++++++++++++++++++++++++++++++++++++++++++++++
mesh/prov-db.h | 2 ++
5 files changed, 124 insertions(+), 6 deletions(-)

diff --git a/mesh/config-client.c b/mesh/config-client.c
index 35ccbff45..b48fe6a9e 100644
--- a/mesh/config-client.c
+++ b/mesh/config-client.c
@@ -255,13 +255,28 @@ static bool client_msg_recvd(uint16_t src, uint8_t *data,
if (data[0] != MESH_STATUS_SUCCESS)
return true;

- bt_shell_printf("Element Addr:\t%4.4x\n", get_le16(data + 1));
- bt_shell_printf("Subscr Addr:\t%4.4x\n", get_le16(data + 3));
- bt_shell_printf("Model ID:\t%4.4x\n", get_le16(data + 5));
- break;
+ ele_addr = get_le16(data + 1);
+ addr = get_le16(data + 3);
+ ele_idx = ele_addr - node_get_primary(node);

- /* TODO */
- /* Save subscription info in database */
+ if (len == 7) {
+ mod_id = get_le16(data + 5);
+ bt_shell_printf("ModelId %4.4x\n", mod_id);
+ mod_id = 0xffff0000 | mod_id;
+ } else {
+ mod_id = get_le16(data + 7);
+ bt_shell_printf("ModelId %4.4x %4.4x\n", get_le16(data + 5),
+ mod_id);
+ mod_id = get_le16(data + 5) << 16 | mod_id;
+ }
+
+ bt_shell_printf("Element Addr:\t%4.4x\n", ele_addr);
+ bt_shell_printf("Subscr Addr:\t%4.4x\n", addr);
+
+ /* Save subscriptions in node and database */
+ node_add_subscription(node, ele_idx, mod_id, addr);
+ prov_db_add_subscription(node, ele_idx, mod_id, addr);
+ break;

/* Per Mesh Profile 4.3.2.27 */
case OP_CONFIG_MODEL_SUB_LIST:
diff --git a/mesh/node.c b/mesh/node.c
index b682a35f7..40b353fa3 100644
--- a/mesh/node.c
+++ b/mesh/node.c
@@ -349,6 +349,14 @@ uint8_t *node_get_device_key(struct mesh_node *node)
return node->dev_key;
}

+uint8_t *node_get_device_uuid(struct mesh_node *node)
+{
+ if (!node)
+ return NULL;
+ else
+ return node->dev_uuid;
+}
+
void node_set_num_elements(struct mesh_node *node, uint8_t num_ele)
{
node->num_ele = num_ele;
@@ -758,6 +766,7 @@ bool node_add_binding(struct mesh_node *node, uint8_t ele_idx,
GList *l;

model = get_model(node, ele_idx, model_id);
+
if(!model)
return false;

@@ -776,6 +785,29 @@ bool node_add_binding(struct mesh_node *node, uint8_t ele_idx,
return true;
}

+bool node_add_subscription(struct mesh_node *node, uint8_t ele_idx,
+ uint32_t model_id, uint16_t addr)
+{
+ struct mesh_model *model;
+ GList *l;
+
+ model = get_model(node, ele_idx, model_id);
+
+ if(!model)
+ return false;
+
+ l = g_list_find(model->subscriptions, GUINT_TO_POINTER(addr));
+
+ if (l)
+ return false;
+
+ model->subscriptions = g_list_append(model->subscriptions,
+
+ GUINT_TO_POINTER(addr));
+
+ return true;
+}
+
uint8_t node_get_default_ttl(struct mesh_node *node)
{
if (!node)
diff --git a/mesh/node.h b/mesh/node.h
index 1fab80a13..69b3bdf94 100644
--- a/mesh/node.h
+++ b/mesh/node.h
@@ -86,6 +86,7 @@ uint16_t node_get_primary(struct mesh_node *node);
uint16_t node_get_primary_net_idx(struct mesh_node *node);
void node_set_device_key(struct mesh_node *node, uint8_t *key);
uint8_t *node_get_device_key(struct mesh_node *node);
+uint8_t *node_get_device_uuid(struct mesh_node *node);
void node_set_num_elements(struct mesh_node *node, uint8_t num_ele);
uint8_t node_get_num_elements(struct mesh_node *node);
bool node_parse_composition(struct mesh_node *node, uint8_t *buf, uint16_t len);
@@ -111,6 +112,8 @@ bool node_set_composition(struct mesh_node *node,
struct mesh_node_composition *comp);
bool node_add_binding(struct mesh_node *node, uint8_t ele_idx,
uint32_t model_id, uint16_t app_idx);
+bool node_add_subscription(struct mesh_node *node, uint8_t ele_idx,
+ uint32_t model_id, uint16_t addr);
uint8_t node_get_default_ttl(struct mesh_node *node);
bool node_set_default_ttl(struct mesh_node *node, uint8_t ttl);
bool node_set_sequence_number(struct mesh_node *node, uint32_t seq);
diff --git a/mesh/prov-db.c b/mesh/prov-db.c
index 04803a5c8..ea0d45af0 100644
--- a/mesh/prov-db.c
+++ b/mesh/prov-db.c
@@ -371,6 +371,34 @@ static bool parse_bindings(struct mesh_node *node, int ele_idx,
return true;
}

+static bool parse_subscriptions(struct mesh_node *node, int ele_idx,
+ uint32_t model_id, json_object *jsubscriptions)
+
+{
+ int cnt;
+ int i;
+
+ cnt = json_object_array_length(jsubscriptions);
+
+ for (i = 0; i < cnt; ++i) {
+ int key_idx;
+ json_object *jvalue;
+
+ jvalue = json_object_array_get_idx(jsubscriptions, i);
+ if (!jvalue)
+ return true;
+
+ key_idx = json_object_get_int(jvalue);
+ if (!CHECK_KEY_IDX_RANGE(key_idx))
+ return false;
+
+ if (!node_add_subscription(node, ele_idx, model_id, key_idx))
+ return false;
+ }
+
+ return true;
+}
+
static bool parse_configuration_models(struct mesh_node *node, int ele_idx,
json_object *jmodels, uint32_t target_id, json_object **jtarget)
{
@@ -411,9 +439,15 @@ static bool parse_configuration_models(struct mesh_node *node, int ele_idx,
}

json_object_object_get_ex(jmodel, "bind", &jarray);
+
if (jarray && !parse_bindings(node, ele_idx, model_id, jarray))
return false;

+ json_object_object_get_ex(jmodel, "subscription", &jarray);
+
+ if (jarray && !parse_subscriptions(node, ele_idx, model_id, jarray))
+ return false;
+
json_object_object_get_ex(jmodel, "publish", &jvalue);

if (jvalue && !parse_model_pub(node, ele_idx, model_id, jvalue))
@@ -1068,6 +1102,35 @@ bool prov_db_add_binding(struct mesh_node *node, uint8_t ele_idx,
return true;
}

+bool prov_db_add_subscription(struct mesh_node *node, uint8_t ele_idx,
+ uint32_t model_id, uint16_t addr)
+{
+ json_object *jmain;
+ json_object *jmodel;
+ json_object *jsubscriptions = NULL;
+ bool local = (node == node_get_local_node());
+
+ jmodel = get_jmodel_obj(node, ele_idx, model_id, &jmain);
+
+ if (!jmodel)
+ return false;
+
+ json_object_object_get_ex(jmodel, "subscription", &jsubscriptions);
+
+ if (!jsubscriptions) {
+ jsubscriptions = json_object_new_array();
+ json_object_object_add(jmodel, "subscription", jsubscriptions);
+ }
+
+ put_uint16_array_entry(jsubscriptions, addr);
+
+ prov_file_write(jmain, local);
+
+ json_object_put(jmain);
+
+ return true;
+}
+
bool prov_db_node_set_model_pub(struct mesh_node *node, uint8_t ele_idx,
uint32_t model_id,
struct mesh_publication *pub)
@@ -1139,6 +1202,9 @@ bool prov_db_add_new_node(struct mesh_node *node)
/* Device key */
add_key(jnode, "deviceKey", node_get_device_key(node));

+ /* Device key */
+ add_key(jnode, "deviceUUID", node_get_device_uuid(node));
+
/* Net key */
jconfig = json_object_new_object();
add_node_idxs(jconfig, "netKeys", node_get_net_keys(node));
diff --git a/mesh/prov-db.h b/mesh/prov-db.h
index b1e4c629c..a49b45cbd 100644
--- a/mesh/prov-db.h
+++ b/mesh/prov-db.h
@@ -30,6 +30,8 @@ bool prov_db_add_node_composition(struct mesh_node *node, uint8_t *data,
bool prov_db_node_keys(struct mesh_node *node, GList *idxs, const char *desc);
bool prov_db_add_binding(struct mesh_node *node, uint8_t ele_idx,
uint32_t model_id, uint16_t app_idx);
+bool prov_db_add_subscription(struct mesh_node *node, uint8_t ele_idx,
+ uint32_t model_id, uint16_t addr);
bool prov_db_node_set_ttl(struct mesh_node *node, uint8_t ttl);
bool prov_db_node_set_iv_seq(struct mesh_node *node, uint32_t iv, uint32_t seq);
bool prov_db_local_set_iv_index(uint32_t iv_index, bool update, bool prov);
--
2.11.0


2017-12-11 14:58:37

by Steve Brown

[permalink] [raw]
Subject: [PATCH 1/3] mesh: Segmentation fails in gatt.c:pipe_write()

From: Steve Brown <[email protected]>

If the first command output in a new connection exceeds 20 bytes,
mesh_gatt_write sets the SAR to FIRST as the write_mtu is initially 0
and the default is GATT_MTU-3 (20).

When pipe_write gets called, a new larger write_mtu has been set, but
the SAR is still set to FIRST. It's assumed that data->gatt_len >
max_len. However, it's not which causes lots of bogus output.

---

At Luiz' suggestion.

1. The WriteValue code has been removed
2. The SAR logic has been moved to pipe_write

It was tested against the zephyr mesh_shell and a small mtu to test the
segmentation logic.
---
mesh/gatt.c | 141 ++++++++----------------------------------------------------
1 file changed, 18 insertions(+), 123 deletions(-)

diff --git a/mesh/gatt.c b/mesh/gatt.c
index 197291e67..9116a9de1 100644
--- a/mesh/gatt.c
+++ b/mesh/gatt.c
@@ -102,27 +102,6 @@ static void write_data_free(void *user_data)
free(data);
}

-static void write_setup(DBusMessageIter *iter, void *user_data)
-{
- struct write_data *data = user_data;
- struct iovec *iov = &data->iov;
- DBusMessageIter array, dict;
-
- dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "y", &array);
- dbus_message_iter_append_fixed_array(&array, DBUS_TYPE_BYTE,
- &iov->iov_base, iov->iov_len);
- dbus_message_iter_close_container(iter, &array);
-
- dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
- DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
- DBUS_TYPE_STRING_AS_STRING
- DBUS_TYPE_VARIANT_AS_STRING
- DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
- &dict);
-
- dbus_message_iter_close_container(iter, &dict);
-}
-
uint16_t mesh_gatt_sar(uint8_t **pkt, uint16_t size)
{
const uint8_t *data = *pkt;
@@ -192,11 +171,12 @@ static bool pipe_write(struct io *io, void *user_data)
struct write_data *data = user_data;
struct iovec iov[2];
uint8_t sar;
- uint8_t max_len = write_mtu - 4;
+ uint8_t max_len;

if (data == NULL)
return true;

+ max_len = write_mtu ? write_mtu - 3 - 1 : GATT_MTU - 3 - 1;
print_byte_array("GATT-TX:\t", data->gatt_data, data->gatt_len);

sar = data->gatt_data[0];
@@ -204,6 +184,14 @@ static bool pipe_write(struct io *io, void *user_data)
data->iov.iov_base = data->gatt_data + 1;
data->iov.iov_len--;

+ sar = data->gatt_data[0] & GATT_TYPE_MASK;
+ data->gatt_len--;
+
+ if (data->gatt_len > max_len) {
+ sar |= GATT_SAR_FIRST;
+ data->iov.iov_len = max_len;
+ }
+
iov[0].iov_base = &sar;
iov[0].iov_len = sizeof(sar);

@@ -245,73 +233,6 @@ static bool pipe_write(struct io *io, void *user_data)
}
}

-static void write_reply(DBusMessage *message, void *user_data)
-{
- struct write_data *data = user_data;
- struct write_data *tmp;
- uint8_t *dptr = data->gatt_data;
- uint8_t max_len = GATT_MTU - 3;
- uint8_t max_seg = GATT_MTU - 4;
- DBusError error;
-
- dbus_error_init(&error);
-
- if (dbus_set_error_from_message(&error, message) == TRUE) {
- bt_shell_printf("Failed to write: %s\n", error.name);
- dbus_error_free(&error);
- return;
- }
-
- if (data == NULL)
- return;
-
- switch (data->gatt_data[0] & GATT_SAR_MASK) {
- case GATT_SAR_FIRST:
- case GATT_SAR_CONTINUE:
- tmp = g_new0(struct write_data, 1);
- if (!data)
- return;
-
- *tmp = *data;
- tmp->gatt_data = g_malloc(data->gatt_len);
-
- if (!tmp->gatt_data) {
- g_free(tmp);
- return;
- }
-
- tmp->gatt_data[0] = dptr[0];
- data = tmp;
- memcpy(data->gatt_data + 1, dptr + max_len,
- data->gatt_len - max_seg);
- data->gatt_len -= max_seg;
- data->gatt_data[0] &= GATT_TYPE_MASK;
- data->iov.iov_base = data->gatt_data;
- if (max_len < data->gatt_len) {
- data->iov.iov_len = max_len;
- data->gatt_data[0] |= GATT_SAR_CONTINUE;
- } else {
- data->iov.iov_len = data->gatt_len;
- data->gatt_data[0] |= GATT_SAR_LAST;
- }
-
- break;
-
- default:
- if(data->cb)
- data->cb(message, data->user_data);
- return;
- }
-
- if (g_dbus_proxy_method_call(data->proxy, "WriteValue", write_setup,
- write_reply, data, write_data_free) == FALSE) {
- bt_shell_printf("Failed to write\n");
- write_data_free(data);
- return;
- }
-
-}
-
static void write_io_destroy(void)
{
io_destroy(write_io);
@@ -361,12 +282,8 @@ static void acquire_write_reply(DBusMessage *message, void *user_data)

if (dbus_set_error_from_message(&error, message) == TRUE) {
dbus_error_free(&error);
- if (g_dbus_proxy_method_call(data->proxy, "WriteValue",
- write_setup, write_reply, data,
- write_data_free) == FALSE) {
- bt_shell_printf("Failed to write\n");
- write_data_free(data);
- }
+ bt_shell_printf("Failed to write\n");
+ write_data_free(data);
return;
}

@@ -402,8 +319,6 @@ bool mesh_gatt_write(GDBusProxy *proxy, uint8_t *buf, uint16_t len,
GDBusReturnFunction cb, void *user_data)
{
struct write_data *data;
- DBusMessageIter iter;
- uint8_t max_len;

if (!buf || !len)
return false;
@@ -415,17 +330,11 @@ bool mesh_gatt_write(GDBusProxy *proxy, uint8_t *buf, uint16_t len,
if (!data)
return false;

- max_len = write_mtu ? write_mtu - 3 : GATT_MTU - 3;
-
/* TODO: should keep in queue in case we need to cancel write? */

data->gatt_len = len;
data->gatt_data = g_memdup(buf, len);
data->gatt_data[0] &= GATT_TYPE_MASK;
- if (max_len < len) {
- len = max_len;
- data->gatt_data[0] |= GATT_SAR_FIRST;
- }
data->iov.iov_base = data->gatt_data;
data->iov.iov_len = len;
data->proxy = proxy;
@@ -435,27 +344,13 @@ bool mesh_gatt_write(GDBusProxy *proxy, uint8_t *buf, uint16_t len,
if (write_io)
return pipe_write(write_io, data);

- if (g_dbus_proxy_get_property(proxy, "WriteAcquired", &iter)) {
- if (g_dbus_proxy_method_call(proxy, "AcquireWrite",
- acquire_setup, acquire_write_reply,
- data, NULL) == FALSE) {
- bt_shell_printf("Failed to AcquireWrite\n");
- write_data_free(data);
- return false;
- }
- } else {
- if (g_dbus_proxy_method_call(data->proxy, "WriteValue",
- write_setup, write_reply, data,
- write_data_free) == FALSE) {
- bt_shell_printf("Failed to write\n");
- write_data_free(data);
- return false;
- }
- print_byte_array("GATT-TX: ", buf, len);
- bt_shell_printf("Attempting to write %s\n",
- g_dbus_proxy_get_path(proxy));
+ if (g_dbus_proxy_method_call(proxy, "AcquireWrite",
+ acquire_setup, acquire_write_reply,
+ data, NULL) == FALSE) {
+ bt_shell_printf("Failed to AcquireWrite\n");
+ write_data_free(data);
+ return false;
}
-
return true;
}

--
2.11.0


2017-12-11 14:58:38

by Steve Brown

[permalink] [raw]
Subject: [PATCH 2/3] mesh: meshctl: Add commands

From: Steve Brown <[email protected]>

Get/Set Proxy
Get/Set Ident
Get/Set Relay
Set Heartbeat
Get Publication
Get/Set Subscription
The json database is not updated (WIP)
Get App
---
mesh/config-client.c | 446 ++++++++++++++++++++++++++++++++++++++++++++++++---
mesh/net.c | 20 ++-
2 files changed, 444 insertions(+), 22 deletions(-)

diff --git a/mesh/config-client.c b/mesh/config-client.c
index 3d618b6a6..35ccbff45 100644
--- a/mesh/config-client.c
+++ b/mesh/config-client.c
@@ -61,6 +61,7 @@ static bool client_msg_recvd(uint16_t src, uint8_t *data,
uint8_t ele_idx;
struct mesh_publication pub;
int n;
+ uint16_t i;

if (mesh_opcode_get(data, len, &opcode, &n)) {
len -= n;
@@ -158,6 +159,29 @@ static bool client_msg_recvd(uint16_t src, uint8_t *data,
prov_db_add_binding(node, addr - src, mod_id, app_idx);
break;

+ case OP_NODE_IDENTITY_STATUS:
+ if (len != 4)
+ return true;
+ bt_shell_printf("Network index 0x%04x has "
+ "Node Identity state 0x%02x %s\n",
+ get_le16(data + 1), data[3],
+ mesh_status_str(data[0]));
+
+ case OP_CONFIG_RELAY_STATUS:
+ if (len != 2)
+ return true;
+ bt_shell_printf("Node %4.4x Relay state: 0x%02x"
+ " count: %d steps: %d\n",
+ src, data[0], data[1]>>5, data[1] & 0x1f);
+ break;
+
+ case OP_CONFIG_PROXY_STATUS:
+ if (len != 1)
+ return true;
+ bt_shell_printf("Node %4.4x Proxy state: 0x%02x\n",
+ src, data[0]);
+ break;
+
case OP_CONFIG_DEFAULT_TTL_STATUS:
if (len != 1)
return true;
@@ -170,9 +194,9 @@ static bool client_msg_recvd(uint16_t src, uint8_t *data,
if (len != 12 && len != 14)
return true;

- bt_shell_printf("\nSet publication for node %4.4x status: %s\n", src,
- data[0] == MESH_STATUS_SUCCESS ? "Success" :
- mesh_status_str(data[0]));
+ bt_shell_printf("\nSet publication for node %4.4x status: %s\n",
+ src, data[0] == MESH_STATUS_SUCCESS ?
+ "Success" : mesh_status_str(data[0]));

if (data[0] != MESH_STATUS_SUCCESS)
return true;
@@ -189,6 +213,7 @@ static bool client_msg_recvd(uint16_t src, uint8_t *data,
pub.ttl = data[7];
pub.period = data[8];
n = (data[8] & 0x3f);
+ bt_shell_printf("Publication address: 0x%04x\n", pub.u.addr16);
switch (data[8] >> 6) {
case 0:
bt_shell_printf("Period: %d ms\n", n * 100);
@@ -206,7 +231,8 @@ static bool client_msg_recvd(uint16_t src, uint8_t *data,

pub.retransmit = data[9];
bt_shell_printf("Retransmit count: %d\n", data[9] >> 5);
- bt_shell_printf("Retransmit Interval Steps: %d\n", data[9] & 0x1f);
+ bt_shell_printf("Retransmit Interval Steps: %d\n",
+ data[9] & 0x1f);

ele_idx = ele_addr - node_get_primary(node);

@@ -218,7 +244,81 @@ static bool client_msg_recvd(uint16_t src, uint8_t *data,
prov_db_node_set_model_pub(node, ele_idx, mod_id,
node_model_pub_get(node, ele_idx, mod_id));
break;
+
+ /* Per Mesh Profile 4.3.2.19 */
+ case OP_CONFIG_MODEL_SUB_STATUS:
+ bt_shell_printf("\nSubscription changed"
+ " for node %4.4x status: %s\n", src,
+ data[0] == MESH_STATUS_SUCCESS ? "Success" :
+ mesh_status_str(data[0]));
+
+ if (data[0] != MESH_STATUS_SUCCESS)
+ return true;
+
+ bt_shell_printf("Element Addr:\t%4.4x\n", get_le16(data + 1));
+ bt_shell_printf("Subscr Addr:\t%4.4x\n", get_le16(data + 3));
+ bt_shell_printf("Model ID:\t%4.4x\n", get_le16(data + 5));
+ break;
+
+ /* TODO */
+ /* Save subscription info in database */
+
+ /* Per Mesh Profile 4.3.2.27 */
+ case OP_CONFIG_MODEL_SUB_LIST:
+
+ bt_shell_printf("\nSubscription list for node %4.4x "
+ "length: %u status: %s\n", src, len,
+ data[0] == MESH_STATUS_SUCCESS ? "Success" :
+ mesh_status_str(data[0]));
+
+ if (data[0] != MESH_STATUS_SUCCESS)
+ return true;
+
+ bt_shell_printf("Element Addr:\t%4.4x\n", get_le16(data + 1));
+ bt_shell_printf("Model ID:\t%4.4x\n", get_le16(data + 3));
+
+ for (i = 5; i < len; i += 2)
+ bt_shell_printf("Subscr Addr:\t%4.4x\n",
+ get_le16(data + i));
+ break;
+
+ /* Per Mesh Profile 4.3.2.50 */
+ case OP_MODEL_APP_LIST:
+ bt_shell_printf("\nModel App Key list for node %4.4x "
+ "length: %u status: %s\n", src, len,
+ data[0] == MESH_STATUS_SUCCESS ? "Success" :
+ mesh_status_str(data[0]));
+
+ if (data[0] != MESH_STATUS_SUCCESS)
+ return true;
+
+ bt_shell_printf("Element Addr:\t%4.4x\n", get_le16(data + 1));
+ bt_shell_printf("Model ID:\t%4.4x\n", get_le16(data + 3));
+
+ for (i = 5; i < len; i += 2)
+ bt_shell_printf("Model App Key:\t%4.4x\n",
+ get_le16(data + i));
+ break;
+
+ /* Per Mesh Profile 4.3.2.63 */
+ case OP_CONFIG_HEARTBEAT_PUB_STATUS:
+ bt_shell_printf("\nSet heartbeat for node %4.4x status: %s\n",
+ src,
+ data[0] == MESH_STATUS_SUCCESS ? "Success" :
+ mesh_status_str(data[0]));
+
+ if (data[0] != MESH_STATUS_SUCCESS)
+ return true;
+
+ bt_shell_printf("Destination:\t%4.4x\n", get_le16(data + 1));
+ bt_shell_printf("Count:\t\t%2.2x\n", data[3]);
+ bt_shell_printf("Period:\t\t%2.2x\n", data[4]);
+ bt_shell_printf("TTL:\t\t%2.2x\n", data[5]);
+ bt_shell_printf("Features:\t%4.4x\n", get_le16(data + 6));
+ bt_shell_printf("Net_Idx:\t%4.4x\n", get_le16(data + 8));
+ break;
}
+
return true;
}

@@ -287,6 +387,23 @@ static bool config_send(uint8_t *buf, uint16_t len)

}

+static void cmd_default(uint32_t opcode)
+{
+ uint16_t n;
+ uint8_t msg[32];
+
+ if (IS_UNASSIGNED(target)) {
+ bt_shell_printf("Destination not set\n");
+ return;
+ }
+
+ n = mesh_opcode_set(opcode, msg);
+
+ if (!config_send(msg, n))
+ bt_shell_printf("Failed to send command (opcode 0x%x)\n",
+ opcode);
+}
+
static void cmd_get_composition(int argc, char *argv[])
{
uint16_t n;
@@ -518,6 +635,113 @@ static void cmd_bind(int argc, char *argv[])
bt_shell_printf("Failed to send \"MODEL APP BIND\"\n");
}

+static void cmd_set_ident(int argc, char *argv[])
+{
+ uint16_t n;
+ uint8_t msg[2 + 3 + 4];
+ int parm_cnt;
+
+ if (!verify_config_target(target))
+ return;
+
+ n = mesh_opcode_set(OP_NODE_IDENTITY_SET, msg);
+
+ parm_cnt = read_input_parameters(argc, argv);
+ if (parm_cnt != 2) {
+ bt_shell_printf("bad arguments\n");
+ return;
+ }
+
+ put_le16(parms[0], msg + n);
+ n += 2;
+ msg[n++] = parms[1];
+
+ if (!config_send(msg, n))
+ bt_shell_printf("Failed to send \"SET IDENTITY\"\n");
+}
+
+static void cmd_get_ident(int argc, char *argv[])
+{
+ uint16_t n;
+ uint8_t msg[2 + 2 + 4];
+ int parm_cnt;
+
+ if (!verify_config_target(target))
+ return;
+
+ n = mesh_opcode_set(OP_NODE_IDENTITY_GET, msg);
+
+ parm_cnt = read_input_parameters(argc, argv);
+ if (parm_cnt != 1) {
+ bt_shell_printf("bad arguments\n");
+ return;
+ }
+
+ put_le16(parms[0], msg + n);
+ n += 2;
+
+ if (!config_send(msg, n))
+ bt_shell_printf("Failed to send \"GET IDENTITY\"\n");
+}
+
+static void cmd_set_proxy(int argc, char *argv[])
+{
+ uint16_t n;
+ uint8_t msg[2 + 1 + 4];
+ int parm_cnt;
+
+ if (!verify_config_target(target))
+ return;
+
+ n = mesh_opcode_set(OP_CONFIG_PROXY_SET, msg);
+
+ parm_cnt = read_input_parameters(argc, argv);
+ if (parm_cnt != 1) {
+ bt_shell_printf("bad arguments");
+ return;
+ }
+
+ msg[n++] = parms[0];
+ msg[n++] = parms[1];
+
+ if (!config_send(msg, n))
+ bt_shell_printf("Failed to send \"SET PROXY\"\n");
+}
+
+static void cmd_get_proxy(int argc, char *argv[])
+{
+ cmd_default(OP_CONFIG_PROXY_GET);
+}
+
+static void cmd_set_relay(int argc, char *argv[])
+{
+ uint16_t n;
+ uint8_t msg[2 + 2 + 4];
+ int parm_cnt;
+
+ if (!verify_config_target(target))
+ return;
+
+ n = mesh_opcode_set(OP_CONFIG_RELAY_SET, msg);
+
+ parm_cnt = read_input_parameters(argc, argv);
+ if (parm_cnt != 3) {
+ bt_shell_printf("bad arguments\n");
+ return;
+ }
+
+ msg[n++] = parms[0];
+ msg[n++] = (parms[1] << 5) | parms[2];
+
+ if (!config_send(msg, n))
+ bt_shell_printf("Failed to send \"SET RELAY\"\n");
+}
+
+static void cmd_get_relay(int argc, char *argv[])
+{
+ cmd_default(OP_CONFIG_RELAY_GET);
+}
+
static void cmd_set_ttl(int argc, char *argv[])
{
uint16_t n;
@@ -556,7 +780,7 @@ static void cmd_set_pub(int argc, char *argv[])
n = mesh_opcode_set(OP_CONFIG_MODEL_PUB_SET, msg);

parm_cnt = read_input_parameters(argc, argv);
- if (parm_cnt != 5) {
+ if (parm_cnt != 6) {
bt_shell_printf("Bad arguments\n");
return;
}
@@ -574,14 +798,14 @@ static void cmd_set_pub(int argc, char *argv[])
/* Publish period step count and step resolution */
msg[n++] = parms[3];
/* Publish retransmit count & interval steps */
- msg[n++] = (1 << 5) + 2;
+ msg[n++] = parms[4];
/* Model Id */
- if (parms[4] > 0xffff) {
- put_le16(parms[4] >> 16, msg + n);
- put_le16(parms[4], msg + n + 2);
+ if (parms[5] > 0xffff) {
+ put_le16(parms[5] >> 16, msg + n);
+ put_le16(parms[5], msg + n + 2);
n += 4;
} else {
- put_le16(parms[4], msg + n);
+ put_le16(parms[5], msg + n);
n += 2;
}

@@ -589,21 +813,176 @@ static void cmd_set_pub(int argc, char *argv[])
bt_shell_printf("Failed to send \"SET MODEL PUBLICATION\"\n");
}

-static void cmd_default(uint32_t opcode)
+static void cmd_get_pub(int argc, char *argv[])
{
uint16_t n;
uint8_t msg[32];
+ int parm_cnt;

if (IS_UNASSIGNED(target)) {
bt_shell_printf("Destination not set\n");
return;
}

- n = mesh_opcode_set(opcode, msg);
+ n = mesh_opcode_set(OP_CONFIG_MODEL_PUB_GET, msg);
+
+ parm_cnt = read_input_parameters(argc, argv);
+ if (parm_cnt != 2) {
+ bt_shell_printf("Bad arguments: %s\n", argv[1]);
+ return;
+ }
+
+ /* Element Address */
+ put_le16(parms[0], msg + n);
+ n += 2;
+ /* Model Id */
+ if (parms[1] > 0xffff) {
+ put_le16(parms[1] >> 16, msg + n);
+ put_le16(parms[1], msg + n + 2);
+ n += 4;
+ } else {
+ put_le16(parms[1], msg + n);
+ n += 2;
+ }

if (!config_send(msg, n))
- bt_shell_printf("Failed to send command (opcode 0x%x)\n",
- opcode);
+ bt_shell_printf("Failed to send \"GET MODEL PUBLICATION\"\n");
+}
+
+static void cmd_sub_add(int argc, char *argv[])
+{
+ uint16_t n;
+ uint8_t msg[32];
+ int parm_cnt;
+
+ if (IS_UNASSIGNED(target)) {
+ bt_shell_printf("Destination not set\n");
+ return;
+ }
+
+ n = mesh_opcode_set(OP_CONFIG_MODEL_SUB_ADD, msg);
+
+ parm_cnt = read_input_parameters(argc, argv);
+ if (parm_cnt != 3) {
+ bt_shell_printf("Bad arguments: %s\n", argv[1]);
+ return;
+ }
+
+ /* Per Mesh Profile 4.3.2.19 */
+ /* Element Address */
+ put_le16(parms[0], msg + n);
+ n += 2;
+ /* Subscription Address */
+ put_le16(parms[1], msg + n);
+ n += 2;
+ /* SIG Model ID */
+ put_le16(parms[2], msg + n);
+ n += 2;
+
+ if (!config_send(msg, n))
+ bt_shell_printf("Failed to send \"ADD SUBSCRIPTION\"\n");
+}
+
+static void cmd_sub_get(int argc, char *argv[])
+{
+ uint16_t n;
+ uint8_t msg[32];
+ int parm_cnt;
+
+ if (IS_UNASSIGNED(target)) {
+ bt_shell_printf("Destination not set\n");
+ return;
+ }
+
+ n = mesh_opcode_set(OP_CONFIG_MODEL_SUB_GET, msg);
+
+ parm_cnt = read_input_parameters(argc, argv);
+ if (parm_cnt != 2) {
+ bt_shell_printf("Bad arguments: %s\n", argv[1]);
+ return;
+ }
+
+ /* Per Mesh Profile 4.3.2.27 */
+ /* Element Address */
+ put_le16(parms[0], msg + n);
+ n += 2;
+ /* Model ID */
+ put_le16(parms[1], msg + n);
+ n += 2;
+
+ if (!config_send(msg, n))
+ bt_shell_printf("Failed to send \"GET SUB GET\"\n");
+}
+
+static void cmd_get_app(int argc, char *argv[])
+{
+ uint16_t n;
+ uint8_t msg[32];
+ int parm_cnt;
+
+ if (IS_UNASSIGNED(target)) {
+ bt_shell_printf("Destination not set\n");
+ return;
+ }
+
+ n = mesh_opcode_set(OP_MODEL_APP_GET, msg);
+
+ parm_cnt = read_input_parameters(argc, argv);
+ if (parm_cnt != 2) {
+ bt_shell_printf("Bad arguments: %s\n", argv[1]);
+ return;
+ }
+
+ /* Per Mesh Profile 4.3.2.49 */
+ /* Element Address */
+ put_le16(parms[0], msg + n);
+ n += 2;
+ /* Model ID */
+ put_le16(parms[1], msg + n);
+ n += 2;
+
+ if (!config_send(msg, n))
+ bt_shell_printf("Failed to send \"GET APP GET\"\n");
+}
+
+static void cmd_set_hb(int argc, char *argv[])
+{
+ uint16_t n;
+ uint8_t msg[32];
+ int parm_cnt;
+
+ if (IS_UNASSIGNED(target)) {
+ bt_shell_printf("Destination not set\n");
+ return;
+ }
+
+ n = mesh_opcode_set(OP_CONFIG_HEARTBEAT_PUB_SET, msg);
+
+ parm_cnt = read_input_parameters(argc, argv);
+ if (parm_cnt != 5) {
+ bt_shell_printf("Bad arguments: %s\n", argv[1]);
+ return;
+ }
+
+ /* Per Mesh Profile 4.3.2.62 */
+ /* Publish address */
+ put_le16(parms[0], msg + n);
+ n += 2;
+ /* Count Log */
+ msg[n++] = parms[1];
+ /* Period Log */
+ msg[n++] = parms[2];
+ /* Heartbeat TTL */
+ msg[n++] = DEFAULT_TTL;
+ /* Features */
+ put_le16(parms[3], msg + n);
+ n += 2;
+ /* NetKey Index */
+ put_le16(parms[4], msg + n);
+ n += 2;
+
+ if (!config_send(msg, n))
+ bt_shell_printf("Failed to send \"SET HEARTBEAT PUBLICATION\"\n");
}

static void cmd_get_ttl(int argc, char *argv[])
@@ -614,27 +993,52 @@ static void cmd_get_ttl(int argc, char *argv[])
static const struct bt_shell_menu cfg_menu = {
.name = "config",
.entries = {
- {"target", "<unicast>", cmd_set_node,
+ {"target", "<unicast>", cmd_set_node,
"Set target node to configure"},
{"get-composition", "[<page_num>]", cmd_get_composition,
"Get Composition Data"},
- {"add-netkey", "<net_idx>", cmd_add_net_key,
+ {"add-netkey", "<net_idx>", cmd_add_net_key,
"Add network key"},
- {"del-netkey", "<net_idx>", cmd_del_net_key,
+ {"del-netkey", "<net_idx>", cmd_del_net_key,
"Delete network key"},
- {"add-appkey", "<app_idx>", cmd_add_app_key,
+ {"add-appkey", "<app_idx>", cmd_add_app_key,
"Add application key"},
- {"del-appkey", "<app_idx>", cmd_del_app_key,
+ {"del-appkey", "<app_idx>", cmd_del_app_key,
"Delete application key"},
{"bind", "<ele_idx> <app_idx> <mod_id> [cid]",
cmd_bind, "Bind app key to a model"},
- {"set-ttl", "<ttl>", cmd_set_ttl,
+ {"set-ttl", "<ttl>", cmd_set_ttl,
"Set default TTL"},
+ {"set-proxy", "<proxy>", cmd_set_proxy,
+ "Set proxy state"},
+ {"get-proxy", NULL, cmd_get_proxy,
+ "Get proxy state"},
+ {"set-ident", "<net_idx> <state>", cmd_set_ident,
+ "Set node identity state"},
+ {"get-ident", "<net_idx>", cmd_get_ident,
+ "Get node identity state"},
+ {"set-relay", "<relay> <rexmt count> <rexmt steps>",
+ cmd_set_relay,
+ "Set relay"},
+ {"get-relay", NULL, cmd_get_relay,
+ "Get relay"},
{"get-ttl", NULL, cmd_get_ttl,
"Get default TTL"},
{"set-pub", "<ele_addr> <pub_addr> <app_idx> "
- "<period (step|res)> <model>",
+ "<period (step|res)> <re-xmt (count|per)> <model>",
cmd_set_pub, "Set publication"},
+ {"get-pub", "<ele_addr> <model>", cmd_get_pub,
+ "Get publication"},
+ {"set-hb", "<pub_addr> <count> <period> <features> <net_idx>",
+ cmd_set_hb, "Set heartbeat"},
+ {"add-sub", "<ele_addr> <sub_addr> <model id>",
+ cmd_sub_add, "Subscription add"},
+ {"get-sub", "<ele_addr> <model id>",
+ cmd_sub_get, "Subscription get"},
+
+ {"get-app", "<ele_addr> <model id>",
+ cmd_get_app, "Get App Keys"},
+
{} },
};

diff --git a/mesh/net.c b/mesh/net.c
index 421dc6955..20dfcb8a8 100644
--- a/mesh/net.c
+++ b/mesh/net.c
@@ -1399,6 +1399,24 @@ static bool ctl_rxed(uint16_t net_idx, uint32_t iv_index,
uint8_t *trans, uint16_t len)
{
/* TODO: Handle control messages */
+
+ /* Per Mesh Profile 3.6.5.10 */
+ if (trans[0] == NET_OP_HEARTBEAT) {
+ uint16_t feat = get_be16(trans + 2);
+
+ bt_shell_printf("HEARTBEAT src: %4.4x dst: %4.4x \
+ TTL: %2.2x feat: %s%s%s%s\n",
+ src, dst, trans[1],
+ (feat & MESH_FEATURE_RELAY) ? "relay " : "",
+ (feat & MESH_FEATURE_PROXY) ? "proxy " : "",
+ (feat & MESH_FEATURE_FRIEND) ? "friend " : "",
+ (feat & MESH_FEATURE_LPN) ? "lpn" : "");
+ return true;
+ }
+
+ bt_shell_printf("unrecognized control message src:%4.4x dst:%4.4x len:%d\n",
+ src, dst, len);
+ print_byte_array("msg: ", trans, len);
return false;
}

@@ -2098,7 +2116,7 @@ bool net_access_layer_send(uint8_t ttl, uint16_t src, uint32_t dst,
if (!result)
return false;

- segN = SEG_MAX(len + sizeof(uint32_t));
+ segN = SEG_MAX(len + sizeof(mic32));

/* Only one ACK required SAR message per destination at a time */
if (segN && IS_UNICAST(dst)) {
--
2.11.0