Esempio n. 1
0
static int
gatt_svr_chr_access_sec_test(uint16_t conn_handle, uint16_t attr_handle,
                             struct ble_gatt_access_ctxt *ctxt,
                             void *arg)
{
    const void *uuid128;
    int rand_num;
    int rc;

    uuid128 = ctxt->chr->uuid128;

    /* Determine which characteristic is being accessed by examining its
     * 128-bit UUID.
     */

    if (memcmp(uuid128, gatt_svr_chr_sec_test_rand_uuid, 16) == 0) {
        assert(ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR);

        /* Respond with a 32-bit random number. */
        rand_num = rand();
        rc = os_mbuf_append(ctxt->om, &rand_num, sizeof rand_num);
        return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
    }

    if (memcmp(uuid128, gatt_svr_chr_sec_test_static_uuid, 16) == 0) {
        switch (ctxt->op) {
        case BLE_GATT_ACCESS_OP_READ_CHR:
            rc = os_mbuf_append(ctxt->om, &gatt_svr_sec_test_static_val,
                                sizeof gatt_svr_sec_test_static_val);
            return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;

        case BLE_GATT_ACCESS_OP_WRITE_CHR:
            rc = gatt_svr_chr_write(ctxt->om,
                                    sizeof gatt_svr_sec_test_static_val,
                                    sizeof gatt_svr_sec_test_static_val,
                                    &gatt_svr_sec_test_static_val, NULL);
            return rc;

        default:
            assert(0);
            return BLE_ATT_ERR_UNLIKELY;
        }
    }

    /* Unknown characteristic; the nimble stack should not have called this
     * function.
     */
    assert(0);
    return BLE_ATT_ERR_UNLIKELY;
}
Esempio n. 2
0
static int
ble_svc_gap_access(uint16_t conn_handle, uint16_t attr_handle,
                   struct ble_gatt_access_ctxt *ctxt, void *arg)
{
    uint16_t uuid16;
    int rc;

    uuid16 = ble_uuid_128_to_16(ctxt->chr->uuid128);
    assert(uuid16 != 0);

    switch (uuid16) {
    case BLE_SVC_GAP_CHR_UUID16_DEVICE_NAME:
        assert(ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR);
        rc = os_mbuf_append(ctxt->om, ble_svc_gap_name,
                            strlen(ble_svc_gap_name));
        return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;

    case BLE_SVC_GAP_CHR_UUID16_APPEARANCE:
        assert(ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR);
        rc = os_mbuf_append(ctxt->om, &ble_svc_gap_appearance,
                            sizeof ble_svc_gap_appearance);
        return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;

    case BLE_SVC_GAP_CHR_UUID16_PERIPH_PRIV_FLAG:
        assert(ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR);
        rc = os_mbuf_append(ctxt->om, &ble_svc_gap_privacy_flag,
                            sizeof ble_svc_gap_privacy_flag);
        return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;

    case BLE_SVC_GAP_CHR_UUID16_RECONNECT_ADDR:
        assert(ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR);
        if (OS_MBUF_PKTLEN(ctxt->om) != sizeof ble_svc_gap_reconnect_addr) {
            return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
        }
        ble_hs_mbuf_to_flat(ctxt->om, ble_svc_gap_reconnect_addr,
                            sizeof ble_svc_gap_reconnect_addr, NULL);
        return 0;

    case BLE_SVC_GAP_CHR_UUID16_PERIPH_PREF_CONN_PARAMS:
        assert(ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR);
        rc = os_mbuf_append(ctxt->om, &ble_svc_gap_pref_conn_params,
                            sizeof ble_svc_gap_pref_conn_params);
        return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;

    default:
        assert(0);
        return BLE_ATT_ERR_UNLIKELY;
    }
}
Esempio n. 3
0
int
nm_rx_rsp(uint8_t *attr_val, uint16_t attr_len)
{
    struct os_mbuf *om;
    int rc;

    om = os_msys_get_pkthdr(attr_len, 0);
    if (om == NULL) {
        rc = 1;
        goto err;
    }

    rc = os_mbuf_append(om, attr_val, attr_len);
    if (rc != 0) {
        goto err;
    }

    bleshell_printf("received nmgr rsp: ");
    rc = shell_nlip_output(om);
    if (rc != 0) {
        goto err;
    }

    return 0;

err:
    os_mbuf_free_chain(om);
    return rc;
}
int
ble_hs_test_util_l2cap_rx_payload_flat(struct ble_hs_conn *conn,
                                       struct ble_l2cap_chan *chan,
                                       const void *data, int len)
{
    struct hci_data_hdr hci_hdr;
    struct os_mbuf *om;
    int rc;

    om = ble_hs_misc_pkthdr();
    TEST_ASSERT_FATAL(om != NULL);

    om->om_data += BLE_L2CAP_HDR_SZ;

    rc = os_mbuf_append(om, data, len);
    TEST_ASSERT_FATAL(rc == 0);

    om = ble_l2cap_prepend_hdr(om, chan->blc_cid, OS_MBUF_PKTLEN(om));
    TEST_ASSERT_FATAL(om != NULL);

    hci_hdr.hdh_handle_pb_bc =
        host_hci_handle_pb_bc_join(conn->bhc_handle,
                                   BLE_HCI_PB_FIRST_FLUSH, 0);
    hci_hdr.hdh_len = OS_MBUF_PKTHDR(om)->omp_len;

    rc = ble_hs_test_util_l2cap_rx(conn, &hci_hdr, om);
    return rc;
}
Esempio n. 5
0
/**
 * Simple read access callback for the tx power level
 * characteristic.
 */
