From f17059b10bde0bdce3aca959ea9ee207ed98ede7 Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Wed, 28 Aug 2019 23:15:22 -0400 Subject: [PATCH] another API rework: less abstraction leakage --- ports/nrf/common-hal/bleio/Characteristic.c | 7 +- ports/nrf/common-hal/bleio/Descriptor.c | 4 +- ports/nrf/common-hal/bleio/Peripheral.c | 3 - ports/nrf/common-hal/bleio/Service.c | 9 +- ports/nrf/common-hal/bleio/__init__.c | 13 +- shared-bindings/bleio/Characteristic.c | 187 +++++++++++--------- shared-bindings/bleio/Characteristic.h | 3 +- shared-bindings/bleio/Descriptor.c | 93 +++++++++- shared-bindings/bleio/Descriptor.h | 2 +- shared-bindings/bleio/Peripheral.c | 45 ----- shared-bindings/bleio/Service.c | 153 ++++++---------- shared-bindings/bleio/Service.h | 3 +- 12 files changed, 270 insertions(+), 252 deletions(-) diff --git a/ports/nrf/common-hal/bleio/Characteristic.c b/ports/nrf/common-hal/bleio/Characteristic.c index 012029b6c3..06a312f583 100644 --- a/ports/nrf/common-hal/bleio/Characteristic.c +++ b/ports/nrf/common-hal/bleio/Characteristic.c @@ -124,8 +124,8 @@ STATIC void characteristic_gattc_read(bleio_characteristic_obj_t *characteristic ble_drv_remove_event_handler(characteristic_on_gattc_read_rsp_evt, characteristic); } -void common_hal_bleio_characteristic_construct(bleio_characteristic_obj_t *self, 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) { - self->service = MP_OBJ_NULL; +void common_hal_bleio_characteristic_construct(bleio_characteristic_obj_t *self, bleio_service_obj_t *service, 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) { + self->service = service; self->uuid = uuid; self->handle = BLE_GATT_HANDLE_INVALID; self->props = props; @@ -222,9 +222,6 @@ bleio_characteristic_properties_t common_hal_bleio_characteristic_get_properties } void common_hal_bleio_characteristic_add_descriptor(bleio_characteristic_obj_t *self, bleio_descriptor_obj_t *descriptor) { - // Connect descriptor to parent characteristic. - descriptor->characteristic = self; - ble_uuid_t desc_uuid; bleio_uuid_convert_to_nrf_ble_uuid(descriptor->uuid, &desc_uuid); diff --git a/ports/nrf/common-hal/bleio/Descriptor.c b/ports/nrf/common-hal/bleio/Descriptor.c index ee8d840f2f..4a5974a064 100644 --- a/ports/nrf/common-hal/bleio/Descriptor.c +++ b/ports/nrf/common-hal/bleio/Descriptor.c @@ -35,8 +35,8 @@ static volatile bleio_descriptor_obj_t *m_read_descriptor; -void common_hal_bleio_descriptor_construct(bleio_descriptor_obj_t *self, bleio_uuid_obj_t *uuid, 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) { - self->characteristic = MP_OBJ_NULL; +void common_hal_bleio_descriptor_construct(bleio_descriptor_obj_t *self, bleio_characteristic_obj_t *characteristic, bleio_uuid_obj_t *uuid, 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) { + self->characteristic = characteristic; self->uuid = uuid; self->handle = BLE_GATT_HANDLE_INVALID; self->read_perm = read_perm; diff --git a/ports/nrf/common-hal/bleio/Peripheral.c b/ports/nrf/common-hal/bleio/Peripheral.c index 6f8a1a2fcd..92dfc2da1d 100644 --- a/ports/nrf/common-hal/bleio/Peripheral.c +++ b/ports/nrf/common-hal/bleio/Peripheral.c @@ -223,8 +223,6 @@ void common_hal_bleio_peripheral_construct(bleio_peripheral_obj_t *self, mp_obj_ } void common_hal_bleio_peripheral_add_service(bleio_peripheral_obj_t *self, bleio_service_obj_t *service) { - service->device = MP_OBJ_FROM_PTR(self); - ble_uuid_t uuid; bleio_uuid_convert_to_nrf_ble_uuid(service->uuid, &uuid); @@ -241,7 +239,6 @@ void common_hal_bleio_peripheral_add_service(bleio_peripheral_obj_t *self, bleio mp_obj_list_append(self->service_list, MP_OBJ_FROM_PTR(service)); } - mp_obj_list_t *common_hal_bleio_peripheral_get_services(bleio_peripheral_obj_t *self) { return self->service_list; } diff --git a/ports/nrf/common-hal/bleio/Service.c b/ports/nrf/common-hal/bleio/Service.c index e5201cc46c..f69bbd89da 100644 --- a/ports/nrf/common-hal/bleio/Service.c +++ b/ports/nrf/common-hal/bleio/Service.c @@ -31,16 +31,17 @@ #include "common-hal/bleio/__init__.h" #include "shared-bindings/bleio/Characteristic.h" #include "shared-bindings/bleio/Descriptor.h" +#include "shared-bindings/bleio/Peripheral.h" #include "shared-bindings/bleio/Service.h" #include "shared-bindings/bleio/Adapter.h" -void common_hal_bleio_service_construct(bleio_service_obj_t *self, bleio_uuid_obj_t *uuid, bool is_secondary) { +void common_hal_bleio_service_construct(bleio_service_obj_t *self, bleio_peripheral_obj_t *peripheral, bleio_uuid_obj_t *uuid, bool is_secondary) { + self->device = MP_OBJ_FROM_PTR(peripheral); self->handle = 0xFFFF; self->uuid = uuid; self->characteristic_list = mp_obj_new_list(0, NULL); self->is_remote = false; self->is_secondary = is_secondary; - self->device = mp_const_none; } bleio_uuid_obj_t *common_hal_bleio_service_get_uuid(bleio_service_obj_t *self) { @@ -59,11 +60,7 @@ bool common_hal_bleio_service_get_is_secondary(bleio_service_obj_t *self) { return self->is_secondary; } - void common_hal_bleio_service_add_characteristic(bleio_service_obj_t *self, bleio_characteristic_obj_t *characteristic) { - // Connect characteristic to parent service. - characteristic->service = self; - ble_gatts_char_md_t char_md = { .char_props.broadcast = (characteristic->props & CHAR_PROP_BROADCAST) ? 1 : 0, .char_props.read = (characteristic->props & CHAR_PROP_READ) ? 1 : 0, diff --git a/ports/nrf/common-hal/bleio/__init__.c b/ports/nrf/common-hal/bleio/__init__.c index 868e0a8e98..4a7597e37b 100644 --- a/ports/nrf/common-hal/bleio/__init__.c +++ b/ports/nrf/common-hal/bleio/__init__.c @@ -158,9 +158,8 @@ STATIC void on_primary_srv_discovery_rsp(ble_gattc_evt_prim_srvc_disc_rsp_t *res service->base.type = &bleio_service_type; // Initialize several fields at once. - common_hal_bleio_service_construct(service, NULL, false); + common_hal_bleio_service_construct(service, device, NULL, false); - service->device = device; service->is_remote = true; service->start_handle = gattc_service->handle_range.start_handle; service->end_handle = gattc_service->handle_range.end_handle; @@ -218,11 +217,10 @@ STATIC void on_char_discovery_rsp(ble_gattc_evt_char_disc_rsp_t *response, mp_ob // Call common_hal_bleio_characteristic_construct() to initalize some fields and set up evt handler. common_hal_bleio_characteristic_construct( - characteristic, uuid, props, SECURITY_MODE_OPEN, SECURITY_MODE_OPEN, + characteristic, m_char_discovery_service, uuid, props, SECURITY_MODE_OPEN, SECURITY_MODE_OPEN, GATT_MAX_DATA_LENGTH, false, // max_length, fixed_length: values may not matter for gattc mp_obj_new_list(0, NULL)); characteristic->handle = gattc_char->handle_value; - characteristic->service = m_char_discovery_service; mp_obj_list_append(m_char_discovery_service->characteristic_list, MP_OBJ_FROM_PTR(characteristic)); } @@ -274,10 +272,11 @@ STATIC void on_desc_discovery_rsp(ble_gattc_evt_desc_disc_rsp_t *response, mp_ob // For now, just leave the UUID as NULL. } - common_hal_bleio_descriptor_construct(descriptor, uuid, SECURITY_MODE_OPEN, SECURITY_MODE_OPEN, - GATT_MAX_DATA_LENGTH, false, mp_const_empty_bytes); + common_hal_bleio_descriptor_construct( + descriptor, m_desc_discovery_characteristic, uuid, + SECURITY_MODE_OPEN, SECURITY_MODE_OPEN, + GATT_MAX_DATA_LENGTH, false, mp_const_empty_bytes); descriptor->handle = gattc_desc->handle; - descriptor->characteristic = m_desc_discovery_characteristic; mp_obj_list_append(m_desc_discovery_characteristic->descriptor_list, MP_OBJ_FROM_PTR(descriptor)); } diff --git a/shared-bindings/bleio/Characteristic.c b/shared-bindings/bleio/Characteristic.c index d9d920b768..90cd77d177 100644 --- a/shared-bindings/bleio/Characteristic.c +++ b/shared-bindings/bleio/Characteristic.c @@ -30,7 +30,7 @@ #include "py/runtime.h" #include "shared-bindings/bleio/Attribute.h" #include "shared-bindings/bleio/Characteristic.h" -#include "shared-bindings/bleio/Descriptor.h" +#include "shared-bindings/bleio/Service.h" #include "shared-bindings/bleio/UUID.h" //| .. currentmodule:: bleio @@ -41,12 +41,111 @@ //| Stores information about a BLE service characteristic and allows reading //| and writing of the characteristic's value. //| -//| A Characteristic cannot be created directly. A new local Characteristic can be created -//| and attached to a Service by calling `Service.add_characteristic()`. +//| There is no regular constructor for a Characteristic. A new local Characteristic can be created +//| and attached to a Service by calling `Characteristic.add_to_service()`. //| Remote Characteristic objects are created by `Central.discover_remote_services()` //| or `Peripheral.discover_remote_services()` as part of remote Services. //| +//| .. method:: add_to_service(service, uuid, *, properties=0, read_perm=`Attribute.OPEN`, write_perm=`Attribute.OPEN`, max_length=20, fixed_length=False, initial_value=None) +//| +//| Create a new `Characteristic` object, and add it to this Service. +//| +//| :param bleio.Service service: The service that will provide this characteristic +//| :param bleio.UUID uuid: The uuid of the characteristic +//| :param int properties: The properties of the characteristic, +//| specified as a bitmask of these values bitwise-or'd together: +//| `Characteristic.BROADCAST`, `Characteristic.INDICATE`, `Characteristic.NOTIFY`, +//| `Characteristic.READ`, `Characteristic.WRITE`, `Characteristic.WRITE_NO_RESPONSE`. +//| :param int read_perm: Specifies whether the characteristic can be read by a client, and if so, which +//| security mode is required. Must be one of the integer values `Attribute.NO_ACCESS`, `Attribute.OPEN`, +//| `Attribute.ENCRYPT_NO_MITM`, `Attribute.ENCRYPT_WITH_MITM`, `Attribute.LESC_ENCRYPT_WITH_MITM`, +//| `Attribute.SIGNED_NO_MITM`, or `Attribute.SIGNED_WITH_MITM`. +//| :param int write_perm: Specifies whether the characteristic can be written by a client, and if so, which +//| security mode is required. Values allowed are the same as ``read_perm``. +//| :param int max_length: Maximum length in bytes of the characteristic value. The maximum allowed is +//| is 512, or possibly 510 if ``fixed_length`` is False. The default, 20, is the maximum +//| number of data bytes that fit in a single BLE 4.x ATT packet. +//| :param bool fixed_length: True if the characteristic value is of fixed length. +//| :param buf initial_value: The initial value for this characteristic. If not given, will be +//| filled with zeros. +//| +//| :return: the new `Characteristic`. +//| +STATIC mp_obj_t bleio_characteristic_add_to_service(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + // class is arg[0], which we can ignore. + + enum { ARG_service, ARG_uuid, ARG_properties, ARG_read_perm, ARG_write_perm, + ARG_max_length, ARG_fixed_length, ARG_initial_value }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_service, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_uuid, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_properties, MP_ARG_KW_ONLY| MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_read_perm, MP_ARG_KW_ONLY| MP_ARG_INT, {.u_int = SECURITY_MODE_OPEN} }, + { MP_QSTR_write_perm, MP_ARG_KW_ONLY| MP_ARG_INT, {.u_int = SECURITY_MODE_OPEN} }, + { MP_QSTR_max_length, MP_ARG_KW_ONLY| MP_ARG_INT, {.u_int = 20} }, + { MP_QSTR_fixed_length, MP_ARG_KW_ONLY| MP_ARG_BOOL, {.u_bool = false} }, + { MP_QSTR_initial_value, MP_ARG_KW_ONLY| MP_ARG_OBJ, {.u_obj = mp_const_none} }, + }; + + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + const mp_obj_t service_obj = args[ARG_service].u_obj; + if (!MP_OBJ_IS_TYPE(service_obj, &bleio_service_type)) { + mp_raise_ValueError(translate("Expected a Service")); + } + + const mp_obj_t uuid_obj = args[ARG_uuid].u_obj; + if (!MP_OBJ_IS_TYPE(uuid_obj, &bleio_uuid_type)) { + mp_raise_ValueError(translate("Expected a UUID")); + } + + const bleio_characteristic_properties_t properties = args[ARG_properties].u_int; + if (properties & ~CHAR_PROP_ALL) { + mp_raise_ValueError(translate("Invalid properties")); + } + + const bleio_attribute_security_mode_t read_perm = args[ARG_read_perm].u_int; + common_hal_bleio_attribute_security_mode_check_valid(read_perm); + + const bleio_attribute_security_mode_t write_perm = args[ARG_write_perm].u_int; + common_hal_bleio_attribute_security_mode_check_valid(write_perm); + + const mp_int_t max_length = args[ARG_max_length].u_int; + const bool fixed_length = args[ARG_fixed_length].u_bool; + mp_obj_t initial_value = args[ARG_initial_value].u_obj; + + // Length will be validated in common_hal. + mp_buffer_info_t initial_value_bufinfo; + if (initial_value == mp_const_none) { + if (fixed_length && max_length > 0) { + initial_value = mp_obj_new_bytes_of_zeros(max_length); + } else { + initial_value = mp_const_empty_bytes; + } + } + mp_get_buffer_raise(initial_value, &initial_value_bufinfo, MP_BUFFER_READ); + + bleio_characteristic_obj_t *characteristic = m_new_obj(bleio_characteristic_obj_t); + characteristic->base.type = &bleio_characteristic_type; + + // Range checking on max_length arg is done by the common_hal layer, because + // it may vary depending on underlying BLE implementation. + common_hal_bleio_characteristic_construct( + characteristic, MP_OBJ_TO_PTR(service_obj), MP_OBJ_TO_PTR(uuid_obj), + properties, read_perm, write_perm, + max_length, fixed_length, &initial_value_bufinfo); + + common_hal_bleio_service_add_characteristic(service_obj, characteristic); + + return MP_OBJ_FROM_PTR(characteristic); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(bleio_characteristic_add_to_service_fun_obj, 3, bleio_characteristic_add_to_service); +STATIC MP_DEFINE_CONST_CLASSMETHOD_OBJ(bleio_characteristic_add_to_service_obj, MP_ROM_PTR(&bleio_characteristic_add_to_service_fun_obj)); + + + //| .. attribute:: properties //| //| An int bitmask representing which properties are set, specified as bitwise or'ing of @@ -155,84 +254,6 @@ const mp_obj_property_t bleio_characteristic_service_obj = { (mp_obj_t)&mp_const_none_obj }, }; -//| .. method:: add_descriptor(uuid, *, read_perm=`Attribute.OPEN`, write_perm=`Attribute.OPEN`, max_length=20, fixed_length=False, initial_value=b'') -//| -//| Create a new `Descriptor` object, and add it to this Service. -//| -//| :param bleio.UUID uuid: The uuid of the descriptor -//| :param int read_perm: Specifies whether the descriptor can be read by a client, and if so, which -//| security mode is required. Must be one of the integer values `Attribute.NO_ACCESS`, `Attribute.OPEN`, -//| `Attribute.ENCRYPT_NO_MITM`, `Attribute.ENCRYPT_WITH_MITM`, `Attribute.LESC_ENCRYPT_WITH_MITM`, -//| `Attribute.SIGNED_NO_MITM`, or `Attribute.SIGNED_WITH_MITM`. -//| :param int write_perm: Specifies whether the descriptor can be written by a client, and if so, which -//| security mode is required. Values allowed are the same as ``read_perm``. -//| :param int max_length: Maximum length in bytes of the descriptor value. The maximum allowed is -//| is 512, or possibly 510 if ``fixed_length`` is False. The default, 20, is the maximum -//| number of data bytes that fit in a single BLE 4.x ATT packet. -//| :param bool fixed_length: True if the descriptor value is of fixed length. -//| :param buf initial_value: The initial value for this descriptor. -//| -//| :return: the new `Descriptor`. -//| -STATIC mp_obj_t bleio_characteristic_add_descriptor(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - bleio_characteristic_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); - - enum { ARG_uuid, ARG_read_perm, ARG_write_perm, - ARG_max_length, ARG_fixed_length, ARG_initial_value }; - static const mp_arg_t allowed_args[] = { - { MP_QSTR_uuid, MP_ARG_REQUIRED | MP_ARG_OBJ }, - { MP_QSTR_read_perm, MP_ARG_KW_ONLY| MP_ARG_INT, {.u_int = SECURITY_MODE_OPEN} }, - { MP_QSTR_write_perm, MP_ARG_KW_ONLY| MP_ARG_INT, {.u_int = SECURITY_MODE_OPEN} }, - { MP_QSTR_max_length, MP_ARG_KW_ONLY| MP_ARG_INT, {.u_int = 20} }, - { MP_QSTR_fixed_length, MP_ARG_KW_ONLY| MP_ARG_BOOL, {.u_bool = false} }, - { MP_QSTR_initial_value, MP_ARG_KW_ONLY| MP_ARG_OBJ, {.u_obj = mp_const_empty_bytes} }, - }; - - mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; - mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - - const mp_obj_t uuid_obj = args[ARG_uuid].u_obj; - - if (!MP_OBJ_IS_TYPE(uuid_obj, &bleio_uuid_type)) { - mp_raise_ValueError(translate("Expected a UUID")); - } - bleio_uuid_obj_t *uuid = MP_OBJ_TO_PTR(uuid_obj); - - const bleio_attribute_security_mode_t read_perm = args[ARG_read_perm].u_int; - common_hal_bleio_attribute_security_mode_check_valid(read_perm); - - const bleio_attribute_security_mode_t write_perm = args[ARG_write_perm].u_int; - common_hal_bleio_attribute_security_mode_check_valid(write_perm); - - const mp_int_t max_length = args[ARG_max_length].u_int; - const bool fixed_length = args[ARG_fixed_length].u_bool; - mp_obj_t initial_value = args[ARG_initial_value].u_obj; - - // Length will be validated in common_hal. - mp_buffer_info_t initial_value_bufinfo; - if (initial_value == mp_const_none) { - if (fixed_length && max_length > 0) { - initial_value = mp_obj_new_bytes_of_zeros(max_length); - } else { - initial_value = mp_const_empty_bytes; - } - } - mp_get_buffer_raise(initial_value, &initial_value_bufinfo, MP_BUFFER_READ); - - bleio_descriptor_obj_t *descriptor = m_new_obj(bleio_descriptor_obj_t); - descriptor->base.type = &bleio_descriptor_type; - - // Range checking on max_length arg is done by the common_hal layer, because - // it may vary depending on underlying BLE implementation. - common_hal_bleio_descriptor_construct( - descriptor, uuid, read_perm, write_perm, max_length, fixed_length, &initial_value_bufinfo); - - common_hal_bleio_characteristic_add_descriptor(self, descriptor); - - return MP_OBJ_FROM_PTR(descriptor); -} -STATIC MP_DEFINE_CONST_FUN_OBJ_KW(bleio_characteristic_add_descriptor_obj, 2, bleio_characteristic_add_descriptor); - //| .. method:: set_cccd(*, notify=False, indicate=False) //| //| Set the remote characteristic's CCCD to enable or disable notification and indication. @@ -260,10 +281,10 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_KW(bleio_characteristic_set_cccd_obj, 1, bleio_ch STATIC const mp_rom_map_elem_t bleio_characteristic_locals_dict_table[] = { - { MP_ROM_QSTR(MP_QSTR_properties), MP_ROM_PTR(&bleio_characteristic_get_properties) }, + { MP_ROM_QSTR(MP_QSTR_add_to_service), MP_ROM_PTR(&bleio_characteristic_add_to_service_obj) }, + { MP_ROM_QSTR(MP_QSTR_properties), MP_ROM_PTR(&bleio_characteristic_get_properties_obj) }, { MP_ROM_QSTR(MP_QSTR_uuid), MP_ROM_PTR(&bleio_characteristic_uuid_obj) }, { MP_ROM_QSTR(MP_QSTR_value), MP_ROM_PTR(&bleio_characteristic_value_obj) }, - { MP_ROM_QSTR(MP_QSTR_add_descriptor), MP_ROM_PTR(&bleio_characteristic_add_descriptor_obj) }, { MP_ROM_QSTR(MP_QSTR_set_cccd), MP_ROM_PTR(&bleio_characteristic_set_cccd_obj) }, // Bitmask constants to represent properties diff --git a/shared-bindings/bleio/Characteristic.h b/shared-bindings/bleio/Characteristic.h index d9d980b330..52df87b36e 100644 --- a/shared-bindings/bleio/Characteristic.h +++ b/shared-bindings/bleio/Characteristic.h @@ -32,10 +32,11 @@ #include "shared-bindings/bleio/Descriptor.h" #include "shared-module/bleio/Characteristic.h" #include "common-hal/bleio/Characteristic.h" +#include "common-hal/bleio/Service.h" extern const mp_obj_type_t bleio_characteristic_type; -extern void common_hal_bleio_characteristic_construct(bleio_characteristic_obj_t *self, 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); +extern void common_hal_bleio_characteristic_construct(bleio_characteristic_obj_t *self, bleio_service_obj_t *service, 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); extern mp_obj_t common_hal_bleio_characteristic_get_value(bleio_characteristic_obj_t *self); extern void common_hal_bleio_characteristic_set_value(bleio_characteristic_obj_t *self, mp_buffer_info_t *bufinfo); extern bleio_characteristic_properties_t common_hal_bleio_characteristic_get_properties(bleio_characteristic_obj_t *self); diff --git a/shared-bindings/bleio/Descriptor.c b/shared-bindings/bleio/Descriptor.c index db8d393d1a..39fd21d888 100644 --- a/shared-bindings/bleio/Descriptor.c +++ b/shared-bindings/bleio/Descriptor.c @@ -29,6 +29,7 @@ #include "py/objproperty.h" #include "py/runtime.h" #include "shared-bindings/bleio/Attribute.h" +#include "shared-bindings/bleio/Characteristic.h" #include "shared-bindings/bleio/Descriptor.h" #include "shared-bindings/bleio/UUID.h" @@ -41,12 +42,98 @@ //| Descriptors are attached to BLE characteristics and provide contextual //| information about the characteristic. //| -//| A Descriptors cannot be created directly. A new local Descriptors can be created -//| and attached to a Descriptors by calling `Service.add_characteristic()`. +//| There is no regular constructor for a Descriptor. A new local Descriptor can be created +//| and attached to a Characteristic by calling `Descriptor.add_to_characteristic()`. //| Remote Descriptor objects are created by `Central.discover_remote_services()` //| or `Peripheral.discover_remote_services()` as part of remote Characteristics //| in the remote Services that are discovered. +//| .. method:: add_to_characteristic(characteristic, uuid, *, read_perm=`Attribute.OPEN`, write_perm=`Attribute.OPEN`, max_length=20, fixed_length=False, initial_value=b'') +//| +//| Create a new `Descriptor` object, and add it to this Service. +//| +//| :param bleio.Characteristic characteristic: The characteristic that will hold this descriptor +//| :param bleio.UUID uuid: The uuid of the descriptor +//| :param int read_perm: Specifies whether the descriptor can be read by a client, and if so, which +//| security mode is required. Must be one of the integer values `Attribute.NO_ACCESS`, `Attribute.OPEN`, +//| `Attribute.ENCRYPT_NO_MITM`, `Attribute.ENCRYPT_WITH_MITM`, `Attribute.LESC_ENCRYPT_WITH_MITM`, +//| `Attribute.SIGNED_NO_MITM`, or `Attribute.SIGNED_WITH_MITM`. +//| :param int write_perm: Specifies whether the descriptor can be written by a client, and if so, which +//| security mode is required. Values allowed are the same as ``read_perm``. +//| :param int max_length: Maximum length in bytes of the descriptor value. The maximum allowed is +//| is 512, or possibly 510 if ``fixed_length`` is False. The default, 20, is the maximum +//| number of data bytes that fit in a single BLE 4.x ATT packet. +//| :param bool fixed_length: True if the descriptor value is of fixed length. +//| :param buf initial_value: The initial value for this descriptor. +//| +//| :return: the new `Descriptor`. +//| +STATIC mp_obj_t bleio_descriptor_add_to_characteristic(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + // class is arg[0], which we can ignore. + + enum { ARG_characteristic, ARG_uuid, ARG_read_perm, ARG_write_perm, + ARG_max_length, ARG_fixed_length, ARG_initial_value }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_characteristic, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_uuid, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_read_perm, MP_ARG_KW_ONLY| MP_ARG_INT, {.u_int = SECURITY_MODE_OPEN} }, + { MP_QSTR_write_perm, MP_ARG_KW_ONLY| MP_ARG_INT, {.u_int = SECURITY_MODE_OPEN} }, + { MP_QSTR_max_length, MP_ARG_KW_ONLY| MP_ARG_INT, {.u_int = 20} }, + { MP_QSTR_fixed_length, MP_ARG_KW_ONLY| MP_ARG_BOOL, {.u_bool = false} }, + { MP_QSTR_initial_value, MP_ARG_KW_ONLY| MP_ARG_OBJ, {.u_obj = mp_const_empty_bytes} }, + }; + + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + const mp_obj_t characteristic_obj = args[ARG_characteristic].u_obj; + if (!MP_OBJ_IS_TYPE(characteristic_obj, &bleio_characteristic_type)) { + mp_raise_ValueError(translate("Expected a Characteristic")); + } + + const mp_obj_t uuid_obj = args[ARG_uuid].u_obj; + if (!MP_OBJ_IS_TYPE(uuid_obj, &bleio_uuid_type)) { + mp_raise_ValueError(translate("Expected a UUID")); + } + + const bleio_attribute_security_mode_t read_perm = args[ARG_read_perm].u_int; + common_hal_bleio_attribute_security_mode_check_valid(read_perm); + + const bleio_attribute_security_mode_t write_perm = args[ARG_write_perm].u_int; + common_hal_bleio_attribute_security_mode_check_valid(write_perm); + + const mp_int_t max_length = args[ARG_max_length].u_int; + const bool fixed_length = args[ARG_fixed_length].u_bool; + mp_obj_t initial_value = args[ARG_initial_value].u_obj; + + // Length will be validated in common_hal. + mp_buffer_info_t initial_value_bufinfo; + if (initial_value == mp_const_none) { + if (fixed_length && max_length > 0) { + initial_value = mp_obj_new_bytes_of_zeros(max_length); + } else { + initial_value = mp_const_empty_bytes; + } + } + mp_get_buffer_raise(initial_value, &initial_value_bufinfo, MP_BUFFER_READ); + + bleio_descriptor_obj_t *descriptor = m_new_obj(bleio_descriptor_obj_t); + descriptor->base.type = &bleio_descriptor_type; + + // Range checking on max_length arg is done by the common_hal layer, because + // it may vary depending on underlying BLE implementation. + common_hal_bleio_descriptor_construct( + descriptor, MP_OBJ_TO_PTR(characteristic_obj), MP_OBJ_TO_PTR(uuid_obj), + read_perm, write_perm, + max_length, fixed_length, &initial_value_bufinfo); + + common_hal_bleio_characteristic_add_descriptor(characteristic_obj, descriptor); + + return MP_OBJ_FROM_PTR(descriptor); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(bleio_descriptor_add_to_characteristic_fun_obj, 3, bleio_descriptor_add_to_characteristic); +STATIC MP_DEFINE_CONST_CLASSMETHOD_OBJ(bleio_descriptor_add_to_characteristic_obj, MP_ROM_PTR(&bleio_descriptor_add_to_characteristic_fun_obj)); + //| .. attribute:: uuid //| //| The descriptor uuid. (read-only) @@ -115,7 +202,7 @@ const mp_obj_property_t bleio_descriptor_value_obj = { }; STATIC const mp_rom_map_elem_t bleio_descriptor_locals_dict_table[] = { - // Properties + { MP_ROM_QSTR(MP_QSTR_add_to_characteristic), MP_ROM_PTR(&bleio_descriptor_add_to_characteristic_obj) }, { MP_ROM_QSTR(MP_QSTR_uuid), MP_ROM_PTR(&bleio_descriptor_uuid_obj) }, { MP_ROM_QSTR(MP_QSTR_characteristic), MP_ROM_PTR(&bleio_descriptor_characteristic_obj) }, { MP_ROM_QSTR(MP_QSTR_value), MP_ROM_PTR(&bleio_descriptor_value_obj) }, diff --git a/shared-bindings/bleio/Descriptor.h b/shared-bindings/bleio/Descriptor.h index c6d49c06d6..d12ebaab99 100644 --- a/shared-bindings/bleio/Descriptor.h +++ b/shared-bindings/bleio/Descriptor.h @@ -35,7 +35,7 @@ extern const mp_obj_type_t bleio_descriptor_type; -extern void common_hal_bleio_descriptor_construct(bleio_descriptor_obj_t *self, bleio_uuid_obj_t *uuid, 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); +extern void common_hal_bleio_descriptor_construct(bleio_descriptor_obj_t *self, bleio_characteristic_obj_t *characteristic, bleio_uuid_obj_t *uuid, 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); extern bleio_uuid_obj_t *common_hal_bleio_descriptor_get_uuid(bleio_descriptor_obj_t *self); extern bleio_characteristic_obj_t *common_hal_bleio_descriptor_get_characteristic(bleio_descriptor_obj_t *self); extern mp_obj_t common_hal_bleio_descriptor_get_value(bleio_descriptor_obj_t *self); diff --git a/shared-bindings/bleio/Peripheral.c b/shared-bindings/bleio/Peripheral.c index 3a78802ac7..6ebc406d60 100644 --- a/shared-bindings/bleio/Peripheral.c +++ b/shared-bindings/bleio/Peripheral.c @@ -112,50 +112,6 @@ STATIC mp_obj_t bleio_peripheral_make_new(const mp_obj_type_t *type, size_t n_ar return MP_OBJ_FROM_PTR(self); } -//| .. method:: add_service(uuid, *, secondary=False) -//| -//| Create a new `Service` object, identitied by the specified UUID, and add it to this ``Peripheral``. -//| -//| To mark the service as secondary, pass `True` as :py:data:`secondary`. -//| -//| :param bleio.UUID uuid: The uuid of the service -//| :param bool secondary: If the service is a secondary one -// -//| :return: the new `Service` -//| -STATIC mp_obj_t bleio_peripheral_add_service(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - bleio_peripheral_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); - - enum { ARG_uuid, ARG_secondary }; - static const mp_arg_t allowed_args[] = { - { MP_QSTR_uuid, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = mp_const_none} }, - { MP_QSTR_secondary, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} }, - }; - - mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; - mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - - const mp_obj_t uuid_obj = args[ARG_uuid].u_obj; - - if (!MP_OBJ_IS_TYPE(uuid_obj, &bleio_uuid_type)) { - mp_raise_ValueError(translate("Expected a UUID")); - } - - const bool is_secondary = args[ARG_secondary].u_bool; - bleio_uuid_obj_t *uuid = MP_OBJ_TO_PTR(uuid_obj); - - bleio_service_obj_t *service = m_new_obj(bleio_service_obj_t); - service->base.type = &bleio_service_type; - - common_hal_bleio_service_construct(service, uuid, is_secondary); - - common_hal_bleio_peripheral_add_service(self, service); - - return MP_OBJ_FROM_PTR(service); -} -STATIC MP_DEFINE_CONST_FUN_OBJ_KW(bleio_peripheral_add_service_obj, 2, bleio_peripheral_add_service); - - //| .. attribute:: connected (read-only) //| //| True if connected to a BLE Central device. @@ -347,7 +303,6 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_1(bleio_peripheral_pair_obj, bleio_peripheral_pai STATIC const mp_rom_map_elem_t bleio_peripheral_locals_dict_table[] = { // Methods - { MP_ROM_QSTR(MP_QSTR_add_service), MP_ROM_PTR(&bleio_peripheral_add_service_obj) }, { MP_ROM_QSTR(MP_QSTR_start_advertising), MP_ROM_PTR(&bleio_peripheral_start_advertising_obj) }, { MP_ROM_QSTR(MP_QSTR_stop_advertising), MP_ROM_PTR(&bleio_peripheral_stop_advertising_obj) }, { MP_ROM_QSTR(MP_QSTR_disconnect), MP_ROM_PTR(&bleio_peripheral_disconnect_obj) }, diff --git a/shared-bindings/bleio/Service.c b/shared-bindings/bleio/Service.c index 2497e9d475..134106915e 100644 --- a/shared-bindings/bleio/Service.c +++ b/shared-bindings/bleio/Service.c @@ -29,6 +29,7 @@ #include "py/objproperty.h" #include "py/runtime.h" #include "shared-bindings/bleio/Characteristic.h" +#include "shared-bindings/bleio/Peripheral.h" #include "shared-bindings/bleio/Service.h" #include "shared-bindings/bleio/UUID.h" @@ -39,12 +40,63 @@ //| //| Stores information about a BLE service and its characteristics. //| -//| A Service cannot be created directly. A new local Service can be created -//| and attached to a Peripheral by calling `Peripheral.add_service()`. +//| There is no regular constructor for a Service. A new local Service can be created +//| and attached to a Peripheral by calling `Service.add_to_peripheral()`. //| Remote Service objects are created by `Central.discover_remote_services()` //| or `Peripheral.discover_remote_services()`. //| +//| .. classmethod:: add_to_peripheral(peripheral, uuid, *, secondary=False) +//| +//| Create a new `Service` object, identitied by the specified UUID, and add it +//| to the given Peripheral. +//| +//| To mark the service as secondary, pass `True` as :py:data:`secondary`. +//| +//| :param bleio.Peripheral peripheral: The peripheral that will provide this service +//| :param bleio.UUID uuid: The uuid of the service +//| :param bool secondary: If the service is a secondary one +// +//| :return: the new `Service` +//| +STATIC mp_obj_t bleio_service_add_to_peripheral(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + // class is arg[0], which we can ignore. + + enum { ARG_peripheral, ARG_uuid, ARG_secondary }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_peripheral, MP_ARG_REQUIRED | MP_ARG_OBJ,}, + { MP_QSTR_uuid, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_secondary, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} }, + }; + + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + const mp_obj_t peripheral_obj = args[ARG_peripheral].u_obj; + if (!MP_OBJ_IS_TYPE(peripheral_obj, &bleio_peripheral_type)) { + mp_raise_ValueError(translate("Expected a Peripheral")); + } + + const mp_obj_t uuid_obj = args[ARG_uuid].u_obj; + if (!MP_OBJ_IS_TYPE(uuid_obj, &bleio_uuid_type)) { + mp_raise_ValueError(translate("Expected a UUID")); + } + + const bool is_secondary = args[ARG_secondary].u_bool; + + bleio_service_obj_t *service = m_new_obj(bleio_service_obj_t); + service->base.type = &bleio_service_type; + + common_hal_bleio_service_construct( + service, MP_OBJ_TO_PTR(peripheral_obj), MP_OBJ_TO_PTR(uuid_obj), is_secondary); + + common_hal_bleio_peripheral_add_service(peripheral_obj, service); + + return MP_OBJ_FROM_PTR(service); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(bleio_service_add_to_peripheral_fun_obj, 3, bleio_service_add_to_peripheral); +STATIC MP_DEFINE_CONST_CLASSMETHOD_OBJ(bleio_service_add_to_peripheral_obj, MP_ROM_PTR(&bleio_service_add_to_peripheral_fun_obj)); + //| .. attribute:: characteristics //| //| A tuple of `bleio.Characteristic` that are offered by this service. (read-only) @@ -120,101 +172,12 @@ const mp_obj_property_t bleio_service_uuid_obj = { (mp_obj_t)&mp_const_none_obj }, }; -//| .. method:: add_characteristic(uuid, *, properties=0, read_perm=`Attribute.OPEN`, write_perm=`Attribute.OPEN`, max_length=20, fixed_length=False, initial_value=None) -//| -//| Create a new `Characteristic` object, and add it to this Service. -//| -//| :param bleio.UUID uuid: The uuid of the characteristic -//| :param int properties: The properties of the characteristic, -//| specified as a bitmask of these values bitwise-or'd together: -//| `Characteristic.BROADCAST`, `Characteristic.INDICATE`, `Characteristic.NOTIFY`, -//| `Characteristic.READ`, `Characteristic.WRITE`, `Characteristic.WRITE_NO_RESPONSE`. -//| :param int read_perm: Specifies whether the characteristic can be read by a client, and if so, which -//| security mode is required. Must be one of the integer values `Attribute.NO_ACCESS`, `Attribute.OPEN`, -//| `Attribute.ENCRYPT_NO_MITM`, `Attribute.ENCRYPT_WITH_MITM`, `Attribute.LESC_ENCRYPT_WITH_MITM`, -//| `Attribute.SIGNED_NO_MITM`, or `Attribute.SIGNED_WITH_MITM`. -//| :param int write_perm: Specifies whether the characteristic can be written by a client, and if so, which -//| security mode is required. Values allowed are the same as ``read_perm``. -//| :param int max_length: Maximum length in bytes of the characteristic value. The maximum allowed is -//| is 512, or possibly 510 if ``fixed_length`` is False. The default, 20, is the maximum -//| number of data bytes that fit in a single BLE 4.x ATT packet. -//| :param bool fixed_length: True if the characteristic value is of fixed length. -//| :param buf initial_value: The initial value for this characteristic. If not given, will be -//| filled with zeros. -//| -//| :return: the new `Characteristic`. -//| -STATIC mp_obj_t bleio_service_add_characteristic(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - bleio_service_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); - - enum { ARG_uuid, ARG_properties, ARG_read_perm, ARG_write_perm, - ARG_max_length, ARG_fixed_length, ARG_initial_value }; - static const mp_arg_t allowed_args[] = { - { MP_QSTR_uuid, MP_ARG_REQUIRED | MP_ARG_OBJ }, - { MP_QSTR_properties, MP_ARG_KW_ONLY| MP_ARG_INT, {.u_int = 0} }, - { MP_QSTR_read_perm, MP_ARG_KW_ONLY| MP_ARG_INT, {.u_int = SECURITY_MODE_OPEN} }, - { MP_QSTR_write_perm, MP_ARG_KW_ONLY| MP_ARG_INT, {.u_int = SECURITY_MODE_OPEN} }, - { MP_QSTR_max_length, MP_ARG_KW_ONLY| MP_ARG_INT, {.u_int = 20} }, - { MP_QSTR_fixed_length, MP_ARG_KW_ONLY| MP_ARG_BOOL, {.u_bool = false} }, - { MP_QSTR_initial_value, MP_ARG_KW_ONLY| MP_ARG_OBJ, {.u_obj = mp_const_none} }, - }; - - mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; - mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - - const mp_obj_t uuid_obj = args[ARG_uuid].u_obj; - - if (!MP_OBJ_IS_TYPE(uuid_obj, &bleio_uuid_type)) { - mp_raise_ValueError(translate("Expected a UUID")); - } - bleio_uuid_obj_t *uuid = MP_OBJ_TO_PTR(uuid_obj); - - const bleio_characteristic_properties_t properties = args[ARG_properties].u_int; - if (properties & ~CHAR_PROP_ALL) { - mp_raise_ValueError(translate("Invalid properties")); - } - - const bleio_attribute_security_mode_t read_perm = args[ARG_read_perm].u_int; - common_hal_bleio_attribute_security_mode_check_valid(read_perm); - - const bleio_attribute_security_mode_t write_perm = args[ARG_write_perm].u_int; - common_hal_bleio_attribute_security_mode_check_valid(write_perm); - - const mp_int_t max_length = args[ARG_max_length].u_int; - const bool fixed_length = args[ARG_fixed_length].u_bool; - mp_obj_t initial_value = args[ARG_initial_value].u_obj; - - // Length will be validated in common_hal. - mp_buffer_info_t initial_value_bufinfo; - if (initial_value == mp_const_none) { - if (fixed_length && max_length > 0) { - initial_value = mp_obj_new_bytes_of_zeros(max_length); - } else { - initial_value = mp_const_empty_bytes; - } - } - mp_get_buffer_raise(initial_value, &initial_value_bufinfo, MP_BUFFER_READ); - - bleio_characteristic_obj_t *characteristic = m_new_obj(bleio_characteristic_obj_t); - characteristic->base.type = &bleio_characteristic_type; - - // Range checking on max_length arg is done by the common_hal layer, because - // it may vary depending on underlying BLE implementation. - common_hal_bleio_characteristic_construct( - characteristic, uuid, properties, read_perm, write_perm, - max_length, fixed_length, &initial_value_bufinfo); - - common_hal_bleio_service_add_characteristic(self, characteristic); - - return MP_OBJ_FROM_PTR(characteristic); -} -STATIC MP_DEFINE_CONST_FUN_OBJ_KW(bleio_service_add_characteristic_obj, 2, bleio_service_add_characteristic); STATIC const mp_rom_map_elem_t bleio_service_locals_dict_table[] = { - { MP_ROM_QSTR(MP_QSTR_characteristics), MP_ROM_PTR(&bleio_service_characteristics_obj) }, - { MP_ROM_QSTR(MP_QSTR_secondary), MP_ROM_PTR(&bleio_service_secondary_obj) }, - { MP_ROM_QSTR(MP_QSTR_uuid), MP_ROM_PTR(&bleio_service_uuid_obj) }, - { MP_ROM_QSTR(MP_QSTR_add_characteristic), MP_ROM_PTR(&bleio_service_add_characteristic_obj) }, + { MP_ROM_QSTR(MP_QSTR_add_to_peripheral), MP_ROM_PTR(&bleio_service_add_to_peripheral_obj) }, + { MP_ROM_QSTR(MP_QSTR_characteristics), MP_ROM_PTR(&bleio_service_characteristics_obj) }, + { MP_ROM_QSTR(MP_QSTR_secondary), MP_ROM_PTR(&bleio_service_secondary_obj) }, + { MP_ROM_QSTR(MP_QSTR_uuid), MP_ROM_PTR(&bleio_service_uuid_obj) }, }; STATIC MP_DEFINE_CONST_DICT(bleio_service_locals_dict, bleio_service_locals_dict_table); diff --git a/shared-bindings/bleio/Service.h b/shared-bindings/bleio/Service.h index 91c08fbb89..ce4e514176 100644 --- a/shared-bindings/bleio/Service.h +++ b/shared-bindings/bleio/Service.h @@ -28,11 +28,12 @@ #ifndef MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_SERVICE_H #define MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_SERVICE_H +#include "common-hal/bleio/Peripheral.h" #include "common-hal/bleio/Service.h" const mp_obj_type_t bleio_service_type; -extern void common_hal_bleio_service_construct(bleio_service_obj_t *self, bleio_uuid_obj_t *uuid, bool is_secondary); +extern void common_hal_bleio_service_construct(bleio_service_obj_t *self, bleio_peripheral_obj_t *peripheral, bleio_uuid_obj_t *uuid, bool is_secondary); extern bleio_uuid_obj_t *common_hal_bleio_service_get_uuid(bleio_service_obj_t *self); extern mp_obj_list_t *common_hal_bleio_service_get_characteristic_list(bleio_service_obj_t *self); extern bool common_hal_bleio_service_get_is_remote(bleio_service_obj_t *self);