Scott Shawcroft 8958e7ef08
Add S3 GATT client support
This allows you to connect to GATT services on the other device.
It also adds connection initiation (GAP central).

More progress on 
2022-02-10 11:31:57 -08:00

238 lines
8.9 KiB
C

/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) Dan Halbert for Adafruit Industries
* Copyright (c) 2018 Artur Pacholec
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <string.h>
#include "py/runtime.h"
#include "shared-bindings/_bleio/__init__.h"
#include "shared-bindings/_bleio/Characteristic.h"
#include "shared-bindings/_bleio/Descriptor.h"
#include "shared-bindings/_bleio/Service.h"
#include "common-hal/_bleio/Adapter.h"
// #include "common-hal/_bleio/bonding.h"
void common_hal_bleio_characteristic_construct(bleio_characteristic_obj_t *self, bleio_service_obj_t *service,
uint16_t handle, bleio_uuid_obj_t *uuid, bleio_characteristic_properties_t props,
bleio_attribute_security_mode_t read_perm, bleio_attribute_security_mode_t write_perm,
mp_int_t max_length, bool fixed_length, mp_buffer_info_t *initial_value_bufinfo,
const char *user_description) {
self->service = service;
self->uuid = uuid;
self->handle = BLEIO_HANDLE_INVALID;
self->cccd_handle = BLEIO_HANDLE_INVALID;
self->sccd_handle = BLEIO_HANDLE_INVALID;
self->props = props;
self->read_perm = read_perm;
self->write_perm = write_perm;
common_hal_bleio_characteristic_set_value(self, initial_value_bufinfo);
if (gc_alloc_possible()) {
self->descriptor_list = mp_obj_new_list(0, NULL);
} else {
self->descriptor_list = NULL;
}
self->max_length = max_length;
self->fixed_length = fixed_length;
if (service->is_remote) {
self->handle = handle;
} else {
common_hal_bleio_service_add_characteristic(self->service, self, initial_value_bufinfo, user_description);
}
}
mp_obj_tuple_t *common_hal_bleio_characteristic_get_descriptors(bleio_characteristic_obj_t *self) {
if (self->descriptor_list == NULL) {
return mp_const_empty_tuple;
}
return mp_obj_new_tuple(self->descriptor_list->len, self->descriptor_list->items);
}
bleio_service_obj_t *common_hal_bleio_characteristic_get_service(bleio_characteristic_obj_t *self) {
return self->service;
}
typedef struct {
TaskHandle_t task;
uint8_t *buf;
uint16_t len;
} _read_info_t;
STATIC int _read_cb(uint16_t conn_handle,
const struct ble_gatt_error *error,
struct ble_gatt_attr *attr,
void *arg) {
_read_info_t *read_info = (_read_info_t *)arg;
switch (error->status) {
case 0: {
int len = MIN(read_info->len, OS_MBUF_PKTLEN(attr->om));
os_mbuf_copydata(attr->om, attr->offset, len, read_info->buf);
read_info->len = len;
}
MP_FALLTHROUGH;
default:
#if CIRCUITPY_VERBOSE_BLE
// For debugging.
mp_printf(&mp_plat_print, "Read status: %d\n", error->status);
#endif
xTaskNotify(read_info->task, error->status, eSetValueWithOverwrite);
break;
}
return 0;
}
size_t common_hal_bleio_characteristic_get_value(bleio_characteristic_obj_t *self, uint8_t *buf, size_t len) {
// Do GATT operations only if this characteristic has been added to a registered service.
if (self->handle == BLEIO_HANDLE_INVALID) {
return 0;
}
uint16_t conn_handle = bleio_connection_get_conn_handle(self->service->connection);
if (common_hal_bleio_service_get_is_remote(self->service)) {
_read_info_t read_info = {
.task = xTaskGetCurrentTaskHandle(),
.buf = buf,
.len = len
};
CHECK_NIMBLE_ERROR(ble_gattc_read(conn_handle, self->handle, _read_cb, &read_info));
int error_code;
xTaskNotifyWait(0, 0, (uint32_t *)&error_code, 200);
CHECK_BLE_ERROR(error_code);
return read_info.len;
} else {
len = MIN(self->current_value_len, len);
memcpy(buf, self->current_value, len);
return len;
}
return 0;
}
size_t common_hal_bleio_characteristic_get_max_length(bleio_characteristic_obj_t *self) {
return self->max_length;
}
STATIC int _write_cb(uint16_t conn_handle,
const struct ble_gatt_error *error,
struct ble_gatt_attr *attr,
void *arg) {
TaskHandle_t task = (TaskHandle_t)arg;
xTaskNotify(task, error->status, eSetValueWithOverwrite);
return 0;
}
void common_hal_bleio_characteristic_set_value(bleio_characteristic_obj_t *self, mp_buffer_info_t *bufinfo) {
if (common_hal_bleio_service_get_is_remote(self->service)) {
uint16_t conn_handle = bleio_connection_get_conn_handle(self->service->connection);
if ((self->props & CHAR_PROP_WRITE_NO_RESPONSE) != 0) {
CHECK_NIMBLE_ERROR(ble_gattc_write_no_rsp_flat(conn_handle, self->handle, bufinfo->buf, bufinfo->len));
} else {
CHECK_NIMBLE_ERROR(ble_gattc_write_flat(conn_handle, self->handle, bufinfo->buf, bufinfo->len, _write_cb, xTaskGetCurrentTaskHandle()));
int error_code;
xTaskNotifyWait(0, 0, (uint32_t *)&error_code, 200);
CHECK_BLE_ERROR(error_code);
}
} else {
// Validate data length for local characteristics only.
// TODO: Test this once we can get servers going.
if (self->fixed_length && bufinfo->len != self->max_length) {
mp_raise_ValueError(translate("Value length != required fixed length"));
}
if (bufinfo->len > self->max_length) {
mp_raise_ValueError(translate("Value length > max_length"));
}
if (bufinfo == NULL) {
self->current_value_len = 0;
ble_gatts_chr_updated(self->handle);
return;
}
self->current_value_len = bufinfo->len;
// If we've already allocated an internal buffer or the provided buffer
// is on the heap, then copy into the internal buffer.
if (self->current_value_alloc > 0 || gc_nbytes(bufinfo->buf) > 0) {
if (self->current_value_alloc < bufinfo->len) {
self->current_value = m_realloc(self->current_value, bufinfo->len);
// Get the number of bytes from the heap because it may be more
// than the len due to gc block size.
self->current_value_alloc = gc_nbytes(self->current_value);
}
memcpy(self->current_value, bufinfo->buf, bufinfo->len);
} else {
// Otherwise, use the provided buffer to delay any heap allocation.
self->current_value = bufinfo->buf;
self->current_value_alloc = 0;
}
ble_gatts_chr_updated(self->handle);
}
}
bleio_uuid_obj_t *common_hal_bleio_characteristic_get_uuid(bleio_characteristic_obj_t *self) {
return self->uuid;
}
bleio_characteristic_properties_t common_hal_bleio_characteristic_get_properties(bleio_characteristic_obj_t *self) {
return self->props;
}
void common_hal_bleio_characteristic_add_descriptor(bleio_characteristic_obj_t *self,
bleio_descriptor_obj_t *descriptor) {
// TODO: Implement this.
mp_obj_list_append(MP_OBJ_FROM_PTR(self->descriptor_list),
MP_OBJ_FROM_PTR(descriptor));
}
void common_hal_bleio_characteristic_set_cccd(bleio_characteristic_obj_t *self, bool notify, bool indicate) {
if (self->cccd_handle == BLEIO_HANDLE_INVALID) {
mp_raise_bleio_BluetoothError(translate("No CCCD for this Characteristic"));
}
if (!common_hal_bleio_service_get_is_remote(self->service)) {
mp_raise_bleio_RoleError(translate("Can't set CCCD on local Characteristic"));
}
const uint16_t conn_handle = bleio_connection_get_conn_handle(self->service->connection);
common_hal_bleio_check_connected(conn_handle);
uint16_t cccd_value =
(notify ? 1 << 0 : 0) |
(indicate ? 1 << 1: 0);
CHECK_NIMBLE_ERROR(ble_gattc_write_flat(conn_handle, self->cccd_handle, &cccd_value, 2, _write_cb, xTaskGetCurrentTaskHandle()));
int error_code;
xTaskNotifyWait(0, 0, (uint32_t *)&error_code, 200);
CHECK_BLE_ERROR(error_code);
}