diff --git a/examples/asymcute_mqttsn/main.c b/examples/asymcute_mqttsn/main.c index 4f3c6b0dbf..ec897a0372 100644 --- a/examples/asymcute_mqttsn/main.c +++ b/examples/asymcute_mqttsn/main.c @@ -89,51 +89,37 @@ static asymcute_sub_t *_find_sub(const char *name) return NULL; } -static uint16_t _topic_parse_pre(const char *name) +static uint16_t _parse_predef_id(const char *name) { - if (strncmp(name, "pre_", 4) == 0) { - return (uint16_t)atoi(&name[4]); + uint16_t id = 0; + if ((strlen(name) > 4) && (strncmp(name, "pre_", 4) == 0)) { + id = (uint16_t)atoi(&name[4]); } - return 0; + return id; } -static int _topic_init(asymcute_topic_t *t, const char *name) +static asymcute_topic_t *_topic_init(asymcute_topic_t *t, const char *name) { - uint16_t id = _topic_parse_pre(name); - + uint16_t id = _parse_predef_id(name); if (id != 0) { name = NULL; } if (asymcute_topic_init(t, name, id) != ASYMCUTE_OK) { - return 1; + return NULL; } - return 0; + return t; } - -static int _topic_find(asymcute_topic_t *t, const char *name) +static asymcute_topic_t *_topic_find(const char *name) { - size_t len = strlen(name); - uint16_t id = _topic_parse_pre(name); - - if ((id != 0) || (len == 2)) { - if (t) { - return _topic_init(t, name); - } - return 0; - } - - /* need to find topic in list of registered ones */ + uint16_t id = _parse_predef_id(name); for (unsigned i = 0; i < TOPIC_BUF_NUMOF; i++) { - if (asymcute_topic_is_reg(&_topics[i]) && + if ((asymcute_topic_is_predef(&_topics[i]) && (id == _topics[i].id)) || (strncmp(name, _topics[i].name, sizeof(_topics[i].name)) == 0)) { - if (t) { - *t = _topics[i]; - } - return 0; + return &_topics[i]; } } - return 1; + return NULL; } static void _topics_clear(void) @@ -151,6 +137,18 @@ static asymcute_topic_t *_topic_get_free(void) return NULL; } +static void _topic_print_info(const asymcute_topic_t *topic) +{ + printf(" id: %u, name: %s", (unsigned)topic->id, topic->name); + if (asymcute_topic_is_short(topic)) { + printf(" (SHORT)"); + } + else if (asymcute_topic_is_predef(topic)) { + printf(" (PREDEF)"); + } + puts(""); +} + static void _topic_print_help(void) { puts(" topic can be\n" @@ -300,34 +298,35 @@ static int _cmd_reg(int argc, char **argv) return 1; } - if (_topic_find(NULL, argv[1]) == 0) { - puts("success: topic already registered (or no registration needed)\n"); + asymcute_topic_t *t = _topic_find(argv[1]); + if ((t != NULL) && (asymcute_topic_is_reg(t))) { + puts("success: topic already registered"); return 0; } - /* find unused slot */ - asymcute_topic_t *t = NULL; - for (unsigned i = 0; i < TOPIC_BUF_NUMOF; i++) { - if (!asymcute_topic_is_reg(&_topics[i])) { - t = &_topics[i]; - break; - } - } - if (t == NULL) { - puts("error: no empty slot left for storing the topic\n"); + /* get registration request context */ + asymcute_req_t *req = _get_req_ctx(); + if (req == NULL) { + puts("error: unable to allocate request context"); return 1; } - /* send registration request */ - asymcute_req_t *req = _get_req_ctx(); - if (req == NULL) { - return 1; + /* find unused slot */ + if (t == NULL) { + t = _topic_get_free(); + if (t == NULL) { + puts("error: no empty slot left for storing the topic\n"); + return 1; + } + if (_topic_init(t, argv[1]) == NULL) { + puts("error: unable to initialize topic"); + return 1; + } } - if (_topic_init(t, argv[1]) != 0) { - puts("error: unable to initialize topic"); - return 1; - } - if (asymcute_register(&_connection, req, t) != ASYMCUTE_OK) { + + int res = asymcute_register(&_connection, req, t); + if (res != ASYMCUTE_OK) { + printf("res: %i\n", res); puts("error: unable to send REGISTER request\n"); return 1; } @@ -341,21 +340,18 @@ static int _cmd_unreg(int argc, char **argv) return 1; } - unsigned i = 0; - for (; i < TOPIC_BUF_NUMOF; i++) { - if (strcmp(argv[1], _topics[i].name) == 0) { - for (unsigned s = 0; s < SUB_CTX_NUMOF; s++) { - if (_subscriptions[i].topic == &_topics[i]) { - puts("error: topic used in active subscription"); - return 1; - } + asymcute_topic_t *t = _topic_find(argv[1]); + if (t) { + for (unsigned s = 0; s < SUB_CTX_NUMOF; s++) { + if (_subscriptions[s].topic == t) { + puts("error: topic used in active subscription"); + return 1; } - memset(&_topics[i], 0, sizeof(asymcute_topic_t)); - puts("success: removed local entry for given topic"); - break; } + asymcute_topic_reset(t); + puts("success: removed local entry for given topic"); } - if (i == TOPIC_BUF_NUMOF) { + else { puts("error: unable to find topic in list of registered topics"); } @@ -370,13 +366,6 @@ static int _cmd_pub(int argc, char **argv) return 1; } - /* parse and register topic */ - asymcute_topic_t t; - if (_topic_find(&t, argv[1]) != 0) { - puts("error: given topic is not registered"); - return 1; - } - /* parse QoS level */ unsigned flags = 0; int qos = _qos_parse(argc, argv, 3, &flags); @@ -388,12 +377,20 @@ static int _cmd_pub(int argc, char **argv) /* get request context */ asymcute_req_t *req = _get_req_ctx(); if (req == NULL) { + puts("error: unable to obtain request context"); + return 1; + } + + /* get topic */ + asymcute_topic_t *t = _topic_find(argv[1]); + if (t == NULL) { + puts("error: given topic is not registered"); return 1; } /* publish data */ size_t len = strlen(argv[2]); - if (asymcute_publish(&_connection, req, &t, argv[2], len, flags) != + if (asymcute_publish(&_connection, req, t, argv[2], len, flags) != ASYMCUTE_OK) { puts("error: unable to send PUBLISH message"); return 1; @@ -424,6 +421,7 @@ static int _cmd_sub(int argc, char **argv) /* get request context */ asymcute_req_t *req = _get_req_ctx(); if (req == NULL) { + puts("error: unable to obtain request context"); return 1; } @@ -434,23 +432,39 @@ static int _cmd_sub(int argc, char **argv) return 1; } - /* parse topic */ - asymcute_topic_t *t = _topic_get_free(); + /* parse topic, see if it exists, otherwise take empty topic */ + asymcute_topic_t *t = _topic_find(argv[1]); if (t == NULL) { - puts("error: no free topic memory"); - return 1; + puts("info: given topic does not exist, creating it now"); + t = _topic_get_free(); + if (t == NULL) { + puts("error: no free topic memory"); + return 1; + } + if (_topic_init(t, argv[1]) == 0) { + puts("error: unable to initialize topic"); + return 1; + } } - if (_topic_init(t, argv[1]) != 0) { - puts("error: unable to initialize topic"); - return 1; + else { + if (!asymcute_topic_is_reg(t)) { + puts("error: given topic is not registered\n"); + return 1; + } } - printf("using req %p, sub %p\n", (void *)req, (void *)sub); - - if (asymcute_subscribe(&_connection, req, sub, t, _on_pub_evt, NULL, flags) - != ASYMCUTE_OK) { - asymcute_topic_reset(t); - puts("error: unable to send SUBSCRIBE request"); + int res = asymcute_subscribe(&_connection, req, sub, t, _on_pub_evt, + NULL, flags); + if (res != ASYMCUTE_OK) { + if (!asymcute_topic_is_reg(t)) { + asymcute_topic_reset(t); + } + if (res == ASYMCUTE_SUBERR) { + puts("error: already subscribed to given topic"); + } + else { + puts("error: unable to send SUBSCRIBE request"); + } return 1; } @@ -496,8 +510,12 @@ static int _cmd_info(int argc, char **argv) for (unsigned i = 0; i < TOPIC_BUF_NUMOF; i++) { printf("topic #%2u - ", i); if (asymcute_topic_is_reg(&_topics[i])) { - printf("[registered] id: %u, name: %s\n", - (unsigned)_topics[i].id, _topics[i].name); + printf("[registered] "); + _topic_print_info(&_topics[i]); + } + else if (asymcute_topic_is_init(&_topics[i])) { + printf("[initialized] "); + _topic_print_info(&_topics[i]); } else { puts("[unused]"); @@ -508,9 +526,8 @@ static int _cmd_info(int argc, char **argv) for (unsigned i = 0; i < SUB_CTX_NUMOF; i++) { printf("sub #%2u - ", i); if (asymcute_sub_active(&_subscriptions[i])) { - printf("[subscribed] id: %u, name: %s\n", - (unsigned)_subscriptions[i].topic->id, - _subscriptions[i].topic->name); + printf("[subscribed]"); + _topic_print_info(_subscriptions[i].topic); } else { puts("[unused]"); diff --git a/sys/include/net/asymcute.h b/sys/include/net/asymcute.h index 75d4ded323..d8939737db 100644 --- a/sys/include/net/asymcute.h +++ b/sys/include/net/asymcute.h @@ -394,6 +394,34 @@ static inline bool asymcute_topic_is_reg(const asymcute_topic_t *topic) return (topic->con != NULL); } +/** + * @brief Check if a given topic is a short topic + * + * @param[in] topic topic to check + * + * @return true if topic is a short topic + * @return false if topic is not short topic + */ +static inline bool asymcute_topic_is_short(const asymcute_topic_t *topic) +{ + assert(topic); + return ((topic->flags & MQTTSN_TIT_SHORT) != 0); +} + +/** + * @brief Check if a given topic is a pre-defined topic + * + * @param[in] topic topic to check + * + * @return true if topic is pre-defined + * @return false if topic is not pre-defined + */ +static inline bool asymcute_topic_is_predef(const asymcute_topic_t *topic) +{ + assert(topic); + return ((topic->flags & MQTTSN_TIT_PREDEF) != 0); +} + /** * @brief Check if a given topic is initialized * diff --git a/sys/net/application_layer/asymcute/asymcute.c b/sys/net/application_layer/asymcute/asymcute.c index 62622007e4..0a9b0dad45 100644 --- a/sys/net/application_layer/asymcute/asymcute.c +++ b/sys/net/application_layer/asymcute/asymcute.c @@ -165,7 +165,12 @@ static void _compile_sub_unsub(asymcute_req_t *req, asymcute_con_t *con, req->data[pos] = type; req->data[pos + 1] = sub->topic->flags; byteorder_htobebufs(&req->data[pos + 2], req->msg_id); - memcpy(&req->data[pos + 4], sub->topic->name, topic_len); + if (sub->topic->flags & MQTTSN_TIT_MASK) { + memcpy(&req->data[pos + 4], &sub->topic->id, 2); + } + else { + memcpy(&req->data[pos + 4], sub->topic->name, topic_len); + } req->data_len = (pos + 4 + topic_len); req->arg = sub; } @@ -474,20 +479,26 @@ static void _on_suback(asymcute_con_t *con, const uint8_t *data, size_t len) } unsigned ret = ASYMCUTE_REJECTED; - if (data[7] == MQTTSN_ACCEPTED) { - /* parse and apply assigned topic id */ - asymcute_sub_t *sub = req->arg; - if (sub == NULL) { - return; - } + /* parse and apply assigned topic id */ + asymcute_sub_t *sub = req->arg; + if (sub == NULL) { + return; + } - sub->topic->id = byteorder_bebuftohs(&data[3]); + if (data[7] == MQTTSN_ACCEPTED) { + /* do not assign a topic ID for short and predefined topics */ + if (!(sub->topic->flags & MQTTSN_TIT_MASK)) { + sub->topic->id = byteorder_bebuftohs(&data[3]); + } sub->topic->con = con; /* insert subscription to connection context */ sub->next = con->subscriptions; con->subscriptions = sub; ret = ASYMCUTE_SUBSCRIBED; } + else { + sub->topic = NULL; + } /* notify the user */ mutex_unlock(&req->lock); @@ -668,12 +679,12 @@ int asymcute_topic_init(asymcute_topic_t *topic, const char *topic_name, { assert(topic); + size_t len = 0; if (asymcute_topic_is_reg(topic)) { return ASYMCUTE_REGERR; } - if (topic_name == NULL) { if ((topic_id == 0) || (topic_id == UINT16_MAX)) { return ASYMCUTE_OVERFLOW; @@ -692,8 +703,7 @@ int asymcute_topic_init(asymcute_topic_t *topic, const char *topic_name, if (topic_name == NULL) { topic->id = topic_id; topic->flags = MQTTSN_TIT_PREDEF; - memcpy(topic->name, &topic_id, 2); - topic->name[2] = '\0'; + memcpy(topic->name, "..\0", 3); } else { strncpy(topic->name, topic_name, sizeof(topic->name)); @@ -823,6 +833,14 @@ int asymcute_register(asymcute_con_t *con, asymcute_req_t *req, goto end; } + /* if we have a short or predefined topic, there is no need to send a + * registration message. We assign the connection right away */ + if (topic->flags & MQTTSN_TIT_MASK) { + topic->con = con; + mutex_unlock(&req->lock); + goto end; + } + /* prepare topic */ req->arg = topic; size_t topic_len = strlen(topic->name); @@ -936,11 +954,14 @@ int asymcute_subscribe(asymcute_con_t *con, asymcute_req_t *req, ret = ASYMCUTE_GWERR; goto end; } - /* check if we are already subscribed to the given topic */ - for (asymcute_sub_t *sub = con->subscriptions; sub; sub = sub->next) { - if (asymcute_topic_equal(topic, sub->topic)) { - ret = ASYMCUTE_SUBERR; - goto end; + /* check if we are already subscribed to the given topic, but only if the + * topic was already registered */ + if (asymcute_topic_is_reg(topic)) { + for (asymcute_sub_t *sub = con->subscriptions; sub; sub = sub->next) { + if (asymcute_topic_equal(topic, sub->topic)) { + ret = ASYMCUTE_SUBERR; + goto end; + } } } /* make sure request context is clear to be used */ @@ -953,6 +974,7 @@ int asymcute_subscribe(asymcute_con_t *con, asymcute_req_t *req, sub->cb = callback; sub->arg = arg; sub->topic = topic; + topic->flags |= flags; /* send SUBSCRIBE message */ _compile_sub_unsub(req, con, sub, MQTTSN_SUBSCRIBE);