@@ -42,40 +42,22 @@ void common_hal_bleio_characteristic_construct(bleio_characteristic_obj_t *self,
4242 bleio_attribute_security_mode_t read_perm , bleio_attribute_security_mode_t write_perm ,
4343 mp_int_t max_length , bool fixed_length , mp_buffer_info_t * initial_value_bufinfo ,
4444 const char * user_description ) {
45- mp_raise_NotImplementedError (NULL );
4645 self -> service = service ;
4746 self -> uuid = uuid ;
4847 self -> handle = BLEIO_HANDLE_INVALID ;
48+ self -> cccd_handle = BLEIO_HANDLE_INVALID ;
49+ self -> sccd_handle = BLEIO_HANDLE_INVALID ;
4950 self -> props = props ;
5051 self -> read_perm = read_perm ;
5152 self -> write_perm = write_perm ;
52- self -> initial_value_len = 0 ;
53- self -> initial_value = NULL ;
54- if (initial_value_bufinfo != NULL ) {
55- // Copy the initial value if it's on the heap. Otherwise it's internal and we may not be able
56- // to allocate.
57- self -> initial_value_len = initial_value_bufinfo -> len ;
58- if (gc_alloc_possible ()) {
59- if (gc_nbytes (initial_value_bufinfo -> buf ) > 0 ) {
60- uint8_t * initial_value = m_malloc (self -> initial_value_len , false);
61- memcpy (initial_value , initial_value_bufinfo -> buf , self -> initial_value_len );
62- self -> initial_value = initial_value ;
63- } else {
64- self -> initial_value = initial_value_bufinfo -> buf ;
65- }
66- self -> descriptor_list = mp_obj_new_list (0 , NULL );
67- } else {
68- self -> initial_value = initial_value_bufinfo -> buf ;
69- self -> descriptor_list = NULL ;
70- }
53+ common_hal_bleio_characteristic_set_value (self , initial_value_bufinfo );
54+
55+ if (gc_alloc_possible ()) {
56+ self -> descriptor_list = mp_obj_new_list (0 , NULL );
57+ } else {
58+ self -> descriptor_list = NULL ;
7159 }
7260
73- // const mp_int_t max_length_max = fixed_length ? BLE_GATTS_FIX_ATTR_LEN_MAX : BLE_GATTS_VAR_ATTR_LEN_MAX;
74- // if (max_length < 0 || max_length > max_length_max) {
75- // mp_raise_ValueError_varg(translate("max_length must be 0-%d when fixed_length is %s"),
76- // max_length_max, fixed_length ? "True" : "False");
77- // }
78- // TODO: Implement this.
7961 self -> max_length = max_length ;
8062 self -> fixed_length = fixed_length ;
8163
@@ -97,17 +79,123 @@ bleio_service_obj_t *common_hal_bleio_characteristic_get_service(bleio_character
9779 return self -> service ;
9880}
9981
82+ typedef struct {
83+ TaskHandle_t task ;
84+ uint8_t * buf ;
85+ uint16_t len ;
86+ } _read_info_t ;
87+
88+ STATIC int _read_cb (uint16_t conn_handle ,
89+ const struct ble_gatt_error * error ,
90+ struct ble_gatt_attr * attr ,
91+ void * arg ) {
92+ _read_info_t * read_info = (_read_info_t * )arg ;
93+ switch (error -> status ) {
94+ case 0 : {
95+ int len = MIN (read_info -> len , OS_MBUF_PKTLEN (attr -> om ));
96+ os_mbuf_copydata (attr -> om , attr -> offset , len , read_info -> buf );
97+ read_info -> len = len ;
98+ }
99+ MP_FALLTHROUGH ;
100+
101+ default :
102+ #if CIRCUITPY_VERBOSE_BLE
103+ // For debugging.
104+ mp_printf (& mp_plat_print , "Read status: %d\n" , error -> status );
105+ #endif
106+ xTaskNotify (read_info -> task , error -> status , eSetValueWithOverwrite );
107+ break ;
108+ }
109+
110+ return 0 ;
111+ }
112+
100113size_t common_hal_bleio_characteristic_get_value (bleio_characteristic_obj_t * self , uint8_t * buf , size_t len ) {
101- // TODO: Implement this.
114+ // Do GATT operations only if this characteristic has been added to a registered service.
115+ if (self -> handle == BLEIO_HANDLE_INVALID ) {
116+ return 0 ;
117+ }
118+ uint16_t conn_handle = bleio_connection_get_conn_handle (self -> service -> connection );
119+ if (common_hal_bleio_service_get_is_remote (self -> service )) {
120+ _read_info_t read_info = {
121+ .task = xTaskGetCurrentTaskHandle (),
122+ .buf = buf ,
123+ .len = len
124+ };
125+ CHECK_NIMBLE_ERROR (ble_gattc_read (conn_handle , self -> handle , _read_cb , & read_info ));
126+ int error_code ;
127+ xTaskNotifyWait (0 , 0 , (uint32_t * )& error_code , 200 );
128+ CHECK_BLE_ERROR (error_code );
129+ return read_info .len ;
130+ } else {
131+ len = MIN (self -> current_value_len , len );
132+ memcpy (buf , self -> current_value , len );
133+ return len ;
134+ }
135+
102136 return 0 ;
103137}
104138
105139size_t common_hal_bleio_characteristic_get_max_length (bleio_characteristic_obj_t * self ) {
106140 return self -> max_length ;
107141}
108142
143+ STATIC int _write_cb (uint16_t conn_handle ,
144+ const struct ble_gatt_error * error ,
145+ struct ble_gatt_attr * attr ,
146+ void * arg ) {
147+ TaskHandle_t task = (TaskHandle_t )arg ;
148+ xTaskNotify (task , error -> status , eSetValueWithOverwrite );
149+
150+ return 0 ;
151+ }
152+
109153void common_hal_bleio_characteristic_set_value (bleio_characteristic_obj_t * self , mp_buffer_info_t * bufinfo ) {
110- // TODO: Implement this.
154+ if (common_hal_bleio_service_get_is_remote (self -> service )) {
155+ uint16_t conn_handle = bleio_connection_get_conn_handle (self -> service -> connection );
156+ if ((self -> props & CHAR_PROP_WRITE_NO_RESPONSE ) != 0 ) {
157+ CHECK_NIMBLE_ERROR (ble_gattc_write_no_rsp_flat (conn_handle , self -> handle , bufinfo -> buf , bufinfo -> len ));
158+ } else {
159+ CHECK_NIMBLE_ERROR (ble_gattc_write_flat (conn_handle , self -> handle , bufinfo -> buf , bufinfo -> len , _write_cb , xTaskGetCurrentTaskHandle ()));
160+ int error_code ;
161+ xTaskNotifyWait (0 , 0 , (uint32_t * )& error_code , 200 );
162+ CHECK_BLE_ERROR (error_code );
163+ }
164+ } else {
165+ // Validate data length for local characteristics only.
166+ // TODO: Test this once we can get servers going.
167+ if (self -> fixed_length && bufinfo -> len != self -> max_length ) {
168+ mp_raise_ValueError (translate ("Value length != required fixed length" ));
169+ }
170+ if (bufinfo -> len > self -> max_length ) {
171+ mp_raise_ValueError (translate ("Value length > max_length" ));
172+ }
173+
174+ if (bufinfo == NULL ) {
175+ self -> current_value_len = 0 ;
176+ ble_gatts_chr_updated (self -> handle );
177+ return ;
178+ }
179+
180+ self -> current_value_len = bufinfo -> len ;
181+ // If we've already allocated an internal buffer or the provided buffer
182+ // is on the heap, then copy into the internal buffer.
183+ if (self -> current_value_alloc > 0 || gc_nbytes (bufinfo -> buf ) > 0 ) {
184+ if (self -> current_value_alloc < bufinfo -> len ) {
185+ self -> current_value = m_realloc (self -> current_value , bufinfo -> len );
186+ // Get the number of bytes from the heap because it may be more
187+ // than the len due to gc block size.
188+ self -> current_value_alloc = gc_nbytes (self -> current_value );
189+ }
190+ memcpy (self -> current_value , bufinfo -> buf , bufinfo -> len );
191+ } else {
192+ // Otherwise, use the provided buffer to delay any heap allocation.
193+ self -> current_value = bufinfo -> buf ;
194+ self -> current_value_alloc = 0 ;
195+ }
196+
197+ ble_gatts_chr_updated (self -> handle );
198+ }
111199}
112200
113201bleio_uuid_obj_t * common_hal_bleio_characteristic_get_uuid (bleio_characteristic_obj_t * self ) {
@@ -118,10 +206,32 @@ bleio_characteristic_properties_t common_hal_bleio_characteristic_get_properties
118206 return self -> props ;
119207}
120208
121- void common_hal_bleio_characteristic_add_descriptor (bleio_characteristic_obj_t * self , bleio_descriptor_obj_t * descriptor ) {
209+ void common_hal_bleio_characteristic_add_descriptor (bleio_characteristic_obj_t * self ,
210+ bleio_descriptor_obj_t * descriptor ) {
122211 // TODO: Implement this.
212+
213+ mp_obj_list_append (MP_OBJ_FROM_PTR (self -> descriptor_list ),
214+ MP_OBJ_FROM_PTR (descriptor ));
123215}
124216
125217void common_hal_bleio_characteristic_set_cccd (bleio_characteristic_obj_t * self , bool notify , bool indicate ) {
126- // TODO: Implement this.
218+ if (self -> cccd_handle == BLEIO_HANDLE_INVALID ) {
219+ mp_raise_bleio_BluetoothError (translate ("No CCCD for this Characteristic" ));
220+ }
221+
222+ if (!common_hal_bleio_service_get_is_remote (self -> service )) {
223+ mp_raise_bleio_RoleError (translate ("Can't set CCCD on local Characteristic" ));
224+ }
225+
226+ const uint16_t conn_handle = bleio_connection_get_conn_handle (self -> service -> connection );
227+ common_hal_bleio_check_connected (conn_handle );
228+
229+ uint16_t cccd_value =
230+ (notify ? 1 << 0 : 0 ) |
231+ (indicate ? 1 << 1 : 0 );
232+
233+ CHECK_NIMBLE_ERROR (ble_gattc_write_flat (conn_handle , self -> cccd_handle , & cccd_value , 2 , _write_cb , xTaskGetCurrentTaskHandle ()));
234+ int error_code ;
235+ xTaskNotifyWait (0 , 0 , (uint32_t * )& error_code , 200 );
236+ CHECK_BLE_ERROR (error_code );
127237}
0 commit comments