55//
66// SPDX-License-Identifier: MIT
77
8+ #include <string.h>
9+
10+ #include <zephyr/bluetooth/gatt.h>
11+
812#include "py/runtime.h"
913#include "shared-bindings/_bleio/Characteristic.h"
1014#include "shared-bindings/_bleio/Descriptor.h"
1115#include "shared-bindings/_bleio/Service.h"
16+ #include "common-hal/_bleio/__init__.h"
17+
18+ uint16_t bleio_security_to_zephyr_perm (
19+ bleio_attribute_security_mode_t read_perm ,
20+ bleio_attribute_security_mode_t write_perm ,
21+ bleio_characteristic_properties_t props ) {
22+ uint16_t perm = 0 ;
23+
24+ if (props & CHAR_PROP_READ ) {
25+ switch (read_perm ) {
26+ case SECURITY_MODE_OPEN :
27+ perm |= BT_GATT_PERM_READ ;
28+ break ;
29+ case SECURITY_MODE_ENC_NO_MITM :
30+ perm |= BT_GATT_PERM_READ_ENCRYPT ;
31+ break ;
32+ case SECURITY_MODE_ENC_WITH_MITM :
33+ perm |= BT_GATT_PERM_READ_AUTHEN ;
34+ break ;
35+ case SECURITY_MODE_LESC_ENC_WITH_MITM :
36+ perm |= BT_GATT_PERM_READ_LESC ;
37+ break ;
38+ default :
39+ break ;
40+ }
41+ }
42+
43+ if (props & (CHAR_PROP_WRITE | CHAR_PROP_WRITE_NO_RESPONSE )) {
44+ switch (write_perm ) {
45+ case SECURITY_MODE_OPEN :
46+ perm |= BT_GATT_PERM_WRITE ;
47+ break ;
48+ case SECURITY_MODE_ENC_NO_MITM :
49+ perm |= BT_GATT_PERM_WRITE_ENCRYPT ;
50+ break ;
51+ case SECURITY_MODE_ENC_WITH_MITM :
52+ perm |= BT_GATT_PERM_WRITE_AUTHEN ;
53+ break ;
54+ case SECURITY_MODE_LESC_ENC_WITH_MITM :
55+ perm |= BT_GATT_PERM_WRITE_LESC ;
56+ break ;
57+ default :
58+ break ;
59+ }
60+ }
61+
62+ return perm ;
63+ }
64+
65+ ssize_t bleio_char_read_cb (struct bt_conn * conn ,
66+ const struct bt_gatt_attr * attr , void * buf , uint16_t len , uint16_t offset ) {
67+ bleio_characteristic_obj_t * self = attr -> user_data ;
68+ return bt_gatt_attr_read (conn , attr , buf , len , offset ,
69+ self -> current_value , self -> current_value_len );
70+ }
71+
72+ ssize_t bleio_char_write_cb (struct bt_conn * conn ,
73+ const struct bt_gatt_attr * attr , const void * buf , uint16_t len ,
74+ uint16_t offset , uint8_t flags ) {
75+ bleio_characteristic_obj_t * self = attr -> user_data ;
76+ if (offset + len > self -> max_length ) {
77+ return BT_GATT_ERR (BT_ATT_ERR_INVALID_OFFSET );
78+ }
79+ memcpy (self -> current_value + offset , buf , len );
80+ if (offset + len > self -> current_value_len ) {
81+ self -> current_value_len = offset + len ;
82+ }
83+ return len ;
84+ }
85+
86+ void bleio_ccc_changed_cb (const struct bt_gatt_attr * attr , uint16_t value ) {
87+ // Track subscription state if needed in the future.
88+ (void )attr ;
89+ (void )value ;
90+ }
1291
1392bleio_characteristic_properties_t common_hal_bleio_characteristic_get_properties (bleio_characteristic_obj_t * self ) {
1493 return self -> props ;
@@ -31,31 +110,95 @@ size_t common_hal_bleio_characteristic_get_max_length(bleio_characteristic_obj_t
31110}
32111
33112size_t common_hal_bleio_characteristic_get_value (bleio_characteristic_obj_t * self , uint8_t * buf , size_t len ) {
34- mp_raise_NotImplementedError (NULL );
113+ size_t copy_len = self -> current_value_len ;
114+ if (copy_len > len ) {
115+ copy_len = len ;
116+ }
117+ memcpy (buf , self -> current_value , copy_len );
118+ return copy_len ;
35119}
36120
37- void common_hal_bleio_characteristic_add_descriptor (bleio_characteristic_obj_t * self , bleio_descriptor_obj_t * descriptor ) {
38- mp_raise_NotImplementedError (NULL );
39- }
121+ void common_hal_bleio_characteristic_construct (bleio_characteristic_obj_t * self ,
122+ bleio_service_obj_t * service , uint16_t handle , bleio_uuid_obj_t * uuid ,
123+ bleio_characteristic_properties_t props ,
124+ bleio_attribute_security_mode_t read_perm ,
125+ bleio_attribute_security_mode_t write_perm ,
126+ mp_int_t max_length , bool fixed_length ,
127+ mp_buffer_info_t * initial_value_bufinfo ,
128+ const char * user_description ) {
40129
41- 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 ) {
42- mp_raise_NotImplementedError (NULL );
130+ self -> service = service ;
131+ self -> uuid = uuid ;
132+ self -> handle = handle ;
133+ self -> props = props ;
134+ self -> read_perm = read_perm ;
135+ self -> write_perm = write_perm ;
136+ self -> max_length = max_length ;
137+ self -> fixed_length = fixed_length ;
138+ self -> observer = mp_const_none ;
139+ self -> descriptor_list = mp_obj_new_list (0 , NULL );
140+
141+ // Allocate value buffer
142+ self -> current_value = m_malloc (max_length );
143+ memset (self -> current_value , 0 , max_length );
144+ self -> current_value_alloc = max_length ;
145+ self -> current_value_len = 0 ;
146+
147+ // Copy initial value if provided
148+ if (initial_value_bufinfo != NULL && initial_value_bufinfo -> len > 0 ) {
149+ size_t len = initial_value_bufinfo -> len ;
150+ if (len > (size_t )max_length ) {
151+ len = max_length ;
152+ }
153+ memcpy (self -> current_value , initial_value_bufinfo -> buf , len );
154+ self -> current_value_len = len ;
155+ }
156+
157+ // Convert UUID to Zephyr format
158+ bleio_uuid_to_zephyr (uuid , & self -> zephyr_uuid );
159+
160+ if (!service -> is_remote ) {
161+ common_hal_bleio_service_add_characteristic (service , self ,
162+ initial_value_bufinfo , user_description );
163+ }
43164}
44165
45166bool common_hal_bleio_characteristic_deinited (bleio_characteristic_obj_t * self ) {
46167 return self -> service == NULL ;
47168}
48169
49170void common_hal_bleio_characteristic_deinit (bleio_characteristic_obj_t * self ) {
50- // Nothing to do
171+ // Nothing to do - service handles unregistration
51172}
52173
53174void common_hal_bleio_characteristic_set_cccd (bleio_characteristic_obj_t * self , bool notify , bool indicate ) {
175+ // Client-side only operation
54176 mp_raise_NotImplementedError (NULL );
55177}
56178
57179void common_hal_bleio_characteristic_set_value (bleio_characteristic_obj_t * self , mp_buffer_info_t * bufinfo ) {
58- mp_raise_NotImplementedError (NULL );
180+ size_t len = bufinfo -> len ;
181+ if (len > self -> max_length ) {
182+ len = self -> max_length ;
183+ }
184+ memcpy (self -> current_value , bufinfo -> buf , len );
185+ self -> current_value_len = len ;
186+
187+ // If NOTIFY and service is registered, send notification
188+ if ((self -> props & CHAR_PROP_NOTIFY ) && self -> service != NULL &&
189+ self -> service -> registered ) {
190+ bt_gatt_notify (NULL , & self -> service -> attrs [self -> value_attr_index ],
191+ self -> current_value , self -> current_value_len );
192+ }
193+ }
194+
195+ void common_hal_bleio_characteristic_add_descriptor (bleio_characteristic_obj_t * self ,
196+ bleio_descriptor_obj_t * descriptor ) {
197+ mp_obj_list_append (MP_OBJ_FROM_PTR (self -> descriptor_list ),
198+ MP_OBJ_FROM_PTR (descriptor ));
199+ // Descriptors added after characteristic construction would need
200+ // service re-registration; for now the common case is handled by
201+ // Service.add_characteristic which adds descriptors at registration time.
59202}
60203
61204void bleio_characteristic_set_observer (bleio_characteristic_obj_t * self , mp_obj_t observer ) {
0 commit comments