/* * Copyright (C) 2014 BlueKitchen GmbH * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holders nor the names of * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * 4. Any redistribution, use, or modification is done solely for * personal benefit and not for any commercial purpose or for * monetary gain. * * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS * RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * Please inquire about commercial licensing options at * contact@bluekitchen-gmbh.com * */ #define __BTSTACK_FILE__ "mesh.c" #include #include #include #include #include "mesh/adv_bearer.h" #include "mesh/gatt_bearer.h" #include "mesh/beacon.h" #include "mesh/mesh_crypto.h" #include "mesh/mesh_lower_transport.h" #include "mesh/mesh_upper_transport.h" #include "mesh/pb_adv.h" #include "mesh/pb_gatt.h" #include "ble/gatt-service/mesh_provisioning_service_server.h" #include "provisioning.h" #include "provisioning_device.h" #include "mesh_foundation.h" #include "mesh_iv_index_seq_number.h" #include "mesh_configuration_server.h" #include "mesh_generic_server.h" #include "mesh_access.h" #include "mesh_virtual_addresses.h" #include "mesh_peer.h" #include "mesh_proxy.h" #include "mesh_generic_model.h" #include "mesh.h" #include "btstack.h" #include "btstack_tlv.h" #define PTS_DEFAULT_TTL 10 static void show_usage(void); const static uint8_t test_device_uuid[] = { 0x00, 0x1B, 0xDC, 0x08, 0x10, 0x21, 0x0B, 0x0E, 0x0A, 0x0C, 0x00, 0x0B, 0x0E, 0x0A, 0x0C, 0x00 }; static btstack_packet_callback_registration_t hci_event_callback_registration; static void packet_handler (uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size); static uint16_t pb_transport_cid = MESH_PB_TRANSPORT_INVALID_CID; // pin entry static int ui_chars_for_pin; static uint8_t ui_pin[17]; static int ui_pin_offset; static uint16_t primary_element_address; static int provisioned; // Test configuration #define MESH_BLUEKITCHEN_MODEL_ID_TEST_SERVER 0x0000u static mesh_configuration_server_model_context_t mesh_configuration_server_model_context; static mesh_model_t mesh_configuration_server_model; static mesh_model_t mesh_health_server_model; static mesh_model_t mesh_vendor_model; static mesh_model_t mesh_generic_on_off_server_model; static mesh_generic_on_off_state_t mesh_generic_on_off_state; // static void mesh_print_x(const char * name, uint32_t value){ // printf("%20s: 0x%x", name, (int) value); // } static void mesh_network_key_dump(const mesh_network_key_t * key){ printf("NetKey: "); printf_hexdump(key->net_key, 16); printf("-- Derived from NetKey --\n"); printf("NID: 0x%02x\n", key->nid); printf("NetworkID: "); printf_hexdump(key->network_id, 8); printf("BeaconKey: "); printf_hexdump(key->beacon_key, 16); printf("EncryptionKey: "); printf_hexdump(key->encryption_key, 16); printf("PrivacyKey: "); printf_hexdump(key->privacy_key, 16); printf("IdentityKey: "); printf_hexdump(key->identity_key, 16); } static void mesh_provisioning_dump(const mesh_provisioning_data_t * data){ printf("UnicastAddr: 0x%02x\n", data->unicast_address); printf("DevKey: "); printf_hexdump(data->device_key, 16); printf("Flags: 0x%02x\n", data->flags); } // helper network layer, temp static uint8_t mesh_network_send(uint16_t netkey_index, uint8_t ctl, uint8_t ttl, uint32_t seq, uint16_t src, uint16_t dest, const uint8_t * transport_pdu_data, uint8_t transport_pdu_len){ // "3.4.5.2: The output filter of the interface connected to advertising or GATT bearers shall drop all messages with TTL value set to 1." // if (ttl <= 1) return 0; // TODO: check transport_pdu_len depending on ctl // lookup network by netkey_index const mesh_network_key_t * network_key = mesh_network_key_list_get(netkey_index); if (!network_key) return 0; // allocate network_pdu mesh_network_pdu_t * network_pdu = mesh_network_pdu_get(); if (!network_pdu) return 0; // setup network_pdu mesh_network_setup_pdu(network_pdu, netkey_index, network_key->nid, ctl, ttl, seq, src, dest, transport_pdu_data, transport_pdu_len); // send network_pdu mesh_lower_transport_send_pdu((mesh_pdu_t *) network_pdu); return 0; } static void printf_hex(const uint8_t * data, uint16_t len){ while (len){ printf("%02x", *data); data++; len--; } printf("\n"); } static void mesh_pts_dump_mesh_options(void){ printf("\nMeshOptions.ini\n"); printf("[mesh]\n"); printf("{IVindex}\n"); printf("%08x\n", mesh_get_iv_index()); mesh_network_key_t * network_key = mesh_network_key_list_get(0); if (network_key){ printf("{NetKey}\n"); printf_hex(network_key->net_key, 16); } mesh_transport_key_t * transport_key = mesh_transport_key_get(0); if (transport_key){ printf("{AppKey}\n"); printf_hex(transport_key->key, 16); } mesh_transport_key_t * device_key = mesh_transport_key_get(MESH_DEVICE_KEY_INDEX); if (device_key){ printf("{DevKey}\n"); printf_hex(device_key->key, 16); } printf("\n"); } static void packet_handler (uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){ UNUSED(channel); UNUSED(size); bd_addr_t addr; int i; switch (packet_type) { case HCI_EVENT_PACKET: switch (hci_event_packet_get_type(packet)) { case BTSTACK_EVENT_STATE: if (btstack_event_state_get_state(packet) != HCI_STATE_WORKING) break; // dump bd_addr in pts format gap_local_bd_addr(addr); printf("Local addr: %s - ", bd_addr_to_str(addr)); for (i=0;i<6;i++) { printf("%02x", addr[i]); } printf("\n"); // startup from provisioning data stored in TLV provisioned = mesh_node_startup_from_tlv(); if (provisioned){ // dump PTS MeshOptions.ini mesh_pts_dump_mesh_options(); } else { mesh_access_setup_without_provisiong_data(test_device_uuid); } #if defined(ENABLE_MESH_ADV_BEARER) || defined(ENABLE_MESH_PB_ADV) // setup scanning gap_set_scan_parameters(0, 0x300, 0x300); gap_start_scan(); #endif // show_usage(); break; case HCI_EVENT_DISCONNECTION_COMPLETE: // enable PB_GATT if (provisioned == 0){ printf("Advertise Mesh Provisiong Service with Device UUID\n"); mesh_proxy_start_advertising_unprovisioned_device(test_device_uuid); } else { #ifdef ENABLE_MESH_PROXY_SERVER printf("Advertise Mesh Proxy Service with Network ID\n"); mesh_proxy_start_advertising_with_network_id(); #endif } break; case HCI_EVENT_LE_META: if (hci_event_le_meta_get_subevent_code(packet) != HCI_SUBEVENT_LE_CONNECTION_COMPLETE) break; // disable PB_GATT printf("Connected, stop advertising GATT service\n"); mesh_proxy_stop_advertising_unprovisioned_device(); break; default: break; } break; } } static void mesh_provisioning_message_handler (uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){ if (packet_type != HCI_EVENT_PACKET) return; mesh_provisioning_data_t provisioning_data; mesh_network_key_t * primary_network_key; mesh_subnet_t * primary_subnet; switch(packet[0]){ case HCI_EVENT_MESH_META: switch(packet[2]){ case MESH_SUBEVENT_PB_TRANSPORT_LINK_OPEN: printf("Provisioner link opened"); pb_transport_cid = mesh_subevent_pb_transport_link_open_get_pb_transport_cid(packet); break; case MESH_SUBEVENT_PB_TRANSPORT_LINK_CLOSED: pb_transport_cid = MESH_PB_TRANSPORT_INVALID_CID; break; case MESH_SUBEVENT_PB_PROV_ATTENTION_TIMER: printf("Attention Timer: %u\n", packet[3]); break; case MESH_SUBEVENT_PB_PROV_INPUT_OOB_REQUEST: printf("Enter passphrase: "); fflush(stdout); ui_chars_for_pin = 1; ui_pin_offset = 0; break; case MESH_SUBEVENT_PB_PROV_COMPLETE: printf("Provisioning complete\n"); // delete old data mesh_delete_network_keys(); mesh_delete_app_keys(); mesh_delete_appkey_lists(); // get provisioning data memcpy(provisioning_data.device_key, provisioning_device_data_get_device_key(), 16); provisioning_data.flags = provisioning_device_data_get_flags(); provisioning_data.unicast_address = provisioning_device_data_get_unicast_address(); // set iv_index mesh_set_iv_index(provisioning_device_data_get_iv_index()); // get primary netkey primary_network_key = provisioning_device_data_get_network_key(); mesh_network_key_dump(primary_network_key); // add to network keys mesh_network_key_add(primary_network_key); // setup primary network mesh_subnet_setup_for_netkey_index(primary_network_key->netkey_index); // setup after provisioned mesh_access_setup_from_provisioning_data(&provisioning_data); // store provisioning data and primary network key in TLV mesh_node_store_provisioning_data(); // store IV Index and sequence number mesh_store_iv_index_and_sequence_number(); // store primary network key mesh_store_network_key(primary_network_key); // dump data mesh_provisioning_dump(&provisioning_data); provisioned = 1; // start advertising with node id after provisioning mesh_proxy_set_advertising_with_node_id(primary_network_key->netkey_index, MESH_NODE_IDENTITY_STATE_ADVERTISING_RUNNING); // start sending Secure Network Beacons primary_subnet = mesh_subnet_get_by_netkey_index(0); beacon_secure_network_start(primary_subnet); break; default: break; } break; default: break; } } static void mesh_state_update_message_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){ if (packet_type != HCI_EVENT_PACKET) return; switch(packet[0]){ case HCI_EVENT_MESH_META: switch(packet[2]){ case MESH_SUBEVENT_STATE_UPDATE_BOOL: printf("State update: model identifier 0x%08x, state identifier 0x%08x, reason %u, state %u\n", mesh_subevent_state_update_bool_get_model_identifier(packet), mesh_subevent_state_update_bool_get_state_identifier(packet), mesh_subevent_state_update_bool_get_reason(packet), mesh_subevent_state_update_bool_get_value(packet)); break; default: break; } break; default: break; } } static void mesh_unprovisioned_beacon_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){ if (packet_type != MESH_BEACON_PACKET) return; uint8_t device_uuid[16]; uint16_t oob; memcpy(device_uuid, &packet[1], 16); oob = big_endian_read_16(packet, 17); printf("received unprovisioned device beacon, oob data %x, device uuid: ", oob); printf_hexdump(device_uuid, 16); pb_adv_create_link(device_uuid); } uint8_t pts_device_uuid[16]; const char * pts_device_uuid_string = "001BDC0810210B0E0A0C000B0E0A0C00"; static int scan_hex_byte(const char * byte_string){ int upper_nibble = nibble_for_char(*byte_string++); if (upper_nibble < 0) return -1; int lower_nibble = nibble_for_char(*byte_string); if (lower_nibble < 0) return -1; return (upper_nibble << 4) | lower_nibble; } static int btstack_parse_hex(const char * string, uint16_t len, uint8_t * buffer){ int i; for (i = 0; i < len; i++) { int single_byte = scan_hex_byte(string); if (single_byte < 0) return 0; string += 2; buffer[i] = (uint8_t)single_byte; // don't check seperator after last byte if (i == len - 1) { return 1; } // optional seperator char separator = *string; if (separator == ':' && separator == '-' && separator == ' ') { string++; } } return 1; } static void btstack_print_hex(const uint8_t * data, uint16_t len, char separator){ int i; for (i=0;ipseudo_dst; // PTS Device UUID btstack_parse_hex(pts_device_uuid_string, 16, pts_device_uuid); btstack_print_hex(pts_device_uuid, 16, 0); // turn on! hci_power_control(HCI_POWER_ON); return 0; } /* EXAMPLE_END */