Sebastián Pastor / EtheriosCloudConnector
Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers connector_sm_send.h Source File

connector_sm_send.h

00001 /*
00002  * Copyright (c) 2013 Digi International Inc.,
00003  * All rights not expressly granted are reserved.
00004  *
00005  * This Source Code Form is subject to the terms of the Mozilla Public
00006  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
00007  * You can obtain one at http://mozilla.org/MPL/2.0/.
00008  *
00009  * Digi International Inc. 11001 Bren Road East, Minnetonka, MN 55343
00010  * =======================================================================
00011  */
00012 static connector_status_t sm_get_user_data_length(connector_data_t * const connector_ptr, connector_sm_data_t * const sm_ptr, connector_sm_session_t * const session)
00013 {
00014     connector_status_t result;
00015     connector_callback_status_t status = connector_callback_continue;
00016 
00017 #if !(defined CONNECTOR_DATA_SERVICE) && !(defined CONNECTOR_SM_CLI)
00018     (void)(connector_ptr); /* Unused argument */
00019 #endif
00020 
00021     switch (session->command)
00022     {
00023         case connector_sm_cmd_data:
00024         case connector_sm_cmd_no_path_data: 
00025         {
00026 #if (defined CONNECTOR_DATA_SERVICE)
00027             connector_data_service_length_t cb_data;
00028 
00029             cb_data.transport = session->transport;
00030             cb_data.user_context = session->user.context;
00031             cb_data.total_bytes = 0;
00032 
00033             #if (defined CONNECTOR_DATA_POINTS)
00034             if (SmIsDatapoint(session->flags))
00035             {
00036                 status = dp_handle_callback(connector_ptr, connector_request_id_data_service_send_length, &cb_data);
00037             }
00038             else
00039             #endif
00040             {
00041                 connector_request_id_t request_id;
00042 
00043                 request_id.data_service_request = SmIsClientOwned(session->flags) ? connector_request_id_data_service_send_length : connector_request_id_data_service_receive_reply_length;
00044                 status = connector_callback(connector_ptr->callback, connector_class_id_data_service, request_id, &cb_data);
00045                 if (status == connector_callback_unrecognized)
00046                     status = connector_callback_continue;
00047             }
00048 
00049             session->in.bytes = cb_data.total_bytes;
00050 #endif
00051             break;
00052         }
00053 
00054         #if (defined CONNECTOR_SM_CLI)
00055         case connector_sm_cmd_cli:
00056         {
00057             connector_sm_cli_response_length_t cb_data;
00058             connector_request_id_t request_id;
00059 
00060             cb_data.transport = session->transport;
00061             cb_data.user_context = session->user.context;
00062             cb_data.total_bytes = 0;
00063 
00064             request_id.sm_request = connector_request_id_sm_cli_response_length;
00065             status = connector_callback(connector_ptr->callback, connector_class_id_short_message, request_id, &cb_data);
00066             if (status == connector_callback_unrecognized)
00067                 status = connector_callback_continue;
00068             session->in.bytes = cb_data.total_bytes;
00069             break;
00070         }
00071         #endif
00072 
00073         default:
00074             result = connector_abort;
00075             ASSERT_GOTO(connector_false, error);
00076             break;
00077     }
00078 
00079     result = sm_map_callback_status_to_connector_status(status);
00080     if (status == connector_callback_continue)
00081     {
00082         ASSERT_GOTO(session->in.bytes < (sm_ptr->transport.sm_mtu_tx * UCHAR_MAX), error);
00083         session->sm_state = connector_sm_state_prepare_payload;
00084     }
00085 
00086 error:
00087     return result;
00088 }
00089 
00090 #if (defined CONNECTOR_DATA_SERVICE)
00091 static connector_status_t sm_get_more_request_data(connector_data_t * const connector_ptr, connector_sm_session_t * const session)
00092 {
00093     connector_status_t result = connector_abort;
00094     uint8_t * dptr = session->in.data;
00095     connector_data_service_send_data_t cb_data;
00096 
00097     ASSERT_GOTO(session->in.bytes > session->bytes_processed, error);
00098 
00099     cb_data.transport = session->transport;
00100     cb_data.user_context = session->user.context;
00101     cb_data.buffer = &dptr[session->bytes_processed];
00102     cb_data.bytes_available = session->in.bytes - session->bytes_processed;
00103     cb_data.bytes_used = 0;
00104     cb_data.more_data = connector_false;
00105 
00106     {
00107         connector_callback_status_t status;
00108 
00109 #if (defined CONNECTOR_DATA_POINTS)
00110         if (SmIsDatapoint(session->flags))
00111         {
00112             status = dp_handle_callback(connector_ptr, connector_request_id_data_service_send_data, &cb_data);
00113         }
00114         else
00115 #endif
00116         {
00117             connector_request_id_t request_id;
00118 
00119             request_id.data_service_request = connector_request_id_data_service_send_data;
00120             status = connector_callback(connector_ptr->callback, connector_class_id_data_service, request_id, &cb_data);
00121         }
00122 
00123         result = sm_map_callback_status_to_connector_status(status);
00124     }
00125 
00126     session->user.context = cb_data.user_context;
00127     if (result == connector_working)
00128     {
00129         session->bytes_processed += cb_data.bytes_used;
00130         ASSERT(session->bytes_processed <= session->in.bytes);
00131         if (!cb_data.more_data)
00132             sm_set_payload_complete(session);
00133     }
00134 
00135 error:
00136     return result;
00137 }
00138 
00139 static connector_status_t sm_get_more_response_data(connector_data_t * const connector_ptr, connector_sm_session_t * const session)
00140 {
00141     connector_status_t result = connector_abort;
00142     uint8_t * dptr = session->in.data;
00143     connector_data_service_receive_reply_data_t cb_data;
00144 
00145     ASSERT_GOTO(session->in.bytes > session->bytes_processed, error);
00146 
00147     cb_data.transport = session->transport;
00148     cb_data.user_context = session->user.context;
00149     cb_data.buffer = &dptr[session->bytes_processed];
00150     cb_data.bytes_available = session->in.bytes - session->bytes_processed;
00151     cb_data.bytes_used = 0;
00152     cb_data.more_data = connector_false;
00153 
00154     {
00155         connector_callback_status_t status;
00156         connector_request_id_t request_id;
00157 
00158         request_id.data_service_request = connector_request_id_data_service_receive_reply_data;
00159         status = connector_callback(connector_ptr->callback, connector_class_id_data_service, request_id, &cb_data);
00160         result = sm_map_callback_status_to_connector_status(status);
00161     }
00162 
00163     session->user.context = cb_data.user_context;
00164     if (result == connector_working)
00165     {
00166         session->bytes_processed += cb_data.bytes_used;
00167         ASSERT(session->bytes_processed <= session->in.bytes);
00168         if (!cb_data.more_data)
00169         {
00170             sm_set_payload_complete(session);
00171         }
00172         else
00173         {
00174             ASSERT(cb_data.bytes_used < cb_data.bytes_available);
00175         }
00176     }
00177 
00178 error:
00179     return result;
00180 }
00181 #endif
00182 
00183 #if (defined CONNECTOR_COMPRESSION)
00184 static connector_status_t sm_compress_data(connector_data_t * const connector_ptr, connector_sm_session_t * const session)
00185 {
00186     connector_status_t status;
00187     size_t const excluded_header_adler32_footer_bytes = 6;
00188 
00189     session->compress.out.data = NULL;
00190     session->compress.out.bytes = session->bytes_processed + excluded_header_adler32_footer_bytes;
00191     status = sm_allocate_user_buffer(connector_ptr, &session->compress.out);
00192     ASSERT_GOTO(status == connector_working, error);
00193 
00194     {
00195         z_streamp const zlib_ptr = &session->compress.zlib;
00196         int zret;
00197 
00198         memset(zlib_ptr, 0, sizeof *zlib_ptr);
00199         zret = deflateInit(zlib_ptr, Z_DEFAULT_COMPRESSION);
00200         ASSERT_GOTO(zret == Z_OK, error);
00201 
00202         zlib_ptr->next_in = session->in.data;
00203         zlib_ptr->avail_in = session->bytes_processed;
00204         zlib_ptr->next_out = session->compress.out.data;
00205         zlib_ptr->avail_out = session->compress.out.bytes;
00206         zret = deflate(zlib_ptr, Z_FINISH);
00207         switch(zret)
00208         {
00209             case Z_STREAM_END:
00210             {
00211                 size_t const compressed_bytes = session->compress.out.bytes - (zlib_ptr->avail_out + excluded_header_adler32_footer_bytes);
00212 
00213                 if (compressed_bytes < session->bytes_processed)
00214                 {
00215                     uint8_t * data_ptr = session->compress.out.data;
00216 
00217                     SmSetCompressed(session->flags);
00218                     status = free_data_buffer(connector_ptr, named_buffer_id(sm_data_block), session->in.data);
00219                     if (status != connector_working) goto error;
00220                     session->in.data = data_ptr;
00221                     session->bytes_processed = compressed_bytes;
00222                     sm_set_payload_process(session);
00223                     break;
00224                 }
00225             }
00226             /* no break */
00227 
00228             case Z_OK:
00229                 status = free_data_buffer(connector_ptr, named_buffer_id(sm_data_block), session->compress.out.data);
00230                 if (status != connector_working) goto error;
00231                 sm_set_payload_process(session);
00232                 break;
00233 
00234             default:
00235                 status = connector_abort;
00236                 ASSERT_GOTO(connector_false, error);
00237                 break;
00238         }
00239 
00240         zret = deflateEnd(zlib_ptr);
00241         ASSERT_GOTO(zret == Z_OK, error);
00242     }
00243 
00244 error:
00245    return status;
00246 }
00247 #endif
00248 
00249 static connector_status_t sm_prepare_segment(connector_sm_data_t * const sm_ptr, connector_sm_session_t * const session)
00250 {
00251     connector_status_t result = connector_abort;
00252     size_t const max_payload = sm_ptr->transport.sm_mtu_tx - record_end(segment);
00253 
00254     session->segments.processed = 0;
00255     session->segments.size_array = NULL;
00256     if (session->in.bytes <= max_payload)
00257         session->segments.count = 1;
00258     #if (defined CONNECTOR_SM_MULTIPART)
00259     else
00260     {
00261         size_t const segment0_overhead_bytes = record_end(segment0) - record_end(segmentn);
00262         size_t const segment_count = (session->in.bytes + ((max_payload + segment0_overhead_bytes) - 1))/max_payload;
00263 
00264         ASSERT_GOTO(segment_count < 256, error);
00265         session->segments.count = segment_count;
00266         SmSetMultiPart(session->flags);
00267     }
00268     #else
00269     else
00270     {
00271         connector_debug_printf("sm_prepare_segment: Multipart is disabled. Please define CONNECTOR_SM_MULTIPART in connector_config.h.\n");
00272         ASSERT(connector_false);
00273         result = connector_invalid_data_size;
00274         goto error;
00275     }
00276     #endif
00277 
00278     session->sm_state = connector_sm_state_send_data;
00279     result = connector_working;
00280 
00281 error:
00282     return result;
00283 }
00284 
00285 static connector_status_t sm_send_segment(connector_data_t * const connector_ptr, connector_sm_data_t * const sm_ptr)
00286 {
00287     connector_status_t result = connector_no_resource;
00288     connector_sm_packet_t * const send_packet = &sm_ptr->network.send_packet;
00289     connector_callback_status_t status;
00290     connector_network_send_t send_data;
00291     connector_request_id_t request_id;
00292 
00293     send_data.buffer = &send_packet->data[send_packet->processed_bytes];
00294     send_data.bytes_available = send_packet->total_bytes - send_packet->processed_bytes;
00295     send_data.handle = sm_ptr->network.handle;
00296     send_data.bytes_used = 0;
00297 
00298     request_id.network_request = connector_request_id_network_send;
00299     status = connector_callback(connector_ptr->callback, sm_ptr->network.class_id, request_id, &send_data);
00300     ASSERT(status != connector_callback_unrecognized);
00301     result = sm_map_callback_status_to_connector_status(status);
00302     if (status != connector_callback_continue) goto error;
00303 
00304     send_packet->processed_bytes += send_data.bytes_used;
00305     if (send_packet->processed_bytes >= send_packet->total_bytes)
00306     {
00307         connector_sm_session_t * const session = send_packet->pending_session;
00308 
00309         ASSERT_GOTO(session != NULL, error);
00310         session->segments.processed++;
00311         if (session->segments.count == session->segments.processed)
00312         {
00313             if (session->in.bytes != 0)
00314             {
00315                 connector_debug_printf("ERROR: sm_send_segment: All segments processed but still remaining bytes\n");
00316             }
00317             result = sm_switch_path(connector_ptr, session, SmIsResponse(session->flags) ? connector_sm_state_complete : connector_sm_state_receive_data);
00318             if (result != connector_working) goto error;
00319         }
00320 
00321         send_packet->total_bytes = 0;
00322         send_packet->processed_bytes = 0;
00323         send_packet->pending_session = NULL;
00324     }
00325 
00326 error:
00327     return result;
00328 }
00329 
00330 #if (defined CONNECTOR_TRANSPORT_SMS)
00331 static connector_status_t sm_encode_segment(connector_data_t * const connector_ptr, connector_sm_data_t * const sm_ptr, connector_sm_session_t * const session)
00332 {
00333     size_t const data_size = 1+(sm_ptr->network.send_packet.total_bytes * 5)/4;
00334     void * data_ptr = NULL;
00335     connector_status_t result = malloc_data_buffer(connector_ptr, data_size, named_buffer_id(sm_data_block), &data_ptr);
00336 
00337     if (result == connector_working)
00338     {
00339         connector_sm_packet_t * const send_ptr = &sm_ptr->network.send_packet;
00340 
00341         send_ptr->total_bytes = sm_encode85(data_ptr, data_size, send_ptr->data, send_ptr->total_bytes);
00342         memcpy(send_ptr->data, data_ptr, send_ptr->total_bytes);
00343         result = free_data_buffer(connector_ptr, named_buffer_id(sm_data_block), data_ptr);
00344         if (result != connector_working) goto error;
00345         session->sm_state = connector_sm_state_send_data;
00346     }
00347 
00348 error:
00349     return result;
00350 }
00351 #endif
00352 
00353 static connector_status_t sm_send_data(connector_data_t * const connector_ptr, connector_sm_data_t * const sm_ptr, connector_sm_session_t * const session)
00354 {
00355     connector_status_t result = connector_working;
00356     connector_sm_packet_t * const send_ptr = &sm_ptr->network.send_packet;
00357     uint8_t * data_ptr = send_ptr->data;
00358     uint8_t * sm_header;
00359 
00360     if (send_ptr->total_bytes > 0)
00361     {
00362         goto send;
00363     }
00364 
00365     switch (sm_ptr->network.transport)
00366     {
00367         #if (defined CONNECTOR_TRANSPORT_UDP)
00368         case connector_transport_udp:
00369         {
00370             uint8_t const sm_udp_version_num = SM_UDP_VERSION << 4;
00371             uint8_t const version_byte = sm_udp_version_num | sm_ptr->transport.id_type;
00372 
00373             *data_ptr++ = version_byte;
00374             ASSERT(connector_ptr->connector_got_device_id);
00375             memcpy(data_ptr, sm_ptr->transport.id, sm_ptr->transport.id_length);
00376             data_ptr += sm_ptr->transport.id_length;
00377             break;
00378         }
00379         #endif
00380 
00381         #if (defined CONNECTOR_TRANSPORT_SMS)
00382         case connector_transport_sms:
00383         {
00384             /* service ID available? */
00385             if (sm_ptr->transport.id_length > 0)
00386             {
00387                 ASSERT(sm_ptr->transport.id != NULL);
00388                 memcpy(data_ptr, sm_ptr->transport.id, sm_ptr->transport.id_length);
00389                 data_ptr += sm_ptr->transport.id_length;
00390                 *data_ptr++ = ' ';
00391             }
00392 
00393             break;
00394         }
00395         #endif
00396 
00397         default:
00398             ASSERT(connector_false);
00399             break;
00400     }
00401 
00402     sm_header = data_ptr;
00403 
00404     {
00405         uint8_t const sm_version_num = 0x01 << 5;
00406         uint8_t const request_id_hi = (session->request_id & SM_REQUEST_ID_MASK) >> 8;
00407         uint8_t const request_id_low = session->request_id & 0xFF;
00408         uint8_t info_field = sm_version_num | request_id_hi;
00409 
00410         if (SmIsResponse(session->flags))
00411             SmSetResponse(info_field);
00412         else if (SmIsResponseNeeded(session->flags))
00413             SmSetResponseNeeded(info_field);
00414 
00415         if (session->segments.processed == 0)
00416         {
00417             uint8_t cmd_field = SmIsRequest(session->flags) ? session->command : 0;
00418 
00419             if (SmIsError(session->flags))
00420                 SmSetError(cmd_field);
00421             if (SmIsCompressed(session->flags))
00422                 SmSetCompressed(cmd_field);
00423 
00424             #if (defined CONNECTOR_SM_MULTIPART)
00425             if (SmIsMultiPart(session->flags))
00426             {
00427                 uint8_t * const segment0 = sm_header;
00428 
00429                 SmSetMultiPart(info_field);
00430                 message_store_u8(segment0, info, info_field);
00431                 message_store_u8(segment0, request, request_id_low);
00432                 message_store_u8(segment0, segment, session->segments.processed);
00433                 message_store_u8(segment0, count, session->segments.count);
00434                 message_store_u8(segment0, cmd_status, cmd_field);
00435                 message_store_be16(segment0, crc, 0);
00436                 data_ptr += record_end(segment0);
00437             }
00438             else
00439             #endif
00440             {
00441                 uint8_t * const segment = sm_header;
00442 
00443                 ASSERT_GOTO(SmIsNotMultiPart(session->flags), done);
00444                 message_store_u8(segment, info, info_field);
00445                 message_store_u8(segment, request, request_id_low);
00446                 message_store_u8(segment, cmd_status, cmd_field);
00447                 message_store_be16(segment, crc, 0);
00448                 data_ptr += record_end(segment);
00449             }
00450         }
00451         else
00452         {
00453             uint8_t * const segmentn = sm_header;
00454 
00455             ASSERT_GOTO(SmIsMultiPart(session->flags), done);
00456             SmSetMultiPart(info_field);
00457             message_store_u8(segmentn, info, info_field);
00458             message_store_u8(segmentn, request, request_id_low);
00459             message_store_u8(segmentn, segment, session->segments.processed);
00460             message_store_be16(segmentn, crc, 0);
00461             data_ptr += record_end(segmentn);
00462         }
00463     }
00464 
00465     {
00466         size_t const filled_bytes = data_ptr - send_ptr->data;
00467         size_t const bytes_available = sm_ptr->transport.sm_mtu_tx - (data_ptr - sm_header);
00468         size_t payload_bytes;
00469 
00470         if (SmIsError(session->flags))
00471         {
00472             uint16_t const error_code = (uint16_t)session->error;
00473             char * const error_text = (session->error == connector_sm_error_in_request) ? "Request error" : "Unexpected request";
00474             size_t const error_text_length = strlen(error_text) + 1;
00475             size_t const error_code_length = sizeof error_code;
00476 
00477             StoreBE16(data_ptr, error_code);
00478             memcpy(data_ptr + error_code_length, error_text, error_text_length);
00479             payload_bytes = error_code_length + error_text_length;
00480         }
00481         else
00482         {
00483             payload_bytes = (session->in.bytes < bytes_available) ? session->in.bytes : bytes_available;
00484 
00485             if (payload_bytes > 0)
00486             {
00487                 memcpy(data_ptr, &session->in.data[session->bytes_processed], payload_bytes);
00488                 session->bytes_processed += payload_bytes;
00489                 session->in.bytes -= payload_bytes;
00490             }
00491         }
00492 
00493         send_ptr->total_bytes = filled_bytes + payload_bytes;
00494 
00495         {
00496             uint8_t * const crc_field = data_ptr - 2;
00497             size_t const header_bytes = data_ptr - sm_header;
00498             size_t const crc_bytes = header_bytes + payload_bytes;
00499             uint16_t crc_value = 0;
00500 
00501             crc_value = sm_calculate_crc16(crc_value, sm_header, crc_bytes);
00502             StoreBE16(crc_field, crc_value);
00503         }
00504     }
00505 
00506     send_ptr->pending_session = session;
00507     #if (defined CONNECTOR_TRANSPORT_SMS)
00508     if (SmIsEncoded(session->flags))
00509     {
00510         session->sm_state = connector_sm_state_encoding;
00511         /* Increase pointer to skip preamble encoding */
00512         if (sm_ptr->transport.id_length)
00513         {
00514             send_ptr->data += (sm_ptr->transport.id_length + SMS_SERVICEID_WRAPPER_TX_SIZE);
00515             send_ptr->total_bytes -= (sm_ptr->transport.id_length + SMS_SERVICEID_WRAPPER_TX_SIZE);
00516         }
00517 
00518         result = sm_encode_segment(connector_ptr, sm_ptr, session);
00519 
00520         /* Restore pointer if it has preamble */
00521         if (sm_ptr->transport.id_length)
00522         {
00523             send_ptr->data -= (sm_ptr->transport.id_length + SMS_SERVICEID_WRAPPER_TX_SIZE);
00524             send_ptr->total_bytes += (sm_ptr->transport.id_length + SMS_SERVICEID_WRAPPER_TX_SIZE);
00525         }
00526     }
00527     #endif
00528 send:
00529     if (result == connector_working)
00530     {
00531         result = sm_send_segment(connector_ptr, sm_ptr);
00532     }
00533 
00534 done:
00535     return result;
00536 }
00537 
00538 static connector_status_t sm_process_send_path(connector_data_t * const connector_ptr, connector_sm_data_t * const sm_ptr, connector_sm_session_t * const session)
00539 {
00540     connector_status_t result = connector_abort;
00541 
00542     ASSERT_GOTO(session != NULL, error);
00543     switch (session->sm_state)
00544     {
00545         case connector_sm_state_get_total_length:
00546             result = sm_get_user_data_length(connector_ptr, sm_ptr, session);
00547             break;
00548 
00549         case connector_sm_state_prepare_payload:
00550             result = sm_prepare_payload(connector_ptr, session);
00551             break;
00552 
00553         case connector_sm_state_more_data:
00554 #if (defined CONNECTOR_DATA_SERVICE)
00555             result = SmIsClientOwned(session->flags) ? sm_get_more_request_data(connector_ptr, session) : sm_get_more_response_data(connector_ptr, session);
00556 #endif
00557             break;
00558 
00559         #if (defined CONNECTOR_COMPRESSION)
00560         case connector_sm_state_compress:
00561             result = sm_compress_data(connector_ptr, session);
00562             break;
00563         #endif
00564 
00565         case connector_sm_state_prepare_segment:
00566             result = sm_prepare_segment(sm_ptr, session);
00567             break;
00568 
00569         #if (defined CONNECTOR_TRANSPORT_SMS)
00570         case connector_sm_state_encoding:
00571             result = sm_encode_segment(connector_ptr, sm_ptr, session);
00572             if (result == connector_working)
00573             {
00574                 result = sm_send_segment(connector_ptr, sm_ptr);
00575             }
00576             break;
00577         #endif
00578 
00579         case connector_sm_state_send_data:
00580             result = sm_send_data(connector_ptr, sm_ptr, session);
00581             break;
00582 
00583         default:
00584             ASSERT(connector_false);
00585             break;
00586     }
00587 
00588     sm_verify_result(sm_ptr, &result);
00589 
00590 error:
00591     return result;
00592 }
00593 
00594