static int
ble_svc_tps_access(uint16_t conn_handle, uint16_t attr_handle,
                   struct ble_gatt_access_ctxt *ctxt, void *arg)
{
    int rc;
    
    assert(ctxt->chr == &ble_svc_tps_defs[0].characteristics[0]);
    
    switch (ctxt->op) {
    case BLE_GATT_ACCESS_OP_READ_CHR:
        rc = ble_hs_hci_util_read_adv_tx_pwr(&ble_svc_tps_tx_power_level);
        if (rc != 0) {
            return BLE_ATT_ERR_UNLIKELY;
        }

        rc = os_mbuf_append(ctxt->om, &ble_svc_tps_tx_power_level,
                            sizeof ble_svc_tps_tx_power_level);
        return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;

    default:
        assert(0);
        return BLE_ATT_ERR_UNLIKELY;
    }

    return 0;
}
Esempio n. 6
0
/*
 * Receive a character from UART.
 */
static int
nmgr_uart_rx_char(void *arg, uint8_t data)
{
    struct nmgr_uart_state *nus = (struct nmgr_uart_state *)arg;
    struct os_mbuf *m;
    int rc;

    if (!nus->nus_rx) {
        m = os_msys_get_pkthdr(SHELL_NLIP_MAX_FRAME, 0);
        if (!m) {
            return 0;
        }
        nus->nus_rx = OS_MBUF_PKTHDR(m);
        if (OS_MBUF_TRAILINGSPACE(m) < SHELL_NLIP_MAX_FRAME) {
            /*
             * mbuf is too small.
             */
            os_mbuf_free_chain(m);
            nus->nus_rx = NULL;
            return 0;
        }
    }

    m = OS_MBUF_PKTHDR_TO_MBUF(nus->nus_rx);
    if (data == '\n') {
        /*
         * Full line of input. Process it outside interrupt context.
         */
        assert(!nus->nus_rx_q);
        nus->nus_rx_q = nus->nus_rx;
        nus->nus_rx = NULL;
        os_eventq_put(g_mgmt_evq, &nus->nus_cb_ev);
        return 0;
    } else {
        rc = os_mbuf_append(m, &data, 1);
        if (rc == 0) {
            return 0;
        }
    }
    /* failed */
    nus->nus_rx->omp_len = 0;
    m->om_len = 0;
    os_mbuf_free_chain(SLIST_NEXT(m, om_next));
    SLIST_NEXT(m, om_next) = NULL;
    return 0;
}
Esempio n. 7
0
static struct os_mbuf *
bletest_send_packet(uint16_t handle)
{
    int j;
    uint8_t val;
    struct os_mbuf *om;
    uint16_t pktlen;

    om = bletest_get_packet();
    if (om) {
        /* set payload length */
#if BLETEST_THROUGHPUT_TEST
        pktlen = BLETEST_PKT_SIZE;
#else
#if (BLETEST_CFG_RAND_PKT_SIZE == 1)
        pktlen = rand() % (BLETEST_MAX_PKT_SIZE + 1);
#else
        pktlen = BLETEST_PKT_SIZE;
#endif
#endif

        /* Put the HCI header in the mbuf */
        put_le16(om->om_data, handle);
        put_le16(om->om_data + 2, pktlen + 4);

        /* Place L2CAP header in packet */
        put_le16(om->om_data + 4, pktlen);
        om->om_data[6] = 0;
        om->om_data[7] = 0;
        om->om_len = 8;
        OS_MBUF_PKTHDR(om)->omp_len = 8;

        /* Fill with incrementing pattern (starting from 1) */
        for (j = 0; j < pktlen; ++j) {
            val = j + 1;
            os_mbuf_append(om, &val, 1);
        }

        /* Transmit it */
        ble_hci_trans_hs_acl_tx(om);
    }

    return om;
}
static int
ble_att_clt_append_blob(uint16_t conn_handle, struct os_mbuf *txom,
                        void *blob, int blob_len)
{
    int rc;

    if (blob_len < 0) {
        return BLE_HS_EINVAL;
    }
    if (blob_len == 0) {
        return 0;
    }

    rc = os_mbuf_append(txom, blob, blob_len);
    if (rc != 0) {
        return rc;
    }

    return 0;
}
Esempio n. 9
0
File: main.c Progetto: A-Paul/RIOT
static int gatt_svr_chr_access_device_info_manufacturer(
        uint16_t conn_handle, uint16_t attr_handle,
        struct ble_gatt_access_ctxt *ctxt, void *arg)
{
    puts("service 'device info: manufacturer' callback triggered");

