Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
connector_cc.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 00013 #define FAC_CC_DISCONNECT 0x00 00014 #define FAC_CC_REDIRECT_TO_SDA 0x03 00015 #define FAC_CC_REDIRECT_REPORT 0x04 00016 #define FAC_CC_CONNECTION_REPORT 0x05 00017 #define FAC_CC_REBOOT 0x06 00018 00019 #define CC_REDIRECT_URL_COUNT 2 00020 00021 typedef enum { 00022 cc_not_redirect, 00023 cc_redirect_success, 00024 cc_redirect_error 00025 } cc_redirect_status_t; 00026 00027 typedef enum { 00028 ethernet_type = 1, 00029 ppp_over_modem_type 00030 } cc_connection_type_t; 00031 00032 enum { 00033 cc_state_redirect_report, 00034 cc_state_connect_report, 00035 cc_state_close, 00036 cc_state_connect 00037 00038 }; 00039 00040 00041 typedef struct { 00042 char origin_url[CLOUD_URL_LENGTH]; 00043 size_t origin_url_length; 00044 00045 uint8_t * redirect; 00046 00047 int state; 00048 size_t redirect_count; 00049 uint8_t report_code; 00050 } connector_cc_data_t; 00051 00052 static void cc_init(connector_cc_data_t * const cc_ptr) 00053 { 00054 cc_ptr->state = cc_state_redirect_report; 00055 cc_ptr->report_code = cc_not_redirect; 00056 cc_ptr->origin_url_length = 0; 00057 00058 } 00059 00060 static connector_status_t edp_send_redirect_report(connector_data_t * const connector_ptr, connector_cc_data_t * const cc_ptr) 00061 { 00062 00063 enum cc_redirect_report { 00064 field_define(redirect_report, opcode, uint8_t), 00065 field_define(redirect_report, code, uint8_t), 00066 field_define(redirect_report, message_length, uint8_t), 00067 /* TODO: Need to fix this enum if message_length > 0. For now, cloud always send message length = 0 */ 00068 field_define(redirect_report, url_length, uint16_t), 00069 record_end(redirect_report) 00070 }; 00071 00072 size_t const report_message_length = 0; 00073 size_t const redirect_report_header_size = record_bytes(redirect_report); 00074 00075 connector_status_t result = connector_working; 00076 uint8_t * edp_header; 00077 uint8_t * redirect_report; 00078 size_t url_length; 00079 size_t redirect_length; 00080 00081 ASSERT(report_message_length == 0); 00082 00083 connector_debug_printf("Connection Control: send redirect_report\n"); 00084 00085 /* build and send redirect report 00086 * ---------------------------------------------------- 00087 * | 0 | 1 | 2 | 3... | x1-x2 | ... | 00088 * ---------------------------------------------------- 00089 * | opcode | report | message | report | url | url | 00090 * | | code | length | message | length | | 00091 * ---------------------------------------------------- 00092 */ 00093 00094 edp_header = tcp_get_packet_buffer(connector_ptr, E_MSG_FAC_CC_NUM, &redirect_report, &redirect_length); 00095 if (edp_header == NULL) 00096 { 00097 result = connector_pending; 00098 goto done; 00099 } 00100 00101 message_store_u8(redirect_report, opcode, FAC_CC_REDIRECT_REPORT); 00102 message_store_u8(redirect_report, code, cc_ptr->report_code); 00103 { 00104 uint8_t const length8 = (uint8_t) report_message_length; 00105 00106 ASSERT(report_message_length <= UCHAR_MAX); 00107 message_store_u8(redirect_report, message_length, length8); 00108 } 00109 00110 url_length = cc_ptr->origin_url_length; 00111 00112 if (url_length > 0) 00113 { 00114 char const prefix_url[] = URL_PREFIX; 00115 size_t const prefix_len = sizeof prefix_url -1; 00116 uint8_t * const redirect_report_url = redirect_report + redirect_report_header_size; 00117 00118 ASSERT(redirect_length > (redirect_report_header_size + prefix_len + cc_ptr->origin_url_length)); 00119 00120 memcpy(redirect_report_url, prefix_url, prefix_len); 00121 memcpy((redirect_report_url + prefix_len), cc_ptr->origin_url, cc_ptr->origin_url_length); 00122 00123 url_length += prefix_len; 00124 00125 } 00126 { 00127 uint16_t const length16 = (uint16_t) url_length; 00128 00129 ASSERT(url_length <= UINT16_MAX); 00130 message_store_be16(redirect_report, url_length, length16); 00131 } 00132 00133 result = tcp_initiate_send_facility_packet(connector_ptr, edp_header, 00134 redirect_report_header_size + url_length, 00135 E_MSG_FAC_CC_NUM, tcp_release_packet_buffer, NULL); 00136 done: 00137 return result; 00138 } 00139 00140 static void build_ip_addr(connector_data_t * const connector_ptr, uint8_t * ipv6_addr) 00141 { 00142 connector_debug_hexvalue("Send device IP address", connector_ptr->edp_data.config.ip_addr, (int) connector_ptr->edp_data.config.ip_addr_length); 00143 00144 if (connector_ptr->edp_data.config.ip_addr_length == CC_IPV6_ADDRESS_LENGTH) 00145 { 00146 /* Just copy IPv6 address */ 00147 memcpy(ipv6_addr, connector_ptr->edp_data.config.ip_addr, CC_IPV6_ADDRESS_LENGTH); 00148 } 00149 else 00150 { 00151 /* good ipv4 addr. Map ipv4 to ipv6 address: 00152 * 10 all-zeros octets, 2 all-ones octets, and 00153 * then the ipv4 addr 00154 */ 00155 00156 static unsigned char const ipv6_padding_for_ipv4[] = {0,0,0,0,0,0,0,0,0,0,0xff,0xff}; 00157 size_t const padding_length = sizeof ipv6_padding_for_ipv4/sizeof ipv6_padding_for_ipv4[0]; 00158 00159 ASSERT(padding_length == (CC_IPV6_ADDRESS_LENGTH - CC_IPV4_ADDRESS_LENGTH)); 00160 00161 memcpy(ipv6_addr, ipv6_padding_for_ipv4, padding_length); 00162 ipv6_addr += padding_length; 00163 memcpy(ipv6_addr, connector_ptr->edp_data.config.ip_addr, CC_IPV4_ADDRESS_LENGTH); 00164 } 00165 00166 return; 00167 00168 } 00169 00170 static connector_status_t send_connection_report(connector_data_t * const connector_ptr, connector_cc_data_t * const cc_ptr) 00171 { 00172 #define FAC_CC_CLIENTTYPE_REBOOTABLE_DEVICE 2 00173 00174 enum cc_connection_report { 00175 field_define(connection_report, opcode, uint8_t), 00176 field_define(connection_report, client_type, uint8_t), 00177 field_define(connection_report, ip1, uint32_t), 00178 field_define(connection_report, ip2, uint32_t), 00179 field_define(connection_report, ip3, uint32_t), 00180 field_define(connection_report, ip4, uint32_t), 00181 field_define(connection_report, connection_type, uint8_t), 00182 record_end(connection_report) 00183 }; 00184 00185 enum cc_connection_info { 00186 field_define(connection_info, link_speed, uint32_t), 00187 record_end(connection_info) 00188 }; 00189 connector_status_t result = connector_working; 00190 uint8_t * edp_header; 00191 uint8_t * connection_report; 00192 size_t avail_length; 00193 size_t report_length; 00194 00195 UNUSED_PARAMETER(cc_ptr); 00196 00197 connector_debug_printf("Connection Control: send connection report\n"); 00198 00199 /* Build Connection report 00200 * ------------------------------------------------------- 00201 * | 0 | 1 | 2 - 17 | 18 | 19 ... | 00202 * ------------------------------------------------------- 00203 * | opcode | client | IP | connection | connection | 00204 * | | type | address | type | information | 00205 * ------------------------------------------------------- 00206 * 00207 * 1. call callback to get ip address & build ip_address 00208 * 2. call callback to get & build connection type 00209 * 3. if connection type is LAN, call callback to get and build mac address for connection information 00210 * 4. if connection type is WAN, call callback to get and build link speed for connection information 00211 * 5. if connection type is WAN, call callback to get and build phone number for connection information 00212 */ 00213 edp_header = tcp_get_packet_buffer(connector_ptr, E_MSG_FAC_CC_NUM, &connection_report, &avail_length); 00214 if (edp_header == NULL) 00215 { 00216 result = connector_pending; 00217 goto done; 00218 } 00219 00220 { 00221 result = get_config_ip_addr(connector_ptr); 00222 00223 if (result != connector_working) 00224 { 00225 goto done; 00226 } 00227 00228 message_store_u8(connection_report, opcode, FAC_CC_CONNECTION_REPORT); 00229 message_store_u8(connection_report, client_type, FAC_CC_CLIENTTYPE_REBOOTABLE_DEVICE); 00230 report_length = field_named_data(connection_report, opcode, size)+field_named_data(connection_report, client_type, size); 00231 00232 build_ip_addr(connector_ptr, (connection_report+report_length)); 00233 report_length += CC_IPV6_ADDRESS_LENGTH; 00234 00235 } 00236 00237 { 00238 cc_connection_type_t const connection_type = (connector_ptr->connection_type == connector_connection_type_lan) ? ethernet_type : ppp_over_modem_type; 00239 00240 message_store_u8(connection_report, connection_type, connection_type); 00241 00242 report_length += field_named_data(connection_report, connection_type, size); 00243 00244 switch (connector_ptr->connection_type) 00245 { 00246 case connector_connection_type_lan: 00247 result = get_config_mac_addr(connector_ptr); 00248 if (result != connector_working) 00249 { 00250 goto done; 00251 00252 } 00253 00254 { 00255 uint8_t * report_ptr = connection_report + report_length; 00256 00257 ASSERT(connector_ptr->mac_addr != NULL); 00258 /* build MAC address for LAN connection type */ 00259 memcpy(report_ptr, connector_ptr->mac_addr, MAC_ADDR_LENGTH); 00260 report_length += MAC_ADDR_LENGTH; 00261 00262 connector_debug_hexvalue("Sending MAC address", connector_ptr->mac_addr, MAC_ADDR_LENGTH); 00263 00264 break; 00265 } 00266 case connector_connection_type_wan: 00267 { 00268 /* build Link speed for WAN connection type */ 00269 uint8_t * connection_info = connection_report + record_bytes(connection_report); 00270 00271 00272 #if (defined CONNECTOR_WAN_LINK_SPEED_IN_BITS_PER_SECOND) 00273 message_store_be32(connection_info, link_speed, CONNECTOR_WAN_LINK_SPEED_IN_BITS_PER_SECOND); 00274 connector_debug_printf("send_connection_report: link_speed = %d\n", CONNECTOR_WAN_LINK_SPEED_IN_BITS_PER_SECOND); 00275 00276 #else 00277 message_store_be32(connection_info, link_speed, connector_ptr->link_speed); 00278 connector_debug_printf("send_connection_report: link_speed = %d\n", connector_ptr->link_speed); 00279 #endif 00280 report_length += field_named_data(connection_info, link_speed, size); 00281 00282 { 00283 /* build phone number for WAN connection type */ 00284 00285 #if (defined CONNECTOR_WAN_PHONE_NUMBER_DIALED) 00286 char const phone[] = CONNECTOR_WAN_PHONE_NUMBER_DIALED; 00287 size_t const length = sizeof phone -1; 00288 #else 00289 size_t length = connector_ptr->phone_dialed_length; 00290 char * phone = connector_ptr->phone_dialed; 00291 #endif 00292 connector_debug_printf("send_connection_report: phone number = %.*s\n", (int)length, phone); 00293 memcpy(connection_report+report_length, phone, length); 00294 report_length += length; 00295 *(connection_report + report_length) = 0x0; 00296 report_length++; 00297 } 00298 } 00299 } 00300 } 00301 00302 ASSERT(avail_length > report_length); 00303 result = tcp_initiate_send_facility_packet(connector_ptr, edp_header, report_length, E_MSG_FAC_CC_NUM, tcp_release_packet_buffer, NULL); 00304 00305 done: 00306 return result; 00307 } 00308 00309 00310 static connector_status_t process_redirect(connector_data_t * const connector_ptr, connector_cc_data_t * const cc_ptr, uint8_t const * const packet) 00311 { 00312 enum cc_redirect { 00313 field_define(redirect, opcode, uint8_t), 00314 field_define(redirect, count, uint8_t), 00315 record_end(redirect) 00316 }; 00317 00318 00319 connector_status_t result = connector_working; 00320 uint8_t * redirect; 00321 00322 connector_debug_printf("process_redirect: redirect to new destination\n"); 00323 /* Redirect to new destination: 00324 * The connector will close connection and connect to new destination. If connect 00325 * to new destination fails, this function will returns SUCCESS to try 00326 * connecting to the original destination. 00327 */ 00328 00329 /* 00330 * parse new destinations 00331 * -------------------------------------------------------------------- 00332 * | 0 | 1 | 2 - 3 | 4 ... | | | 00333 * -------------------------------------------------------------------- 00334 * | opcode | URL count | URL 1 Length| URL 1 | URL 2 Length | URL 2 | 00335 * -------------------------------------------------------------------- 00336 * 00337 */ 00338 redirect = GET_PACKET_DATA_POINTER(packet, PACKET_EDP_FACILITY_SIZE); 00339 cc_ptr->redirect_count = message_load_u8(redirect, count); 00340 00341 if (cc_ptr->redirect_count == 0) 00342 { /* nothing to redirect */ 00343 connector_debug_printf("cc_process_redirect: redirect with no url specified\n"); 00344 result = connector_idle; 00345 goto done; 00346 } 00347 00348 if (cc_ptr->redirect_count > CC_REDIRECT_URL_COUNT) 00349 { 00350 cc_ptr->redirect_count = CC_REDIRECT_URL_COUNT; 00351 } 00352 00353 00354 /* let's start parsing url length and url string */ 00355 redirect += record_bytes(redirect); 00356 cc_ptr->redirect = redirect; 00357 00358 /* save original Device Cloud url that we connected before */ 00359 memcpy(cc_ptr->origin_url, connector_ptr->edp_data.config.cloud_url, connector_ptr->edp_data.config.cloud_url_length); 00360 cc_ptr->origin_url_length = connector_ptr->edp_data.config.cloud_url_length; 00361 00362 /* Close the connection before parsing new destination url */ 00363 edp_set_active_state(connector_ptr, connector_transport_redirect); 00364 cc_ptr->state = cc_state_close; 00365 00366 done: 00367 return result; 00368 } 00369 00370 static connector_status_t cc_process(connector_data_t * const connector_ptr, void * const facility_data, 00371 uint8_t * const packet, unsigned int * const receive_timeout) 00372 { 00373 connector_status_t result = connector_idle; 00374 00375 UNUSED_PARAMETER(receive_timeout); 00376 00377 /* process incoming message from Device Cloud for Connection Control facility */ 00378 if (packet != NULL) 00379 { 00380 uint8_t opcode; 00381 uint8_t * ptr; 00382 connector_cc_data_t * cc_ptr = facility_data; 00383 00384 /* get the DATA portion of the packet */ 00385 ptr = GET_PACKET_DATA_POINTER(packet, PACKET_EDP_FACILITY_SIZE); 00386 opcode = *ptr; 00387 00388 switch (opcode) 00389 { 00390 00391 case FAC_CC_DISCONNECT: 00392 { 00393 00394 edp_set_close_status(connector_ptr, connector_close_status_cloud_disconnected); 00395 edp_set_active_state(connector_ptr, connector_transport_close); 00396 result = connector_working; 00397 break; 00398 } 00399 case FAC_CC_REDIRECT_TO_SDA: 00400 result = process_redirect(connector_ptr, cc_ptr, packet); 00401 break; 00402 case FAC_CC_REBOOT: 00403 result = connector_reboot(connector_ptr); 00404 break; 00405 default: 00406 connector_debug_printf("connector_cc_process: unsupported opcode %u\n", opcode); 00407 break; 00408 } 00409 } 00410 00411 return result; 00412 } 00413 static connector_status_t cc_discovery(connector_data_t * const connector_ptr, void * const facility_data, 00414 uint8_t * const packet, unsigned int * receive_timeout) 00415 { 00416 connector_status_t result = connector_idle; 00417 connector_cc_data_t * cc_ptr = facility_data; 00418 00419 UNUSED_PARAMETER(receive_timeout); 00420 UNUSED_PARAMETER(packet); 00421 /* Connection control facility needs to send redirect and 00422 * connection reports on discovery layer. 00423 */ 00424 if (cc_ptr->state == cc_state_redirect_report) 00425 { 00426 result = edp_send_redirect_report(connector_ptr, cc_ptr); 00427 if (result == connector_working) 00428 { 00429 result = connector_pending; /* still not complete cc_discovery */ 00430 cc_ptr->report_code = cc_not_redirect; 00431 cc_ptr->origin_url_length = 0; 00432 cc_ptr->state = cc_state_connect_report; 00433 } 00434 } 00435 else if (cc_ptr->state == cc_state_connect_report) 00436 { 00437 result = send_connection_report(connector_ptr, cc_ptr); 00438 } 00439 00440 return result; 00441 } 00442 00443 static connector_status_t connector_facility_cc_cleanup(connector_data_t * const connector_ptr) 00444 { 00445 connector_status_t result = connector_working; 00446 connector_cc_data_t * const cc_ptr = get_facility_data(connector_ptr, E_MSG_FAC_CC_NUM); 00447 if (cc_ptr != NULL && cc_ptr->state != cc_state_close) 00448 { 00449 cc_ptr->state = cc_state_redirect_report; 00450 cc_ptr->report_code = cc_not_redirect; 00451 cc_ptr->origin_url_length = 0; 00452 cc_ptr->redirect_count = 0; 00453 } 00454 00455 return result; 00456 } 00457 00458 static connector_status_t connector_facility_cc_delete(connector_data_t * const connector_ptr) 00459 { 00460 return del_facility_data(connector_ptr, E_MSG_FAC_CC_NUM); 00461 } 00462 00463 static connector_status_t connector_facility_cc_init(connector_data_t * const connector_ptr, unsigned int const facility_index) 00464 { 00465 connector_status_t result = connector_working; 00466 connector_cc_data_t * cc_ptr; 00467 00468 /* Add Connection control facility 00469 * 00470 * Make sure connection control is not already created. If Connection 00471 * control facility is already created, we probably reconnect to Device Cloud 00472 * so just need to reset to initial state. 00473 * 00474 */ 00475 cc_ptr = get_facility_data(connector_ptr, E_MSG_FAC_CC_NUM); 00476 if (cc_ptr == NULL) 00477 { 00478 void * ptr; 00479 00480 result = add_facility_data(connector_ptr, facility_index, E_MSG_FAC_CC_NUM, &ptr, sizeof *cc_ptr); 00481 00482 if (result != connector_working || ptr == NULL) 00483 { 00484 goto done; 00485 } 00486 cc_ptr = ptr; 00487 00488 cc_init(cc_ptr); 00489 } 00490 00491 done: 00492 return result; 00493 } 00494 00495 static connector_status_t edp_redirect_process(connector_data_t * const connector_ptr) 00496 { 00497 00498 connector_status_t result = connector_working; 00499 00500 00501 connector_cc_data_t * const cc_ptr = get_facility_data(connector_ptr, E_MSG_FAC_CC_NUM); 00502 ASSERT(cc_ptr != NULL); 00503 00504 switch (cc_ptr->state) 00505 { 00506 case cc_state_close: 00507 00508 /* set the reason for closing */ 00509 edp_set_close_status(connector_ptr, connector_close_status_cloud_redirected); 00510 00511 result = tcp_close_cloud(connector_ptr); 00512 if (result == connector_working) 00513 { 00514 if (edp_get_active_state(connector_ptr) == connector_transport_idle) 00515 { 00516 /* continue to redirect state */ 00517 edp_set_active_state(connector_ptr, connector_transport_redirect); 00518 cc_ptr->state = cc_state_connect; 00519 goto done; 00520 } 00521 } 00522 00523 if (result != connector_pending && result != connector_abort && result != connector_device_terminated) 00524 { 00525 /* reset initial value */ 00526 cc_init(cc_ptr); 00527 } 00528 break; 00529 00530 case cc_state_connect: 00531 00532 /* let parse url length and url string */ 00533 if (cc_ptr->redirect_count > 0) 00534 { 00535 enum cc_redirect_url { 00536 field_define(redirect, url_length, uint16_t), 00537 record_end(redirect_url_length) 00538 }; 00539 00540 uint8_t * redirect = cc_ptr->redirect; 00541 char const * cloud_url; 00542 uint16_t cloud_url_length; 00543 00544 uint16_t const url_length = message_load_be16(redirect, url_length); 00545 redirect += record_bytes(redirect_url_length); 00546 00547 { 00548 size_t const prefix_len = sizeof URL_PREFIX -1; 00549 00550 cloud_url = (char const *)redirect; 00551 cloud_url_length = url_length; 00552 00553 if (memcmp(cloud_url, URL_PREFIX, prefix_len) == 0) 00554 { 00555 cloud_url += prefix_len; 00556 cloud_url_length -= (uint16_t) prefix_len; 00557 } 00558 00559 ASSERT(cloud_url_length < sizeof connector_ptr->edp_data.config.cloud_url); 00560 ASSERT(cloud_url_length != 0); 00561 00562 memcpy(connector_ptr->edp_data.config.cloud_url, cloud_url, cloud_url_length); 00563 connector_ptr->edp_data.config.cloud_url_length = cloud_url_length; 00564 connector_ptr->edp_data.config.cloud_url[cloud_url_length] = 0x0; 00565 00566 result = connect_to_cloud(connector_ptr, connector_ptr->edp_data.config.cloud_url); 00567 } 00568 00569 if (result == connector_working) 00570 { 00571 cc_ptr->report_code = cc_redirect_success; 00572 cc_ptr->state = cc_state_redirect_report; 00573 edp_set_active_state(connector_ptr, connector_transport_open); 00574 edp_set_edp_state(connector_ptr, edp_communication_send_version); 00575 00576 connector_ptr->edp_data.send_packet.packet_buffer.in_use = connector_false; 00577 connector_ptr->edp_data.receive_packet.packet_buffer.in_use = connector_false; 00578 connector_ptr->edp_data.receive_packet.packet_buffer.next = NULL; 00579 connector_ptr->edp_data.receive_packet.free_packet_buffer = &connector_ptr->edp_data.receive_packet.packet_buffer; 00580 } 00581 else if (result != connector_pending && result != connector_abort) 00582 { 00583 /* connect to next redirect url */ 00584 result = connector_pending; 00585 cc_ptr->redirect = redirect; 00586 cc_ptr->redirect += url_length; 00587 cc_ptr->redirect_count--; 00588 } 00589 } 00590 else 00591 { 00592 cc_ptr->report_code = cc_redirect_error; 00593 cc_ptr->state = cc_state_redirect_report; 00594 edp_set_active_state(connector_ptr, connector_transport_open); 00595 /* restore the original url */ 00596 edp_get_device_cloud(connector_ptr); 00597 } 00598 break; 00599 } 00600 00601 done: 00602 return result; 00603 } 00604
Generated on Tue Jul 12 2022 19:18:38 by
1.7.2