blob: 24af84dde6b671af64841d572e2eba91b0cd7fcd [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#include <assert.h>
#include <stdio.h>
#include <string.h>
#include "os/mynewt.h"
#include "host/ble_hs.h"
#include "mgmt/mgmt.h"
#include "newtmgr/newtmgr.h"
#include "console/console.h"
/* nmgr ble mqueue */
struct os_mqueue nmgr_ble_mq;
/* ble nmgr transport */
struct nmgr_transport ble_nt;
/* ble nmgr attr handle */
uint16_t g_ble_nmgr_attr_handle;
/**
* The vendor specific "newtmgr" service consists of one write no-rsp
* characteristic for newtmgr requests: a single-byte characteristic that can
* only accepts write-without-response commands. The contents of each write
* command contains an NMP request. NMP responses are sent back in the form of
* unsolicited notifications from the same characteristic.
*/
/* {8D53DC1D-1DB7-4CD3-868B-8A527460AA84} */
static const ble_uuid128_t gatt_svr_svc_newtmgr =
BLE_UUID128_INIT(0x84, 0xaa, 0x60, 0x74, 0x52, 0x8a, 0x8b, 0x86,
0xd3, 0x4c, 0xb7, 0x1d, 0x1d, 0xdc, 0x53, 0x8d);
/* {DA2E7828-FBCE-4E01-AE9E-261174997C48} */
static const ble_uuid128_t gatt_svr_chr_newtmgr =
BLE_UUID128_INIT(0x48, 0x7c, 0x99, 0x74, 0x11, 0x26, 0x9e, 0xae,
0x01, 0x4e, 0xce, 0xfb, 0x28, 0x78, 0x2e, 0xda);
static int
gatt_svr_chr_access_newtmgr(uint16_t conn_handle, uint16_t attr_handle,
struct ble_gatt_access_ctxt *ctxt, void *arg);
static const struct ble_gatt_svc_def gatt_svr_svcs[] = {
{
/* Service: newtmgr */
.type = BLE_GATT_SVC_TYPE_PRIMARY,
.uuid = &gatt_svr_svc_newtmgr.u,
.characteristics = (struct ble_gatt_chr_def[]) { {
/* Characteristic: Write No Rsp */
.uuid = &gatt_svr_chr_newtmgr.u,
.access_cb = gatt_svr_chr_access_newtmgr,
.flags = BLE_GATT_CHR_F_WRITE_NO_RSP | BLE_GATT_CHR_F_NOTIFY,
.val_handle = &g_ble_nmgr_attr_handle,
}, {
0, /* No more characteristics in this service */
} },
},
{
0, /* No more services */
},
};
static int
gatt_svr_chr_access_newtmgr(uint16_t conn_handle, uint16_t attr_handle,
struct ble_gatt_access_ctxt *ctxt, void *arg)
{
int rc;
struct os_mbuf *m_req;
switch (ctxt->op) {
case BLE_GATT_ACCESS_OP_WRITE_CHR:
/* Try to reuse the BLE packet mbuf as the newtmgr request. This
* requires a two-byte usrhdr to hold the BLE connection handle so
* that the newtmgr response can be sent to the correct peer. If
* it is not possible to reuse the mbuf, then allocate a new one
* and copy the request contents.
*/
if (OS_MBUF_USRHDR_LEN(ctxt->om) >= sizeof (conn_handle)) {
/* Sufficient usrhdr space already present. */
m_req = ctxt->om;
ctxt->om = NULL;
} else if (OS_MBUF_LEADINGSPACE(ctxt->om) >=
sizeof (conn_handle)) {
/* Usrhdr isn't present, but there is enough leading space to
* add one.
*/
m_req = ctxt->om;
ctxt->om = NULL;
m_req->om_pkthdr_len += sizeof (conn_handle);
} else {
/* The mbuf can't be reused. Allocate a new one and perform a
* copy. Don't set ctxt->om to NULL; let the NimBLE host free
* it.
*/
m_req = os_msys_get_pkthdr(OS_MBUF_PKTLEN(ctxt->om),
sizeof (conn_handle));
if (!m_req) {
return BLE_ATT_ERR_INSUFFICIENT_RES;
}
rc = os_mbuf_appendfrom(m_req, ctxt->om, 0,
OS_MBUF_PKTLEN(ctxt->om));
if (rc) {
return BLE_ATT_ERR_INSUFFICIENT_RES;
}
}
/* Write the connection handle to the newtmgr request usrhdr. This
* is necessary so that we later know who to send the newtmgr
* response to.
*/
memcpy(OS_MBUF_USRHDR(m_req), &conn_handle, sizeof(conn_handle));
rc = nmgr_rx_req(&ble_nt, m_req);
if (rc != 0) {
return BLE_ATT_ERR_UNLIKELY;
}
return 0;
default:
assert(0);
return BLE_ATT_ERR_UNLIKELY;
}
}
uint16_t
nmgr_ble_get_mtu(struct os_mbuf *req) {
uint16_t conn_handle;
uint16_t mtu;
memcpy(&conn_handle, OS_MBUF_USRHDR(req), sizeof (conn_handle));
mtu = ble_att_mtu(conn_handle);
if (!mtu) {
/* No longer connected. */
return 0;
}
/* 3 is the number of bytes for ATT notification base */
mtu = mtu - 3;
return (mtu);
}
/**
* Nmgr ble process mqueue event
* Gets an event from the nmgr mqueue and does a notify with the response
*
* @param eventq
* @return 0 on success; non-zero on failure
*/
static void
nmgr_ble_event_data_in(struct os_event *ev)
{
struct os_mbuf *m_resp;
uint16_t conn_handle;
while ((m_resp = os_mqueue_get(&nmgr_ble_mq)) != NULL) {
assert(OS_MBUF_USRHDR_LEN(m_resp) >= sizeof (conn_handle));
memcpy(&conn_handle, OS_MBUF_USRHDR(m_resp), sizeof (conn_handle));
ble_gattc_notify_custom(conn_handle, g_ble_nmgr_attr_handle,
m_resp);
}
}
static int
nmgr_ble_out(struct nmgr_transport *nt, struct os_mbuf *om)
{
int rc;
rc = os_mqueue_put(&nmgr_ble_mq, mgmt_evq_get(), om);
if (rc != 0) {
goto err;
}
return (0);
err:
os_mbuf_free_chain(om);
return (rc);
}
/**
* Nmgr ble GATT server initialization
*
* @param eventq
* @return 0 on success; non-zero on failure
*/
int
nmgr_ble_gatt_svr_init(void)
{
int rc;
rc = ble_gatts_count_cfg(gatt_svr_svcs);
if (rc != 0) {
goto err;
}
rc = ble_gatts_add_svcs(gatt_svr_svcs);
if (rc != 0) {
return rc;
}
os_mqueue_init(&nmgr_ble_mq, &nmgr_ble_event_data_in, NULL);
rc = nmgr_transport_init(&ble_nt, nmgr_ble_out, nmgr_ble_get_mtu);
err:
return rc;
}
void
newtmgr_ble_pkg_init(void)
{
int rc;
/* Ensure this function only gets called by sysinit. */
SYSINIT_ASSERT_ACTIVE();
rc = nmgr_ble_gatt_svr_init();
SYSINIT_PANIC_ASSERT(rc == 0);
}