2626
2727#include " nRF5xn.h"
2828
29+ namespace {
30+
31+ static const ble_gatts_rw_authorize_reply_params_t write_auth_queue_full_reply = {
32+ .type = BLE_GATTS_AUTHORIZE_TYPE_WRITE,
33+ .params = {
34+ .write = {
35+ .gatt_status = BLE_GATT_STATUS_ATTERR_PREPARE_QUEUE_FULL
36+ }
37+ }
38+ };
39+
40+ static const ble_gatts_rw_authorize_reply_params_t write_auth_invalid_offset_reply = {
41+ .type = BLE_GATTS_AUTHORIZE_TYPE_WRITE,
42+ .params = {
43+ .write = {
44+ .gatt_status = BLE_GATT_STATUS_ATTERR_INVALID_OFFSET
45+ }
46+ }
47+ };
48+
49+ static const ble_gatts_rw_authorize_reply_params_t write_auth_succes_reply = {
50+ .type = BLE_GATTS_AUTHORIZE_TYPE_WRITE,
51+ .params = {
52+ .write = {
53+ .gatt_status = BLE_GATT_STATUS_SUCCESS,
54+ .update = 0
55+ }
56+ }
57+ };
58+
59+ static const ble_gatts_rw_authorize_reply_params_t write_auth_invalid_reply = {
60+ .type = BLE_GATTS_AUTHORIZE_TYPE_WRITE,
61+ .params = {
62+ .write = {
63+ .gatt_status = BLE_GATT_STATUS_ATTERR_INVALID
64+ }
65+ }
66+ };
67+
68+ }
69+
2970/* *************************************************************************/
3071/* !
3172 @brief Adds a new service to the GATT table on the peripheral
@@ -354,6 +395,8 @@ ble_error_t nRF5xGattServer::reset(void)
354395 memset (nrfDescriptorHandles, 0 , sizeof (nrfDescriptorHandles));
355396 descriptorCount = 0 ;
356397
398+ releaseAllWriteRequests ();
399+
357400 return BLE_ERROR_NONE;
358401}
359402
@@ -427,13 +470,33 @@ void nRF5xGattServer::hwCallback(ble_evt_t *p_ble_evt)
427470 }
428471 break ;
429472
473+ case BLE_EVT_USER_MEM_REQUEST: {
474+ uint16_t conn_handle = p_ble_evt->evt .common_evt .conn_handle ;
475+
476+ // allocate a new long request for this connection
477+ // NOTE: we don't care about the result at this stage,
478+ // it is not possible to cancel the operation anyway.
479+ // If the request was not allocated then it will gracefully failled
480+ // at subsequent stages.
481+ allocateLongWriteRequest (conn_handle);
482+ sd_ble_user_mem_reply (conn_handle, NULL );
483+ return ;
484+ }
485+
430486 default :
431487 return ;
432488 }
433489
434490 int characteristicIndex = resolveValueHandleToCharIndex (handle_value);
435491 if (characteristicIndex == -1 ) {
436- return ;
492+ // filter out the case were the request is a long one,
493+ // and there is no attribute handle provided
494+ uint8_t write_op = gattsEventP->params .authorize_request .request .write .op ;
495+ if (eventType != GattServerEvents::GATT_EVENT_WRITE_AUTHORIZATION_REQ ||
496+ (write_op != BLE_GATTS_OP_EXEC_WRITE_REQ_NOW &&
497+ write_op != BLE_GATTS_OP_EXEC_WRITE_REQ_CANCEL)) {
498+ return ;
499+ }
437500 }
438501
439502 /* Find index (charHandle) in the pool */
@@ -451,6 +514,131 @@ void nRF5xGattServer::hwCallback(ble_evt_t *p_ble_evt)
451514 break ;
452515 }
453516 case GattServerEvents::GATT_EVENT_WRITE_AUTHORIZATION_REQ: {
517+ uint16_t conn_handle = gattsEventP->conn_handle ;
518+ const ble_gatts_evt_write_t & input_req = gattsEventP->params .authorize_request .request .write ;
519+ const uint16_t max_size = getBiggestCharacteristicSize ();
520+
521+ // this is a long write request, handle it here.
522+ switch (input_req.op ) {
523+ case BLE_GATTS_OP_PREP_WRITE_REQ: {
524+ // verify that the request is not outside of the possible range
525+ if ((input_req.offset + input_req.len ) > max_size) {
526+ sd_ble_gatts_rw_authorize_reply (conn_handle, &write_auth_invalid_offset_reply);
527+ releaseLongWriteRequest (conn_handle);
528+ return ;
529+ }
530+
531+ // find the write request
532+ long_write_request_t * req = findLongWriteRequest (conn_handle);
533+ if (!req) {
534+ sd_ble_gatts_rw_authorize_reply (conn_handle, &write_auth_invalid_reply);
535+ return ;
536+ }
537+
538+ // initialize the first request by setting the offset
539+ if (req->length == 0 ) {
540+ req->attr_handle = input_req.handle ;
541+ req->offset = input_req.offset ;
542+ } else {
543+ // it should be the subsequent write
544+ if ((req->offset + req->length ) != input_req.offset ) {
545+ sd_ble_gatts_rw_authorize_reply (conn_handle, &write_auth_invalid_offset_reply);
546+ releaseLongWriteRequest (conn_handle);
547+ return ;
548+ }
549+
550+ // it is not allowed to write multiple characteristic with the same request
551+ if (input_req.handle != req->attr_handle ) {
552+ sd_ble_gatts_rw_authorize_reply (conn_handle, &write_auth_invalid_reply);
553+ releaseLongWriteRequest (conn_handle);
554+ return ;
555+ }
556+ }
557+
558+ // start the copy of what is in input
559+ memcpy (req->data + req->length , input_req.data , input_req.len );
560+
561+ // update the lenght of the data written
562+ req->length = req->length + input_req.len ;
563+
564+ // success, signal it to the softdevice
565+ ble_gatts_rw_authorize_reply_params_t reply = {
566+ .type = BLE_GATTS_AUTHORIZE_TYPE_WRITE,
567+ .params = {
568+ .write = {
569+ .gatt_status = BLE_GATT_STATUS_SUCCESS,
570+ .update = 1 ,
571+ .offset = input_req.offset ,
572+ .len = input_req.len ,
573+ .p_data = input_req.data
574+ }
575+ }
576+ };
577+
578+ sd_ble_gatts_rw_authorize_reply (conn_handle, &reply);
579+ } return ;
580+
581+ case BLE_GATTS_OP_EXEC_WRITE_REQ_CANCEL: {
582+ releaseLongWriteRequest (conn_handle);
583+ sd_ble_gatts_rw_authorize_reply (conn_handle, &write_auth_succes_reply);
584+ } return ;
585+
586+ case BLE_GATTS_OP_EXEC_WRITE_REQ_NOW: {
587+ long_write_request_t * req = findLongWriteRequest (conn_handle);
588+ if (!req) {
589+ sd_ble_gatts_rw_authorize_reply (conn_handle, &write_auth_invalid_reply);
590+ return ;
591+ }
592+
593+ GattWriteAuthCallbackParams cbParams = {
594+ .connHandle = conn_handle,
595+ .handle = req->attr_handle ,
596+ .offset = req->offset ,
597+ .len = req->length ,
598+ .data = req->data ,
599+ .authorizationReply = AUTH_CALLBACK_REPLY_SUCCESS /* the callback handler must leave this member
600+ * set to AUTH_CALLBACK_REPLY_SUCCESS if the client
601+ * request is to proceed. */
602+ };
603+ uint16_t write_authorization = p_characteristics[characteristicIndex]->authorizeWrite (&cbParams);
604+
605+ // the user code didn't provide the write authorization,
606+ // just leave here.
607+ if (write_authorization != AUTH_CALLBACK_REPLY_SUCCESS) {
608+ // report the status of the operation in any cases
609+ sd_ble_gatts_rw_authorize_reply (conn_handle, &write_auth_invalid_reply);
610+ releaseLongWriteRequest (conn_handle);
611+ return ;
612+ }
613+
614+ // FIXME can't use ::write here, this function doesn't take the offset into account ...
615+ ble_gatts_value_t value = {
616+ .len = req->length ,
617+ .offset = req->offset ,
618+ .p_value = req->data
619+ };
620+ uint32_t update_err = sd_ble_gatts_value_set (conn_handle, req->attr_handle , &value);
621+ if (update_err) {
622+ sd_ble_gatts_rw_authorize_reply (conn_handle, &write_auth_invalid_reply);
623+ releaseLongWriteRequest (conn_handle);
624+ return ;
625+ }
626+
627+ sd_ble_gatts_rw_authorize_reply (conn_handle, &write_auth_succes_reply);
628+
629+ GattWriteCallbackParams writeParams = {
630+ .connHandle = conn_handle,
631+ .handle = req->attr_handle ,
632+ .writeOp = static_cast <GattWriteCallbackParams::WriteOp_t>(input_req.op ),
633+ .offset = req->offset ,
634+ .len = req->length ,
635+ .data = req->data ,
636+ };
637+ handleDataWrittenEvent (&writeParams);
638+ releaseLongWriteRequest (conn_handle);
639+ } return ;
640+ }
641+
454642 GattWriteAuthCallbackParams cbParams = {
455643 .connHandle = gattsEventP->conn_handle ,
456644 .handle = handle_value,
@@ -541,3 +729,64 @@ void nRF5xGattServer::hwCallback(ble_evt_t *p_ble_evt)
541729 break ;
542730 }
543731}
732+
733+ uint16_t nRF5xGattServer::getBiggestCharacteristicSize () const {
734+ uint16_t result = 0 ;
735+ for (size_t i = 0 ; i < characteristicCount; ++i) {
736+ uint16_t current_size = p_characteristics[i]->getValueAttribute ().getMaxLength ();
737+ if (current_size > result) {
738+ result = current_size;
739+ }
740+ }
741+ return result;
742+ }
743+
744+ nRF5xGattServer::long_write_request_t * nRF5xGattServer::allocateLongWriteRequest (uint16_t connection_handle) {
745+ for (size_t i = 0 ; i < TOTAL_CONCURRENT_LONG_WRITE_REQUESTS; ++i) {
746+ long_write_request_t & req = long_write_requests[i];
747+ if (req.data == NULL ) {
748+ uint16_t block_size = getBiggestCharacteristicSize ();
749+ req.data = static_cast <uint8_t *>(malloc (block_size));
750+ req.offset = 0 ;
751+ req.length = 0 ;
752+ req.conn_handle = connection_handle;
753+ return &req;
754+ }
755+ }
756+ // if nothing has been found then return null
757+ return NULL ;
758+ }
759+
760+ bool nRF5xGattServer::releaseLongWriteRequest (uint16_t connection_handle) {
761+ long_write_request_t * req = findLongWriteRequest (connection_handle);
762+ if (!req) {
763+ return false ;
764+ }
765+
766+ free (req->data );
767+ req->data = NULL ;
768+
769+ // the other fields are not relevant, return now
770+ return true ;
771+ }
772+
773+ nRF5xGattServer::long_write_request_t * nRF5xGattServer::findLongWriteRequest (uint16_t connection_handle) {
774+ for (size_t i = 0 ; i < TOTAL_CONCURRENT_LONG_WRITE_REQUESTS; ++i) {
775+ long_write_request_t & req = long_write_requests[i];
776+ if (req.data != NULL && req.conn_handle == connection_handle) {
777+ return &req;
778+ }
779+ }
780+ // if nothing has been found then return null
781+ return NULL ;
782+ }
783+
784+ void nRF5xGattServer::releaseAllWriteRequests () {
785+ for (size_t i = 0 ; i < TOTAL_CONCURRENT_LONG_WRITE_REQUESTS; ++i) {
786+ long_write_request_t & req = long_write_requests[i];
787+ if (req.data != NULL ) {
788+ free (req.data );
789+ req.data = NULL ;
790+ }
791+ }
792+ }
0 commit comments