    (void) conn_handle;
    (void) attr_handle;
    (void) arg;

    snprintf(str_answer, STR_ANSWER_BUFFER_SIZE,
             "This is RIOT! (Version: %s)\n", RIOT_VERSION);
    puts(str_answer);

    int rc = os_mbuf_append(ctxt->om, str_answer, strlen(str_answer));

    puts("");

    return rc;
}
static int
ble_gatts_read_test_util_access_1(uint16_t conn_handle,
                                  uint16_t attr_handle,
                                  struct ble_gatt_access_ctxt *ctxt,
                                  void *arg)
{
    int rc;

    TEST_ASSERT_FATAL(ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR);
    TEST_ASSERT_FATAL(attr_handle == ble_gatts_read_test_chr_1_val_handle);

    TEST_ASSERT(ctxt->chr ==
                &ble_gatts_read_test_svcs[0].characteristics[0]);

    rc = os_mbuf_append(ctxt->om, ble_gatts_read_test_chr_1_val,
                        ble_gatts_read_test_chr_1_len);
    TEST_ASSERT(rc == 0);

    return 0;
}
Esempio n. 11
0
int
nm_chr_access(uint16_t conn_handle, uint16_t attr_handle,
              uint8_t op, union ble_gatt_access_ctxt *ctxt,
              void *arg)
{
    struct nmgr_transport *nt;
    struct os_mbuf *om;
    int rc;

    assert(attr_handle == nm_attr_val_handle);

    om = NULL;

    if (op != BLE_GATT_ACCESS_OP_WRITE_CHR) {
        rc = BLE_ATT_ERR_WRITE_NOT_PERMITTED;
        goto err;
    }

    om = os_msys_get_pkthdr(ctxt->chr_access.len, 2);
    if (om == NULL) {
        rc = 1;
        goto err;
    }

    /* Put the connection handle in the request packet header. */
    memcpy(OS_MBUF_USRHDR(om), &conn_handle, sizeof conn_handle);

    rc = os_mbuf_append(om, ctxt->chr_access.data, ctxt->chr_access.len);
    if (rc != 0) {
        goto err;
    }

    nt = arg;
    rc = nmgr_rx_req(nt, om);

    return rc;

err:
    os_mbuf_free_chain(om);
    return rc;
}
Esempio n. 12
0
File: main.c Progetto: A-Paul/RIOT
static int gatt_svr_chr_access_device_info_model(
        uint16_t conn_handle, uint16_t attr_handle,
        struct ble_gatt_access_ctxt *ctxt, void *arg)
{
    puts("service 'device info: model' callback triggered");

