1*1fbe4564SMatthias Ringwald /* 2*1fbe4564SMatthias Ringwald * Copyright (C) 2014 BlueKitchen GmbH 3*1fbe4564SMatthias Ringwald * 4*1fbe4564SMatthias Ringwald * Redistribution and use in source and binary forms, with or without 5*1fbe4564SMatthias Ringwald * modification, are permitted provided that the following conditions 6*1fbe4564SMatthias Ringwald * are met: 7*1fbe4564SMatthias Ringwald * 8*1fbe4564SMatthias Ringwald * 1. Redistributions of source code must retain the above copyright 9*1fbe4564SMatthias Ringwald * notice, this list of conditions and the following disclaimer. 10*1fbe4564SMatthias Ringwald * 2. Redistributions in binary form must reproduce the above copyright 11*1fbe4564SMatthias Ringwald * notice, this list of conditions and the following disclaimer in the 12*1fbe4564SMatthias Ringwald * documentation and/or other materials provided with the distribution. 13*1fbe4564SMatthias Ringwald * 3. Neither the name of the copyright holders nor the names of 14*1fbe4564SMatthias Ringwald * contributors may be used to endorse or promote products derived 15*1fbe4564SMatthias Ringwald * from this software without specific prior written permission. 16*1fbe4564SMatthias Ringwald * 4. Any redistribution, use, or modification is done solely for 17*1fbe4564SMatthias Ringwald * personal benefit and not for any commercial purpose or for 18*1fbe4564SMatthias Ringwald * monetary gain. 19*1fbe4564SMatthias Ringwald * 20*1fbe4564SMatthias Ringwald * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS 21*1fbe4564SMatthias Ringwald * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22*1fbe4564SMatthias Ringwald * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 23*1fbe4564SMatthias Ringwald * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS 24*1fbe4564SMatthias Ringwald * RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 25*1fbe4564SMatthias Ringwald * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 26*1fbe4564SMatthias Ringwald * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS 27*1fbe4564SMatthias Ringwald * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 28*1fbe4564SMatthias Ringwald * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 29*1fbe4564SMatthias Ringwald * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 30*1fbe4564SMatthias Ringwald * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31*1fbe4564SMatthias Ringwald * SUCH DAMAGE. 32*1fbe4564SMatthias Ringwald * 33*1fbe4564SMatthias Ringwald * Please inquire about commercial licensing options at 34*1fbe4564SMatthias Ringwald * [email protected] 35*1fbe4564SMatthias Ringwald * 36*1fbe4564SMatthias Ringwald */ 37*1fbe4564SMatthias Ringwald 38*1fbe4564SMatthias Ringwald #define __BTSTACK_FILE__ "gap_le_advertisements.c" 39*1fbe4564SMatthias Ringwald 40*1fbe4564SMatthias Ringwald 41*1fbe4564SMatthias Ringwald // ***************************************************************************** 42*1fbe4564SMatthias Ringwald /* EXAMPLE_START(gap_le_advertisements): GAP LE Advertisements Dumper 43*1fbe4564SMatthias Ringwald * 44*1fbe4564SMatthias Ringwald * @text This example shows how to scan and parse advertisements. 45*1fbe4564SMatthias Ringwald * 46*1fbe4564SMatthias Ringwald */ 47*1fbe4564SMatthias Ringwald // ***************************************************************************** 48*1fbe4564SMatthias Ringwald 49*1fbe4564SMatthias Ringwald 50*1fbe4564SMatthias Ringwald #include <stdint.h> 51*1fbe4564SMatthias Ringwald #include <inttypes.h> 52*1fbe4564SMatthias Ringwald #include <stdio.h> 53*1fbe4564SMatthias Ringwald #include <stdlib.h> 54*1fbe4564SMatthias Ringwald #include <string.h> 55*1fbe4564SMatthias Ringwald 56*1fbe4564SMatthias Ringwald #include "btstack.h" 57*1fbe4564SMatthias Ringwald 58*1fbe4564SMatthias Ringwald static btstack_packet_callback_registration_t hci_event_callback_registration; 59*1fbe4564SMatthias Ringwald 60*1fbe4564SMatthias Ringwald #define NUM_NODES 2 61*1fbe4564SMatthias Ringwald 62*1fbe4564SMatthias Ringwald static const char * nodes_string[NUM_NODES] = { 63*1fbe4564SMatthias Ringwald "5C:F3:70:60:7B:87", 64*1fbe4564SMatthias Ringwald "00:1B:DC:07:32:EF", 65*1fbe4564SMatthias Ringwald }; 66*1fbe4564SMatthias Ringwald static bd_addr_t nodes_addr[NUM_NODES]; 67*1fbe4564SMatthias Ringwald 68*1fbe4564SMatthias Ringwald /* @section GAP LE setup for receiving advertisements 69*1fbe4564SMatthias Ringwald * 70*1fbe4564SMatthias Ringwald * @text GAP LE advertisements are received as custom HCI events of the 71*1fbe4564SMatthias Ringwald * GAP_EVENT_ADVERTISING_REPORT type. To receive them, you'll need to register 72*1fbe4564SMatthias Ringwald * the HCI packet handler, as shown in Listing GAPLEAdvSetup. 73*1fbe4564SMatthias Ringwald */ 74*1fbe4564SMatthias Ringwald 75*1fbe4564SMatthias Ringwald static char * ad_types[] = { 76*1fbe4564SMatthias Ringwald "", 77*1fbe4564SMatthias Ringwald "Flags", 78*1fbe4564SMatthias Ringwald "Incomplete List of 16-bit Service Class UUIDs", 79*1fbe4564SMatthias Ringwald "Complete List of 16-bit Service Class UUIDs", 80*1fbe4564SMatthias Ringwald "Incomplete List of 32-bit Service Class UUIDs", 81*1fbe4564SMatthias Ringwald "Complete List of 32-bit Service Class UUIDs", 82*1fbe4564SMatthias Ringwald "Incomplete List of 128-bit Service Class UUIDs", 83*1fbe4564SMatthias Ringwald "Complete List of 128-bit Service Class UUIDs", 84*1fbe4564SMatthias Ringwald "Shortened Local Name", 85*1fbe4564SMatthias Ringwald "Complete Local Name", 86*1fbe4564SMatthias Ringwald "Tx Power Level", 87*1fbe4564SMatthias Ringwald "", 88*1fbe4564SMatthias Ringwald "", 89*1fbe4564SMatthias Ringwald "Class of Device", 90*1fbe4564SMatthias Ringwald "Simple Pairing Hash C", 91*1fbe4564SMatthias Ringwald "Simple Pairing Randomizer R", 92*1fbe4564SMatthias Ringwald "Device ID", 93*1fbe4564SMatthias Ringwald "Security Manager TK Value", 94*1fbe4564SMatthias Ringwald "Slave Connection Interval Range", 95*1fbe4564SMatthias Ringwald "", 96*1fbe4564SMatthias Ringwald "List of 16-bit Service Solicitation UUIDs", 97*1fbe4564SMatthias Ringwald "List of 128-bit Service Solicitation UUIDs", 98*1fbe4564SMatthias Ringwald "Service Data", 99*1fbe4564SMatthias Ringwald "Public Target Address", 100*1fbe4564SMatthias Ringwald "Random Target Address", 101*1fbe4564SMatthias Ringwald "Appearance", 102*1fbe4564SMatthias Ringwald "Advertising Interval" 103*1fbe4564SMatthias Ringwald }; 104*1fbe4564SMatthias Ringwald 105*1fbe4564SMatthias Ringwald static char * flags[] = { 106*1fbe4564SMatthias Ringwald "LE Limited Discoverable Mode", 107*1fbe4564SMatthias Ringwald "LE General Discoverable Mode", 108*1fbe4564SMatthias Ringwald "BR/EDR Not Supported", 109*1fbe4564SMatthias Ringwald "Simultaneous LE and BR/EDR to Same Device Capable (Controller)", 110*1fbe4564SMatthias Ringwald "Simultaneous LE and BR/EDR to Same Device Capable (Host)", 111*1fbe4564SMatthias Ringwald "Reserved", 112*1fbe4564SMatthias Ringwald "Reserved", 113*1fbe4564SMatthias Ringwald "Reserved" 114*1fbe4564SMatthias Ringwald }; 115*1fbe4564SMatthias Ringwald 116*1fbe4564SMatthias Ringwald /* LISTING_START(GAPLEAdvSetup): Setting up GAP LE client for receiving advertisements */ 117*1fbe4564SMatthias Ringwald static void packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size); 118*1fbe4564SMatthias Ringwald 119*1fbe4564SMatthias Ringwald static void sniffer_setup(void){ 120*1fbe4564SMatthias Ringwald hci_event_callback_registration.callback = &packet_handler; 121*1fbe4564SMatthias Ringwald hci_add_event_handler(&hci_event_callback_registration); 122*1fbe4564SMatthias Ringwald // Active scanning, 100% (scan interval = scan window) 123*1fbe4564SMatthias Ringwald gap_set_scan_parameters(1,48,48); 124*1fbe4564SMatthias Ringwald // 125*1fbe4564SMatthias Ringwald int i; 126*1fbe4564SMatthias Ringwald for (i=0;i<NUM_NODES;i++){ 127*1fbe4564SMatthias Ringwald sscanf_bd_addr(nodes_string[i], nodes_addr[i]); 128*1fbe4564SMatthias Ringwald printf("Listening for %s\n", bd_addr_to_str(nodes_addr[i])); 129*1fbe4564SMatthias Ringwald } 130*1fbe4564SMatthias Ringwald } 131*1fbe4564SMatthias Ringwald 132*1fbe4564SMatthias Ringwald static int addr_from_list(bd_addr_t addr){ 133*1fbe4564SMatthias Ringwald int i; 134*1fbe4564SMatthias Ringwald for (i=0;i<NUM_NODES;i++){ 135*1fbe4564SMatthias Ringwald if (memcmp(addr, nodes_addr[i], 6) == 0) return 1; 136*1fbe4564SMatthias Ringwald } 137*1fbe4564SMatthias Ringwald return 0; 138*1fbe4564SMatthias Ringwald } 139*1fbe4564SMatthias Ringwald 140*1fbe4564SMatthias Ringwald /* LISTING_END */ 141*1fbe4564SMatthias Ringwald 142*1fbe4564SMatthias Ringwald 143*1fbe4564SMatthias Ringwald /* LISTING_START(GAPLEAdvDataParsing): Parsing advertising data */ 144*1fbe4564SMatthias Ringwald static void dump_advertisement_data(const uint8_t * adv_data, uint8_t adv_size){ 145*1fbe4564SMatthias Ringwald ad_context_t context; 146*1fbe4564SMatthias Ringwald bd_addr_t address; 147*1fbe4564SMatthias Ringwald uint8_t uuid_128[16]; 148*1fbe4564SMatthias Ringwald for (ad_iterator_init(&context, adv_size, (uint8_t *)adv_data) ; ad_iterator_has_more(&context) ; ad_iterator_next(&context)){ 149*1fbe4564SMatthias Ringwald uint8_t data_type = ad_iterator_get_data_type(&context); 150*1fbe4564SMatthias Ringwald uint8_t size = ad_iterator_get_data_len(&context); 151*1fbe4564SMatthias Ringwald const uint8_t * data = ad_iterator_get_data(&context); 152*1fbe4564SMatthias Ringwald 153*1fbe4564SMatthias Ringwald if (data_type > 0 && data_type < 0x1B){ 154*1fbe4564SMatthias Ringwald printf(" %s: ", ad_types[data_type]); 155*1fbe4564SMatthias Ringwald } 156*1fbe4564SMatthias Ringwald int i; 157*1fbe4564SMatthias Ringwald // Assigned Numbers GAP 158*1fbe4564SMatthias Ringwald 159*1fbe4564SMatthias Ringwald switch (data_type){ 160*1fbe4564SMatthias Ringwald case BLUETOOTH_DATA_TYPE_FLAGS: 161*1fbe4564SMatthias Ringwald // show only first octet, ignore rest 162*1fbe4564SMatthias Ringwald for (i=0; i<8;i++){ 163*1fbe4564SMatthias Ringwald if (data[0] & (1<<i)){ 164*1fbe4564SMatthias Ringwald printf("%s; ", flags[i]); 165*1fbe4564SMatthias Ringwald } 166*1fbe4564SMatthias Ringwald 167*1fbe4564SMatthias Ringwald } 168*1fbe4564SMatthias Ringwald break; 169*1fbe4564SMatthias Ringwald case BLUETOOTH_DATA_TYPE_INCOMPLETE_LIST_OF_16_BIT_SERVICE_CLASS_UUIDS: 170*1fbe4564SMatthias Ringwald case BLUETOOTH_DATA_TYPE_COMPLETE_LIST_OF_16_BIT_SERVICE_CLASS_UUIDS: 171*1fbe4564SMatthias Ringwald case BLUETOOTH_DATA_TYPE_LIST_OF_16_BIT_SERVICE_SOLICITATION_UUIDS: 172*1fbe4564SMatthias Ringwald for (i=0; i<size;i+=2){ 173*1fbe4564SMatthias Ringwald printf("%02X ", little_endian_read_16(data, i)); 174*1fbe4564SMatthias Ringwald } 175*1fbe4564SMatthias Ringwald break; 176*1fbe4564SMatthias Ringwald case BLUETOOTH_DATA_TYPE_INCOMPLETE_LIST_OF_32_BIT_SERVICE_CLASS_UUIDS: 177*1fbe4564SMatthias Ringwald case BLUETOOTH_DATA_TYPE_COMPLETE_LIST_OF_32_BIT_SERVICE_CLASS_UUIDS: 178*1fbe4564SMatthias Ringwald case BLUETOOTH_DATA_TYPE_LIST_OF_32_BIT_SERVICE_SOLICITATION_UUIDS: 179*1fbe4564SMatthias Ringwald for (i=0; i<size;i+=4){ 180*1fbe4564SMatthias Ringwald printf("%04"PRIX32, little_endian_read_32(data, i)); 181*1fbe4564SMatthias Ringwald } 182*1fbe4564SMatthias Ringwald break; 183*1fbe4564SMatthias Ringwald case BLUETOOTH_DATA_TYPE_INCOMPLETE_LIST_OF_128_BIT_SERVICE_CLASS_UUIDS: 184*1fbe4564SMatthias Ringwald case BLUETOOTH_DATA_TYPE_COMPLETE_LIST_OF_128_BIT_SERVICE_CLASS_UUIDS: 185*1fbe4564SMatthias Ringwald case BLUETOOTH_DATA_TYPE_LIST_OF_128_BIT_SERVICE_SOLICITATION_UUIDS: 186*1fbe4564SMatthias Ringwald reverse_128(data, uuid_128); 187*1fbe4564SMatthias Ringwald printf("%s", uuid128_to_str(uuid_128)); 188*1fbe4564SMatthias Ringwald break; 189*1fbe4564SMatthias Ringwald case BLUETOOTH_DATA_TYPE_SHORTENED_LOCAL_NAME: 190*1fbe4564SMatthias Ringwald case BLUETOOTH_DATA_TYPE_COMPLETE_LOCAL_NAME: 191*1fbe4564SMatthias Ringwald for (i=0; i<size;i++){ 192*1fbe4564SMatthias Ringwald printf("%c", (char)(data[i])); 193*1fbe4564SMatthias Ringwald } 194*1fbe4564SMatthias Ringwald break; 195*1fbe4564SMatthias Ringwald case BLUETOOTH_DATA_TYPE_TX_POWER_LEVEL: 196*1fbe4564SMatthias Ringwald printf("%d dBm", *(int8_t*)data); 197*1fbe4564SMatthias Ringwald break; 198*1fbe4564SMatthias Ringwald case BLUETOOTH_DATA_TYPE_SLAVE_CONNECTION_INTERVAL_RANGE: 199*1fbe4564SMatthias Ringwald printf("Connection Interval Min = %u ms, Max = %u ms", little_endian_read_16(data, 0) * 5/4, little_endian_read_16(data, 2) * 5/4); 200*1fbe4564SMatthias Ringwald break; 201*1fbe4564SMatthias Ringwald case BLUETOOTH_DATA_TYPE_SERVICE_DATA: 202*1fbe4564SMatthias Ringwald printf_hexdump(data, size); 203*1fbe4564SMatthias Ringwald break; 204*1fbe4564SMatthias Ringwald case BLUETOOTH_DATA_TYPE_PUBLIC_TARGET_ADDRESS: 205*1fbe4564SMatthias Ringwald case BLUETOOTH_DATA_TYPE_RANDOM_TARGET_ADDRESS: 206*1fbe4564SMatthias Ringwald reverse_bd_addr(data, address); 207*1fbe4564SMatthias Ringwald printf("%s", bd_addr_to_str(address)); 208*1fbe4564SMatthias Ringwald break; 209*1fbe4564SMatthias Ringwald case BLUETOOTH_DATA_TYPE_APPEARANCE: 210*1fbe4564SMatthias Ringwald // https://developer.bluetooth.org/gatt/characteristics/Pages/CharacteristicViewer.aspx?u=org.bluetooth.characteristic.gap.appearance.xml 211*1fbe4564SMatthias Ringwald printf("%02X", little_endian_read_16(data, 0) ); 212*1fbe4564SMatthias Ringwald break; 213*1fbe4564SMatthias Ringwald case BLUETOOTH_DATA_TYPE_ADVERTISING_INTERVAL: 214*1fbe4564SMatthias Ringwald printf("%u ms", little_endian_read_16(data, 0) * 5/8 ); 215*1fbe4564SMatthias Ringwald break; 216*1fbe4564SMatthias Ringwald case BLUETOOTH_DATA_TYPE_3D_INFORMATION_DATA: 217*1fbe4564SMatthias Ringwald printf_hexdump(data, size); 218*1fbe4564SMatthias Ringwald break; 219*1fbe4564SMatthias Ringwald case BLUETOOTH_DATA_TYPE_MANUFACTURER_SPECIFIC_DATA: // Manufacturer Specific Data 220*1fbe4564SMatthias Ringwald break; 221*1fbe4564SMatthias Ringwald case BLUETOOTH_DATA_TYPE_CLASS_OF_DEVICE: 222*1fbe4564SMatthias Ringwald case BLUETOOTH_DATA_TYPE_SIMPLE_PAIRING_HASH_C: 223*1fbe4564SMatthias Ringwald case BLUETOOTH_DATA_TYPE_SIMPLE_PAIRING_RANDOMIZER_R: 224*1fbe4564SMatthias Ringwald case BLUETOOTH_DATA_TYPE_DEVICE_ID: 225*1fbe4564SMatthias Ringwald case BLUETOOTH_DATA_TYPE_SECURITY_MANAGER_OUT_OF_BAND_FLAGS: 226*1fbe4564SMatthias Ringwald default: 227*1fbe4564SMatthias Ringwald printf("Advertising Data Type 0x%2x not handled yet", data_type); 228*1fbe4564SMatthias Ringwald break; 229*1fbe4564SMatthias Ringwald } 230*1fbe4564SMatthias Ringwald printf("\n"); 231*1fbe4564SMatthias Ringwald } 232*1fbe4564SMatthias Ringwald printf("\n"); 233*1fbe4564SMatthias Ringwald } 234*1fbe4564SMatthias Ringwald /* LISTING_END */ 235*1fbe4564SMatthias Ringwald 236*1fbe4564SMatthias Ringwald /* @section HCI packet handler 237*1fbe4564SMatthias Ringwald * 238*1fbe4564SMatthias Ringwald * @text The HCI packet handler has to start the scanning, 239*1fbe4564SMatthias Ringwald * and to handle received advertisements. Advertisements are received 240*1fbe4564SMatthias Ringwald * as HCI event packets of the GAP_EVENT_ADVERTISING_REPORT type, 241*1fbe4564SMatthias Ringwald * see Listing GAPLEAdvPacketHandler. 242*1fbe4564SMatthias Ringwald */ 243*1fbe4564SMatthias Ringwald 244*1fbe4564SMatthias Ringwald /* LISTING_START(GAPLEAdvPacketHandler): Scanning and receiving advertisements */ 245*1fbe4564SMatthias Ringwald 246*1fbe4564SMatthias Ringwald static void packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){ 247*1fbe4564SMatthias Ringwald UNUSED(channel); 248*1fbe4564SMatthias Ringwald UNUSED(size); 249*1fbe4564SMatthias Ringwald 250*1fbe4564SMatthias Ringwald if (packet_type != HCI_EVENT_PACKET) return; 251*1fbe4564SMatthias Ringwald 252*1fbe4564SMatthias Ringwald switch (hci_event_packet_get_type(packet)) { 253*1fbe4564SMatthias Ringwald case BTSTACK_EVENT_STATE: 254*1fbe4564SMatthias Ringwald // BTstack activated, get started 255*1fbe4564SMatthias Ringwald if (btstack_event_state_get_state(packet) == HCI_STATE_WORKING){ 256*1fbe4564SMatthias Ringwald printf("Start scaning!\n"); 257*1fbe4564SMatthias Ringwald gap_set_scan_parameters(0,0x0030, 0x0030); 258*1fbe4564SMatthias Ringwald gap_start_scan(); 259*1fbe4564SMatthias Ringwald } 260*1fbe4564SMatthias Ringwald break; 261*1fbe4564SMatthias Ringwald case GAP_EVENT_ADVERTISING_REPORT:{ 262*1fbe4564SMatthias Ringwald bd_addr_t address; 263*1fbe4564SMatthias Ringwald gap_event_advertising_report_get_address(packet, address); 264*1fbe4564SMatthias Ringwald // check if in list 265*1fbe4564SMatthias Ringwald if (!addr_from_list(address)) break; 266*1fbe4564SMatthias Ringwald 267*1fbe4564SMatthias Ringwald uint8_t event_type = gap_event_advertising_report_get_advertising_event_type(packet); 268*1fbe4564SMatthias Ringwald uint8_t address_type = gap_event_advertising_report_get_address_type(packet); 269*1fbe4564SMatthias Ringwald int8_t rssi = gap_event_advertising_report_get_rssi(packet); 270*1fbe4564SMatthias Ringwald uint8_t length = gap_event_advertising_report_get_data_length(packet); 271*1fbe4564SMatthias Ringwald const uint8_t * data = gap_event_advertising_report_get_data(packet); 272*1fbe4564SMatthias Ringwald 273*1fbe4564SMatthias Ringwald printf("Advertisement event: evt-type %u, addr-type %u, addr %s, rssi %d, data[%u] ", event_type, 274*1fbe4564SMatthias Ringwald address_type, bd_addr_to_str(address), rssi, length); 275*1fbe4564SMatthias Ringwald printf_hexdump(data, length); 276*1fbe4564SMatthias Ringwald dump_advertisement_data(data, length); 277*1fbe4564SMatthias Ringwald break; 278*1fbe4564SMatthias Ringwald } 279*1fbe4564SMatthias Ringwald default: 280*1fbe4564SMatthias Ringwald break; 281*1fbe4564SMatthias Ringwald } 282*1fbe4564SMatthias Ringwald } 283*1fbe4564SMatthias Ringwald /* LISTING_END */ 284*1fbe4564SMatthias Ringwald 285*1fbe4564SMatthias Ringwald int btstack_main(void); 286*1fbe4564SMatthias Ringwald int btstack_main(void) 287*1fbe4564SMatthias Ringwald { 288*1fbe4564SMatthias Ringwald sniffer_setup(); 289*1fbe4564SMatthias Ringwald 290*1fbe4564SMatthias Ringwald // turn on! 291*1fbe4564SMatthias Ringwald hci_power_control(HCI_POWER_ON); 292*1fbe4564SMatthias Ringwald 293*1fbe4564SMatthias Ringwald return 0; 294*1fbe4564SMatthias Ringwald } 295*1fbe4564SMatthias Ringwald 296*1fbe4564SMatthias Ringwald /* EXAMPLE_END */ 297