static wiced_result_t consume_message(websocket_message_t *msg, websocket_msg_handler_t binary_msg_handler, websocket_msg_handler_t text_msg_handler, void *ctx) { wiced_result_t ret = WICED_ERROR; if (!msg->is_active) { return ret; } if (msg->is_text) { if (text_msg_handler) { WPRINT_LIB_INFO( ("(WebSocket) Sending text message (%u bytes) to handler.\n", (unsigned int)msg->len) ); ret = text_msg_handler(ctx, msg->buf, msg->len); } else { WPRINT_LIB_INFO( ("(WebSocket) Text message (%u bytes) discarded because there is no handler.\n", (unsigned int)msg->len) ); ret = WICED_SUCCESS; } } else { if (binary_msg_handler) { WPRINT_LIB_INFO( ("(WebSocket) Sending binary message (%u bytes) to handler.\n", (unsigned int)msg->len) ); ret = binary_msg_handler(ctx, msg->buf, msg->len); } else { WPRINT_LIB_INFO( ("(WebSocket) Binary message (%u bytes) discarded because there is no handler.\n", (unsigned int)msg->len) ); ret = WICED_SUCCESS; } } message_reset(msg); return ret; }
static wiced_result_t print_uuid( const wiced_bt_uuid_t* uuid ) { if ( uuid->size == UUID_16BIT ) { WPRINT_LIB_INFO( ( "%04x\n", (int)uuid->value.value_16_bit ) ); } else { WPRINT_LIB_INFO( ( "%04x %04x %04x %04x %04x %04x %04x %04x\n", (int)uuid->value.value_128_bit[0], (int)uuid->value.value_128_bit[1], (int)uuid->value.value_128_bit[2], (int)uuid->value.value_128_bit[3], (int)uuid->value.value_128_bit[4], (int)uuid->value.value_128_bit[5], (int)uuid->value.value_128_bit[6], (int)uuid->value.value_128_bit[7] ) ); } return WICED_BT_SUCCESS; }
static wiced_result_t send_control_frame(wiced_tcp_socket_t *sock, uint8_t opcode, const char *payload, uint32_t payload_len) { wiced_result_t ret = WICED_ERROR; uint8_t frame_buf[256]; uint32_t frame_size = 2; WPRINT_LIB_DEBUG( ("(WebSocket) Sending control frame with opcode 0x%02X\n", opcode) ); if (payload_len > 125) { WPRINT_LIB_INFO( ("(WebSocket) Failed to send control frame: payload longer than 125 bytes\n") ); return ret; } frame_buf[0] = (uint8_t)(0x80 | opcode); if (payload && payload_len > 0) { frame_buf[1] = (uint8_t)(0x80 | payload_len); /* Generate mask. */ wiced_crypto_get_random(&frame_buf[frame_size], 4); frame_size += 4; memcpy(&frame_buf[frame_size], payload, payload_len); unmask_frame_payload((char *)&frame_buf[frame_size], payload_len, &frame_buf[2]); frame_size += payload_len; } else { frame_buf[1] = (uint8_t)0x80; } ret = wiced_tcp_send_buffer(sock, frame_buf, (uint16_t)frame_size); if (ret != WICED_SUCCESS) { WPRINT_LIB_INFO( ("(WebSocket) Failed to send control frame (err=%u)\n", ret) ); } return ret; }
wiced_result_t websocket_handshake_init(websocket_handshake_t *hs, const char *hostname, const char *path) { wiced_result_t ret; char key_buf[WEBSOCKET_KEY_BASE64_LENGTH]; ret = generate_websocket_client_key(key_buf, sizeof(key_buf)); if (ret != WICED_SUCCESS) { WPRINT_LIB_INFO( ("Failed to generate WebSocket key (err=%u)\n", ret) ); return ret; } hs->hostname = strdup(hostname); hs->path = strdup(path); hs->key = strdup(key_buf); hs->headers = (https_header_t *)malloc(sizeof(https_header_t) * MAX_REQ_HEADERS); hs->num_headers = 0; return ret; }
wiced_result_t open_websocket(websocket_handshake_t *hs, websocket_msg_handler_t binary_msg_handler, websocket_msg_handler_t text_msg_handler, void *ctx) { wiced_result_t ret; wiced_ip_address_t host_ip; wiced_tls_context_t tls_ctx; wiced_tcp_socket_t sock; wiced_tcp_stream_t stream; char *buf = NULL; uint32_t i; https_header_t *header; WPRINT_LIB_INFO( ("Starting WebSocket handshake to https://%s%s\n", hs->hostname, hs->path) ); ret = wiced_hostname_lookup(hs->hostname, &host_ip, DNS_TIMEOUT); if (ret != WICED_SUCCESS) { WPRINT_LIB_INFO( ("DNS lookup failed for %s (err=%u)\n", hs->hostname, ret) ); return ret; } wiced_tls_init_context(&tls_ctx, NULL, NULL); wiced_tcp_create_socket(&sock, WICED_STA_INTERFACE); wiced_tcp_enable_tls(&sock, &tls_ctx); { char ip_str[48]; ip_to_str(&host_ip, ip_str); WPRINT_LIB_INFO( ("Establishing TLS connection to %s port %d\n", ip_str, HTTPS_PORT) ); } ret = wiced_tcp_connect(&sock, &host_ip, HTTPS_PORT, HTTPS_CONNECT_TIMEOUT); if (ret != WICED_SUCCESS) { WPRINT_LIB_INFO( ("Failed to create TCP connection (err=%u)\n", ret) ); wiced_tcp_delete_socket(&sock); return ret; } do { ret = wiced_tcp_stream_init(&stream, &sock); if (ret != WICED_SUCCESS) { WPRINT_LIB_INFO( ("Failed to initialize TCP stream (err=%u)\n", ret) ); break; } buf = (char *)malloc(STREAM_BUF_SIZE); snprintf(buf, STREAM_BUF_SIZE, "GET %s HTTP/1.1\r\n", hs->path); WRITE_STREAM(stream, buf, ret); // Required headers. snprintf(buf, STREAM_BUF_SIZE, "Host: %s\r\n" \ "Connection: upgrade\r\n" \ "Upgrade: websocket\r\n" \ "Sec-WebSocket-Key: %s\r\n" \ "Sec-WebSocket-Version: 13\r\n", hs->hostname, hs->key); WRITE_STREAM(stream, buf, ret); // Additional headers. for (i = 0; i < hs->num_headers; i++) { header = &(hs->headers[i]); snprintf(buf, STREAM_BUF_SIZE, "%s: %s\r\n", header->name, header->value); WRITE_STREAM(stream, buf, ret); } if (i < hs->num_headers) { break; } strcpy(buf, "\r\n"); WRITE_STREAM(stream, buf, ret); ret = wiced_tcp_stream_flush(&stream); if (ret != WICED_SUCCESS) { break; } ret = process_handshake_response(hs, &sock); } while (WICED_FALSE); if (buf) { free(buf); } wiced_tcp_stream_deinit(&stream); if (ret == WICED_SUCCESS) { WPRINT_LIB_INFO( ("WebSocket handshake OK\n") ); process_frames(&sock, binary_msg_handler, text_msg_handler, ctx); WPRINT_LIB_INFO( ("Closing WebSocket.\n") ); } else { WPRINT_LIB_INFO( ("WebSocket handshake failed (err=%u)\n", ret) ); } wiced_tcp_disconnect(&sock); wiced_tcp_delete_socket(&sock); return ret; }
static void process_frames(wiced_tcp_socket_t *sock, websocket_msg_handler_t binary_msg_handler, websocket_msg_handler_t text_msg_handler, void *ctx) { wiced_result_t ret; char *buf, *cur; uint32_t len; websocket_frame_t frame; websocket_message_t msg; wiced_bool_t exit_loop = WICED_FALSE; message_init(&msg); while (WICED_TRUE) { buf = NULL; ret = receive_data(sock, 3000, &buf, &len); if (ret == WICED_TIMEOUT) { continue; } if (ret != WICED_SUCCESS) { WPRINT_LIB_INFO( ("(WebSocket) Failed to read from socket (err=%u)\n", ret) ); break; } WPRINT_LIB_DEBUG( ("(WebSocket) Received %u bytes\n", (unsigned int)len) ); cur = buf; while (len > 0) { if (get_frame(&cur, &len, &frame) != WICED_SUCCESS) { WPRINT_LIB_INFO( ("(WebSocket) Failed to read frame (err=%u)\n", ret) ); exit_loop = WICED_TRUE; break; } if (frame.opcode == WS_OPCODE_PING) { WPRINT_LIB_DEBUG( ("(WebSocket) Received PING frame.\n")); send_control_frame(sock, WS_OPCODE_PONG, frame.payload, frame.payload_len); } else if (frame.opcode == WS_OPCODE_PONG) { WPRINT_LIB_DEBUG( ("(WebSocket) Received PONG frame.\n")); /* Nothing to do. */ } else if (frame.opcode == WS_OPCODE_CLOSE) { WPRINT_LIB_DEBUG( ("(WebSocket) Received CLOSE frame.\n") ); /* Echo the CLOSE frame with the 2-byte status code. */ if (frame.payload_len >= 2) { send_control_frame(sock, WS_OPCODE_CLOSE, frame.payload, 2); } else { send_control_frame(sock, WS_OPCODE_CLOSE, NULL, 0); } exit_loop = WICED_TRUE; break; } else if (frame.opcode == WS_OPCODE_CONTINUATION) { WPRINT_LIB_DEBUG( ("(WebSocket) Received CONTINUATION frame.\n") ); if (msg.is_active) { message_append(&msg, frame.payload, frame.payload_len); if (frame.is_final) { consume_message(&msg, binary_msg_handler, text_msg_handler, ctx); } } else { exit_loop = WICED_TRUE; break; } } else if (frame.opcode == WS_OPCODE_TEXT) { WPRINT_LIB_DEBUG( ("(WebSocket) Received TEXT frame.\n") ); if (msg.is_active) { WPRINT_LIB_DEBUG( ("(WebSocket) A pending message already exists.\n") ); exit_loop = WICED_TRUE; break; } message_start(&msg, WICED_TRUE, frame.payload, frame.payload_len); if (frame.is_final) { consume_message(&msg, binary_msg_handler, text_msg_handler, ctx); } } else if (frame.opcode == WS_OPCODE_BINARY) { WPRINT_LIB_DEBUG( ("(WebSocket) Received BINARY frame.\n") ); if (msg.is_active) { WPRINT_LIB_DEBUG( ("(WebSocket) A pending message already exists.\n") ); exit_loop = WICED_TRUE; break; } message_start(&msg, WICED_FALSE, frame.payload, frame.payload_len); if (frame.is_final) { consume_message(&msg, binary_msg_handler, text_msg_handler, ctx); } } else { WPRINT_LIB_INFO(("(WebSocket) Received invalid opcode %02X\n", frame.opcode)); exit_loop = WICED_TRUE; break; } } free(buf); if (exit_loop) { WPRINT_LIB_INFO( ("(WebSocket) Exiting read loop.\n") ); break; } } message_deinit(&msg); }
static wiced_result_t process_handshake_response(websocket_handshake_t *hs, wiced_tcp_socket_t *sock) { char *buf, *cur, *line, *token, *hdr_name, *hdr_val; uint32_t len; const char *val; https_response_t res; wiced_result_t ret = WICED_ERROR; ret = receive_data(sock, HTTPS_READ_TIMEOUT, &buf, &len); if (ret != WICED_SUCCESS) { return ret; } cur = buf; line = get_line(&cur); token = strsep(&line, " "); if (strcmp(token, "HTTP/1.1") && strcmp(token, "HTTP/1.0")) { free(buf); return WICED_ERROR; } token = strsep(&line, " "); if (!token) { free(buf); return WICED_ERROR; } https_init_response(&res); res.status = (uint32_t)atoi(token); while (cur) { line = get_line(&cur); if (*line == '\0') { break; } line += strspn(line, " "); hdr_name = strsep(&line, ":"); if (!*hdr_name || !line) { /* Skip bad header line. */ continue; } hdr_val = line + strspn(line, " "); if (!*hdr_val) { /* Skip bad header line. */ continue; } https_add_response_header(&res, hdr_name, hdr_val); } do { if (res.status != 101) { WPRINT_LIB_INFO( ("Server returned unexpected status %u\n", (unsigned int)res.status) ); break; } val = https_get_response_header(&res, "Connection"); if (!val || strcasecmp(val, "upgrade")) { WPRINT_LIB_INFO( ("Connection header is invalid or missing.\n") ); break; } val = https_get_response_header(&res, "Upgrade"); if (!val || strcasecmp(val, "websocket")) { WPRINT_LIB_INFO( ("Upgrade header is invalid or missing.\n") ); break; } val = https_get_response_header(&res, "Sec-WebSocket-Accept"); if (!val || (verify_websocket_server_key(val, hs->key) != WICED_SUCCESS)) { WPRINT_LIB_INFO( ("Sec-WebSocket-Accept header is invalid or missing.\n") ); break; } ret = WICED_SUCCESS; } while (WICED_FALSE); https_deinit_response(&res); free(buf); return ret; }
static wiced_result_t print_type( const wiced_bt_uuid_t* uuid ) { if ( uuid->size != UUID_16BIT ) { WPRINT_LIB_INFO( ( "Unknown Type" ) ); return WICED_SUCCESS; } switch ( uuid->value.value_16_bit ) { case 0x2800: { WPRINT_LIB_INFO( ( "Primary Service" ) ); break; } case 0x2801: { WPRINT_LIB_INFO( ( "Secondary Service" ) ); break; } case 0x2802: { WPRINT_LIB_INFO( ( "Include" ) ); break; } case 0x2803: { WPRINT_LIB_INFO( ( "Characteristic" ) ); break; } case 0x2900: { WPRINT_LIB_INFO( ( "Characteristic Descriptor - Extended Characteristic Properties" ) ); break; } case 0x2901: { WPRINT_LIB_INFO( ( "Characteristic Descriptor - User Description" ) ); break; } case 0x2902: { WPRINT_LIB_INFO( ( "Characteristic Descriptor - Client Characteristic Configuration" ) ); break; } case 0x2903: { WPRINT_LIB_INFO( ( "Characteristic Descriptor - Server Characteristic Configuration" ) ); break; } case 0x2904: { WPRINT_LIB_INFO( ( "Characteristic Descriptor - Characteristic Presentation Format" ) ); break; } case 0x2905: { WPRINT_LIB_INFO( ( "Characteristic Descriptor - Characteristic Aggregate Format" ) ); break; } case 0x2A00: { WPRINT_LIB_INFO( ( "Characteristic Type - Device Name" ) ); break; } case 0x2A01: { WPRINT_LIB_INFO( ( "Characteristic Type - Appearance" ) ); break; } case 0x2A02: { WPRINT_LIB_INFO( ( "Characteristic Type - Peripheral Privacy Flags" ) ); break; } case 0x2A03: { WPRINT_LIB_INFO( ( "Characteristic Type - Reconnection Address" ) ); break; } case 0x2A04: { WPRINT_LIB_INFO( ( "Characteristic Type - Peripheral Preferred Connection Parameters" ) ); break; } case 0x2A05: { WPRINT_LIB_INFO( ( "Service Changed" ) ); break; } default: { WPRINT_LIB_INFO( ( "Unknown Type" ) ); break; } } return WICED_BT_SUCCESS; }
wiced_result_t wiced_bt_smart_attribute_print( const wiced_bt_smart_attribute_t* attribute ) { wiced_bt_smart_attribute_t* curr_attr = (wiced_bt_smart_attribute_t*)attribute; if ( attribute == NULL ) { return WICED_BT_BADARG; } WPRINT_LIB_INFO( ( "----------------------------------------------------\n" ) ); print_type( &curr_attr->type ); WPRINT_LIB_INFO( ( "\n" ) ); WPRINT_LIB_INFO( ( "Handle : %d\n", (int)curr_attr->handle ) ); WPRINT_LIB_INFO( ( "Type : " ) ); print_uuid( &curr_attr->type ); WPRINT_LIB_INFO( ( "Permission : %d\n", (int)curr_attr->permission ) ); WPRINT_LIB_INFO( ( "Value Length : %d\n", (int)curr_attr->value_length ) ); if ( curr_attr->type.size == UUID_16BIT ) { switch ( curr_attr->type.value.value_16_bit ) { case 0x2800: { WPRINT_LIB_INFO( ( "Start Handle : %d\n", (int)curr_attr->value.service.start_handle ) ); WPRINT_LIB_INFO( ( "End Handle : %d\n", (int)curr_attr->value.service.end_handle ) ); WPRINT_LIB_INFO( ( "Service UUID : ") ); print_uuid( &curr_attr->value.service.uuid ); break; } case 0x2802: { WPRINT_LIB_INFO( ( "Start Handle : %d\n", (int)curr_attr->value.include.included_service_handle ) ); WPRINT_LIB_INFO( ( "End Handle : %d\n", (int)curr_attr->value.include.end_group_handle ) ); WPRINT_LIB_INFO( ( "Service UUID : ") ); print_uuid( &curr_attr->value.include.uuid ); break; } case 0x2803: { WPRINT_LIB_INFO( ( "Properties : %d\n", (int)curr_attr->value.characteristic.properties ) ); WPRINT_LIB_INFO( ( "Value Handle : %d\n", (int)curr_attr->value.characteristic.value_handle ) ); WPRINT_LIB_INFO( ( "Value UUID : ") ); print_uuid( &curr_attr->value.characteristic.uuid ); break; } case 0x2900: { WPRINT_LIB_INFO( ( "Extended Properties : %d\n", (int)curr_attr->value.extended_properties.properties ) ); break; } case 0x2901: { WPRINT_LIB_INFO( ( "Extended Properties : %s\n", curr_attr->value.user_description.string ) ); break; } case 0x2902: { WPRINT_LIB_INFO( ( "Client Configuration : %d\n", (int)curr_attr->value.client_config.config_bits ) ); break; } case 0x2903: { WPRINT_LIB_INFO( ( "Server Configuration : %d\n", (int)curr_attr->value.server_config.config_bits ) ); break; } case 0x2904: { WPRINT_LIB_INFO( ( "Format : %d\n", (int)curr_attr->value.presentation_format.format ) ); WPRINT_LIB_INFO( ( "Exponent : %d\n", (int)curr_attr->value.presentation_format.exponent ) ); WPRINT_LIB_INFO( ( "Unit : %d\n", (int)curr_attr->value.presentation_format.unit ) ); WPRINT_LIB_INFO( ( "Namespace : %d\n", (int)curr_attr->value.presentation_format.name_space ) ); WPRINT_LIB_INFO( ( "Description : %d\n", (int)curr_attr->value.presentation_format.description ) ); break; } case 0x2905: { uint32_t i; WPRINT_LIB_INFO( ( "List of Handles : \n" ) ); for ( i = 0; i < curr_attr->value_length / 2; i ++ ) { WPRINT_LIB_INFO( ( "%02d ", (int)curr_attr->value.aggregate_format.handle_list[i] ) ); } WPRINT_LIB_INFO( ( "\n" ) ); break; } case 0x2A00: { WPRINT_LIB_INFO( ( "Device Name : %s\n", curr_attr->value.device_name.device_name ) ); break; } case 0x2A01: { WPRINT_LIB_INFO( ( "Appearance : %d\n", (int)curr_attr->value.appearance.appearance ) ); break; } case 0x2A02: { WPRINT_LIB_INFO( ( "Peripheral Privacy Flag : %d\n", (int)curr_attr->value.periph_privacy_flag.periph_privacy_flag ) ); break; } case 0x2A03: { WPRINT_LIB_INFO( ( "Reconnection Address : %02x:%02x:%02x:%02x:%02x:%02x\n", (int)curr_attr->value.reconn_address.reconn_address[0], (int)curr_attr->value.reconn_address.reconn_address[1], (int)curr_attr->value.reconn_address.reconn_address[2], (int)curr_attr->value.reconn_address.reconn_address[3], (int)curr_attr->value.reconn_address.reconn_address[4], (int)curr_attr->value.reconn_address.reconn_address[5] ) ); break; } case 0x2A04: { WPRINT_LIB_INFO( ( "Max Connection Interval : %d\n", (int)curr_attr->value.periph_preferred_conn_params.max_conn_interval ) ); WPRINT_LIB_INFO( ( "Min Connection Interval : %d\n", (int)curr_attr->value.periph_preferred_conn_params.min_conn_interval ) ); WPRINT_LIB_INFO( ( "Slave Latency : %d\n", (int)curr_attr->value.periph_preferred_conn_params.slave_latency ) ); WPRINT_LIB_INFO( ( "Supervision Timeout Multiplier : %d\n", (int)curr_attr->value.periph_preferred_conn_params.conn_supervision_timeout_multiplier ) ); break; } default: { uint32_t i; WPRINT_LIB_INFO( ( "Value : \n" ) ); for ( i = 0; i < curr_attr->value_length; i ++ ) { WPRINT_LIB_INFO( ( "%02x ", (int)curr_attr->value.value[i] ) ); } WPRINT_LIB_INFO( ( "\n" ) ); break; } } } WPRINT_LIB_INFO( ( "----------------------------------------------------\n" ) ); return WICED_BT_SUCCESS; }
wiced_result_t bt_smartbridge_att_cache_enable( uint32_t cache_count ) { uint32_t a; wiced_result_t result; bt_smartbridge_att_cache_manager_t* manager; if ( att_cache_manager != NULL ) { return WICED_BT_SUCCESS; } manager = (bt_smartbridge_att_cache_manager_t*)malloc_named( "att_cache", CALCULATE_ATT_CACHE_MANAGER_SIZE( cache_count ) ); if ( manager == NULL ) { return WICED_BT_OUT_OF_HEAP_SPACE; } memset( manager, 0, CALCULATE_ATT_CACHE_MANAGER_SIZE( cache_count ) ); att_cache_manager = manager; manager->count = cache_count; result = bt_linked_list_init( &manager->free_list ); if ( result != WICED_BT_SUCCESS ) { WPRINT_LIB_INFO( ( "Error creating linked list\n" ) ); goto error; } result = bt_linked_list_init( &manager->used_list ); if ( result != WICED_BT_SUCCESS ) { WPRINT_LIB_INFO( ( "Error creating linked list\n" ) ); goto error; } result = wiced_rtos_init_mutex( &manager->mutex ); if ( result != WICED_SUCCESS ) { WPRINT_LIB_INFO( ( "Error creating mutex\n" ) ); goto error; } /* Initialise mutexes for protecting access to cached attributes */ for ( a = 0; a < manager->count; a++ ) { result = wiced_rtos_init_mutex( &manager->pool[a].mutex ); if ( result != WICED_BT_SUCCESS ) { goto error; } /* Point node data to cached attribute instance */ manager->pool[a].node.data = (void*)&manager->pool[a]; /* Insert cached attribute instance into free list */ result = bt_linked_list_insert_at_rear( &manager->free_list, &manager->pool[a].node ); if ( result != WICED_BT_SUCCESS ) { goto error; } } return WICED_BT_SUCCESS; error: bt_smartbridge_att_cache_disable(); return result; }