    (void) conn_handle;
    (void) attr_handle;
    (void) arg;

    snprintf(str_answer, STR_ANSWER_BUFFER_SIZE,
             "You are running RIOT on a(n) %s board, "
             "which features a(n) %s MCU.", RIOT_BOARD, RIOT_MCU);
    puts(str_answer);

    int rc = os_mbuf_append(ctxt->om, str_answer, strlen(str_answer));

    puts("");

    return rc;
}
static int
ble_att_clt_build_find_type_value_req(struct ble_att_find_type_value_req *req,
                                      void *attribute_value, int value_len,
                                      struct os_mbuf **out_txom)
{
    int rc;

    rc = ble_att_clt_init_req(BLE_ATT_FIND_TYPE_VALUE_REQ_BASE_SZ, out_txom);
    if (rc != 0) {
        return rc;
    }

    ble_att_find_type_value_req_write((*out_txom)->om_data,
                                      (*out_txom)->om_len,
                                      req);
    rc = os_mbuf_append(*out_txom, attribute_value, value_len);
    if (rc != 0) {
        os_mbuf_free_chain(*out_txom);
        return BLE_HS_ENOMEM;
    }

    return 0;
}
Esempio n. 14
0
int
ble_uuid_append(struct os_mbuf *om, void *uuid128)
{
    uint16_t uuid16;
    void *buf;
    int rc;

    uuid16 = ble_uuid_128_to_16(uuid128);
    if (uuid16 != 0) {
        buf = os_mbuf_extend(om, 2);
        if (buf == NULL) {
            return BLE_HS_ENOMEM;
        }

        htole16(buf, uuid16);
    } else {
        rc = os_mbuf_append(om, uuid128, 16);
        if (rc != 0) {
            return BLE_HS_ENOMEM;
        }
    }

    return 0;
}
Esempio n. 15
0
File: main.c Progetto: A-Paul/RIOT
static int gatt_svr_chr_access_rw_demo(
        uint16_t conn_handle, uint16_t attr_handle,
        struct ble_gatt_access_ctxt *ctxt, void *arg)
{
    puts("service 'rw demo' callback triggered");

    (void) conn_handle;
    (void) attr_handle;
    (void) arg;

    int rc = 0;

    ble_uuid_t* write_uuid = (ble_uuid_t*) &gatt_svr_chr_rw_demo_write_uuid.u;
    ble_uuid_t* readonly_uuid = (ble_uuid_t*) &gatt_svr_chr_rw_demo_readonly_uuid.u;

    if (ble_uuid_cmp(ctxt->chr->uuid, write_uuid) == 0) {

        puts("access to characteristic 'rw demo (write)'");

        switch (ctxt->op) {

            case BLE_GATT_ACCESS_OP_READ_CHR:
                puts("read from characteristic");
                printf("current value of rm_demo_write_data: '%s'\n",
                       rm_demo_write_data);

                /* send given data to the client */
                rc = os_mbuf_append(ctxt->om, &rm_demo_write_data,
                                    strlen(rm_demo_write_data));

                break;

            case BLE_GATT_ACCESS_OP_WRITE_CHR:
                puts("write to characteristic");

                printf("old value of rm_demo_write_data: '%s'\n",
                       rm_demo_write_data);

                uint16_t om_len;
                om_len = OS_MBUF_PKTLEN(ctxt->om);

                /* read sent data */
                rc = ble_hs_mbuf_to_flat(ctxt->om, &rm_demo_write_data,
                                         sizeof rm_demo_write_data, &om_len);
                /* we need to null-terminate the received string */
                rm_demo_write_data[om_len] = '\0';

                printf("new value of rm_demo_write_data: '%s'\n",
                       rm_demo_write_data);

                break;

            case BLE_GATT_ACCESS_OP_READ_DSC:
                puts("read from descriptor");
                break;

            case BLE_GATT_ACCESS_OP_WRITE_DSC:
                puts("write to descriptor");
                break;

            default:
                puts("unhandled operation!");
                rc = 1;
                break;
        }

        puts("");

        return rc;
    }
    else if (ble_uuid_cmp(ctxt->chr->uuid, readonly_uuid) == 0) {

        puts("access to characteristic 'rw demo (read-only)'");

        if (ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR) {
            char random_digit;
            /* get random char between '0' and '9' */
            random_digit = 48 + (rand() % 10);

            snprintf(str_answer, STR_ANSWER_BUFFER_SIZE,
                     "new random number: %c", random_digit);
            puts(str_answer);

            rc = os_mbuf_append(ctxt->om, &str_answer, strlen(str_answer));

            puts("");

            return rc;
        }

        return 0;
    }

    puts("unhandled uuid!");
    return 1;
}
Esempio n. 16
0
static int
gatt_svr_chr_access_alert(uint16_t conn_handle, uint16_t attr_handle,
                          struct ble_gatt_access_ctxt *ctxt,
                          void *arg)
{
    uint16_t uuid16;
    int rc;

    uuid16 = ble_uuid_128_to_16(ctxt->chr->uuid128);
    assert(uuid16 != 0);

    switch (uuid16) {
    case GATT_SVR_CHR_SUP_NEW_ALERT_CAT_UUID:
        assert(ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR);
        rc = os_mbuf_append(ctxt->om, &gatt_svr_new_alert_cat,
                            sizeof gatt_svr_new_alert_cat);
        return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;

    case GATT_SVR_CHR_NEW_ALERT:
        if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR) {
            rc = gatt_svr_chr_write(ctxt->om, 0,
                                    sizeof gatt_svr_new_alert_val,
                                    gatt_svr_new_alert_val,
                                    &gatt_svr_new_alert_val_len);
            return rc;
        } else if (ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR) {
            rc = os_mbuf_append(ctxt->om, &gatt_svr_new_alert_val,
                                sizeof gatt_svr_new_alert_val);
            return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
        }

    case GATT_SVR_CHR_SUP_UNR_ALERT_CAT_UUID:
        assert(ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR);
        rc = os_mbuf_append(ctxt->om, &gatt_svr_unr_alert_cat,
                            sizeof gatt_svr_unr_alert_cat);
        return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;

    case GATT_SVR_CHR_UNR_ALERT_STAT_UUID:
        if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR) {
            rc = gatt_svr_chr_write(ctxt->om, 2, 2, &gatt_svr_unr_alert_stat,
                                    NULL);
            return rc;
        } else {
            rc = os_mbuf_append(ctxt->om, &gatt_svr_unr_alert_stat,
                                sizeof gatt_svr_unr_alert_stat);
            return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
        }

    case GATT_SVR_CHR_ALERT_NOT_CTRL_PT:
        if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR) {
            rc = gatt_svr_chr_write(ctxt->om, 2, 2,
                                    &gatt_svr_alert_not_ctrl_pt, NULL);
        } else {
            rc = BLE_ATT_ERR_UNLIKELY;
        }
        return rc;

    default:
        assert(0);
        return BLE_ATT_ERR_UNLIKELY;
    }
}
Esempio n. 17
0
/*
 * Check for full packet. If frame is not right, free the mbuf.
 */
static void
nmgr_uart_rx_pkt(struct nmgr_uart_state *nus, struct os_mbuf_pkthdr *rxm)
{
    struct os_mbuf *m;
    struct nmgr_ser_hdr *nsh;
    uint16_t crc;
    int rc;

    m = OS_MBUF_PKTHDR_TO_MBUF(rxm);

    if (rxm->omp_len <= sizeof(uint16_t) + sizeof(crc)) {
        goto err;
    }

    nsh = (struct nmgr_ser_hdr *)m->om_data;
    switch (nsh->nsh_seq) {
    case htons(SHELL_NLIP_PKT):
        if (nus->nus_rx_pkt) {
            os_mbuf_free_chain(OS_MBUF_PKTHDR_TO_MBUF(nus->nus_rx_pkt));
            nus->nus_rx_pkt = NULL;
        }
        break;
    case htons(SHELL_NLIP_DATA):
        if (!nus->nus_rx_pkt) {
            goto err;
        }
        break;
    default:
        goto err;
    }

    if (os_mbuf_append(m, "\0", 1)) {
        /*
         * Null-terminate the line for base64_decode's sake.
         */
        goto err;
    }
    m = os_mbuf_pullup(m, rxm->omp_len);
    if (!m) {
        /*
         * Make data contiguous for base64_decode's sake.
         */
        goto err;
    }
    rxm = OS_MBUF_PKTHDR(m);
    rc = base64_decode((char *)m->om_data + 2, (char *)m->om_data + 2);
    if (rc < 0) {
        goto err;
    }
    rxm->omp_len = m->om_len = rc + 2;
    if (nus->nus_rx_pkt) {
        os_mbuf_adj(m, 2);
        os_mbuf_concat(OS_MBUF_PKTHDR_TO_MBUF(nus->nus_rx_pkt), m);
    } else {
        nus->nus_rx_pkt = rxm;
    }

    m = OS_MBUF_PKTHDR_TO_MBUF(nus->nus_rx_pkt);
    nsh = (struct nmgr_ser_hdr *)m->om_data;
    if (nus->nus_rx_pkt->omp_len - sizeof(*nsh) == ntohs(nsh->nsh_len)) {
        os_mbuf_adj(m, 4);
        os_mbuf_adj(m, -2);
        nmgr_rx_req(&nus->nus_transport, m);
        nus->nus_rx_pkt = NULL;
    }
    return;
err:
    os_mbuf_free_chain(m);
}
Esempio n. 18
0
/*
 * Called by mgmt to queue packet out to UART.
 */
static int
nmgr_uart_out(struct nmgr_transport *nt, struct os_mbuf *m)
{
    struct nmgr_uart_state *nus = (struct nmgr_uart_state *)nt;
    struct os_mbuf_pkthdr *mpkt;
    struct os_mbuf *n;
    char tmp_buf[12];
    uint16_t crc;
    char *dst;
    int off;
    int boff;
    int slen;
    int sr;
    int rc;
    int last;
    int tx_sz;

    assert(OS_MBUF_IS_PKTHDR(m));
    mpkt = OS_MBUF_PKTHDR(m);

    off = 0;
    crc = CRC16_INITIAL_CRC;
    for (n = m; n; n = SLIST_NEXT(n, om_next)) {
        crc = crc16_ccitt(crc, n->om_data, n->om_len);
    }
    crc = htons(crc);
    dst = os_mbuf_extend(m, sizeof(crc));
    if (!dst) {
        goto err;
    }
    memcpy(dst, &crc, sizeof(crc));

    n = os_msys_get(SHELL_NLIP_MAX_FRAME, 0);
    if (!n || OS_MBUF_TRAILINGSPACE(n) < 32) {
        goto err;
    }

    while (off < mpkt->omp_len) {
        tx_sz = 2;
        dst = os_mbuf_extend(n, 2);
        if (off == 0) {
            *(uint16_t *)dst = htons(SHELL_NLIP_PKT);
            *(uint16_t *)tmp_buf = htons(mpkt->omp_len);
            boff = 2;
        } else {
            *(uint16_t *)dst = htons(SHELL_NLIP_DATA);
            boff = 0;
        }

        while (off < mpkt->omp_len) {
            slen = mpkt->omp_len - off;
            last = 1;
            if (slen > sizeof(tmp_buf) + boff) {
                slen = sizeof(tmp_buf) - boff;
                last = 0;
            }
            if (tx_sz + BASE64_ENCODE_SIZE(slen + boff) >= 124) {
                break;
            }
            rc = os_mbuf_copydata(m, off, slen, tmp_buf + boff);
            assert(rc == 0);

            off += slen;
            slen += boff;

            dst = os_mbuf_extend(n, BASE64_ENCODE_SIZE(slen));
            if (!dst) {
                goto err;
            }
            tx_sz += base64_encode(tmp_buf, slen, dst, last);
            boff = 0;
        }

        if (os_mbuf_append(n, "\n", 1)) {
            goto err;
        }
    }

    os_mbuf_free_chain(m);
    OS_ENTER_CRITICAL(sr);
    if (!nus->nus_tx) {
        nus->nus_tx = n;
        uart_start_tx(nus->nus_dev);
    } else {
        os_mbuf_concat(nus->nus_tx, n);
    }
    OS_EXIT_CRITICAL(sr);

    return 0;
err:
    os_mbuf_free_chain(m);
    os_mbuf_free_chain(n);
    return -1;
}