xref: /btstack/platform/daemon/example/inquiry.c (revision db09c08d745c978bc51e1abcf0215f8727d1e684)
1 /*
2  * Copyright (C) 2014 BlueKitchen GmbH
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  *
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. Neither the name of the copyright holders nor the names of
14  *    contributors may be used to endorse or promote products derived
15  *    from this software without specific prior written permission.
16  * 4. Any redistribution, use, or modification is done solely for
17  *    personal benefit and not for any commercial purpose or for
18  *    monetary gain.
19  *
20  * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS
21  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BLUEKITCHEN
24  * GMBH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
27  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
28  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
30  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  *
33  * Please inquire about commercial licensing options at
34  * [email protected]
35  *
36  */
37 
38 #define BTSTACK_FILE__ "inquiry.c"
39 
40 /*
41  *  inquiry.c
42  *
43  *  basic inquiry scan with remote name lookup
44  */
45 
46 #include <unistd.h>
47 #include <stdio.h>
48 #include <stdlib.h>
49 #include <string.h>
50 
51 #include "btstack_client.h"
52 
53 #ifdef _WIN32
54 #include "btstack_run_loop_windows.h"
55 #else
56 #include "btstack_run_loop_posix.h"
57 #endif
58 
59 #define MAX_DEVICES 10
60 struct device {
61 	bd_addr_t  address;
62 	uint16_t   clockOffset;
63 	uint32_t   classOfDevice;
64 	uint8_t	   pageScanRepetitionMode;
65 	uint8_t    rssi;
66 	uint8_t    state; // 0 empty, 1 found, 2 remote name tried, 3 remote name found
67 };
68 
69 #define INQUIRY_INTERVAL 5
70 struct device devices[MAX_DEVICES];
71 int deviceCount = 0;
72 
73 enum STATE {INIT, W4_INQUIRY_MODE_COMPLETE, ACTIVE, DEVICE_NAME, REMOTE_NAME_REQUEST, REMOTE_NAME_INQUIRED, REMOTE_NAME_FETCHED} ;
74 enum STATE state = INIT;
75 
getDeviceIndexForAddress(bd_addr_t addr)76 int getDeviceIndexForAddress( bd_addr_t addr){
77 	int j;
78 	for (j=0; j< deviceCount; j++){
79 		if (bd_addr_cmp(addr, devices[j].address) == 0){
80 			return j;
81 		}
82 	}
83 	return -1;
84 }
85 
start_scan(void)86 void start_scan(void){
87 	printf("Starting inquiry scan..\n");
88 	bt_send_cmd(&hci_inquiry, GAP_IAC_GENERAL_INQUIRY, INQUIRY_INTERVAL, 0);
89 }
90 
has_more_remote_name_requests(void)91 int has_more_remote_name_requests(void){
92 	int i;
93 	for (i=0;i<deviceCount;i++) {
94 		if (devices[i].state == REMOTE_NAME_REQUEST) return 1;
95 	}
96 	return 0;
97 }
98 
do_next_remote_name_request(void)99 void do_next_remote_name_request(void){
100 	int i;
101 	for (i=0;i<deviceCount;i++) {
102 		// remote name request
103 		if (devices[i].state == REMOTE_NAME_REQUEST){
104 			devices[i].state = REMOTE_NAME_INQUIRED;
105 			printf("Get remote name of %s...\n", bd_addr_to_str(devices[i].address));
106 			bt_send_cmd(&hci_remote_name_request, devices[i].address,
107 								devices[i].pageScanRepetitionMode, 0, devices[i].clockOffset | 0x8000);
108 			return;
109 		}
110 	}
111 }
112 
113 
packet_handler(uint8_t packet_type,uint16_t channel,uint8_t * packet,uint16_t size)114 static void packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
115 //static void packet_handler (uint8_t packet_type, uint8_t *packet, uint16_t size){
116 	bd_addr_t addr;
117 	int i;
118 	int index;
119 	int numResponses;
120 
121 	// printf("packet_handler: pt: 0x%02x, packet[0]: 0x%02x\n", packet_type, packet[0]);
122 	if (packet_type != HCI_EVENT_PACKET) return;
123 
124 	uint8_t event = hci_event_packet_get_type(packet);
125 
126 	switch(state){
127 
128 		case INIT:
129 			if (btstack_event_state_get_state(packet) == HCI_STATE_WORKING){
130 				bt_send_cmd(&hci_write_inquiry_mode, 0x01); // with RSSI
131 				state = W4_INQUIRY_MODE_COMPLETE;
132 			}
133 			break;
134 
135 		case W4_INQUIRY_MODE_COMPLETE:
136 			switch(event){
137 				case HCI_EVENT_COMMAND_COMPLETE:
138 					if (hci_event_command_complete_get_command_opcode(packet) == HCI_OPCODE_HCI_WRITE_INQUIRY_MODE){
139 						start_scan();
140 						state = ACTIVE;
141 					}
142 					break;
143 				case HCI_EVENT_COMMAND_STATUS:
144 					if (hci_event_command_complete_get_command_opcode(packet) == HCI_OPCODE_HCI_WRITE_INQUIRY_MODE){
145 						printf("Ignoring error (0x%x) from hci_write_inquiry_mode.\n", packet[2]);
146 						start_scan();
147 						state = ACTIVE;
148 					}
149 					break;
150 				default:
151 					break;
152 			}
153 
154 			break;
155 
156 		case ACTIVE:
157 			switch(event){
158 				case HCI_EVENT_INQUIRY_RESULT:
159 				case HCI_EVENT_INQUIRY_RESULT_WITH_RSSI:{
160                     numResponses = hci_event_inquiry_result_get_num_responses(packet);
161                     int offset = 3;
162                     for (i=0; i<numResponses && deviceCount < MAX_DEVICES;i++){
163                         reverse_bd_addr(&packet[offset], addr);
164                         offset += 6;
165                         index = getDeviceIndexForAddress(addr);
166                         if (index >= 0) continue;   // already in our list
167                         memcpy(devices[deviceCount].address, addr, 6);
168 
169                         devices[deviceCount].pageScanRepetitionMode = packet[offset];
170                         offset += 1;
171 
172                         if (event == HCI_EVENT_INQUIRY_RESULT){
173                             offset += 2; // Reserved + Reserved
174                             devices[deviceCount].classOfDevice = little_endian_read_24(packet, offset);
175                             offset += 3;
176                             devices[deviceCount].clockOffset =   little_endian_read_16(packet, offset) & 0x7fff;
177                             offset += 2;
178                             devices[deviceCount].rssi  = 0;
179                         } else {
180                             offset += 1; // Reserved
181                             devices[deviceCount].classOfDevice = little_endian_read_24(packet, offset);
182                             offset += 3;
183                             devices[deviceCount].clockOffset =   little_endian_read_16(packet, offset) & 0x7fff;
184                             offset += 2;
185                             devices[deviceCount].rssi  = packet[offset];
186                             offset += 1;
187                         }
188                         devices[deviceCount].state = REMOTE_NAME_REQUEST;
189                         printf("Device found: %s with COD: 0x%06x, pageScan %d, clock offset 0x%04x, rssi 0x%02x\n", bd_addr_to_str(addr),
190                                 devices[deviceCount].classOfDevice, devices[deviceCount].pageScanRepetitionMode,
191                                 devices[deviceCount].clockOffset, devices[deviceCount].rssi);
192                         deviceCount++;
193                     }
194 
195                     break;
196                 }
197 
198 				case HCI_EVENT_INQUIRY_COMPLETE:
199 					for (i=0;i<deviceCount;i++) {
200 						// retry remote name request
201 						if (devices[i].state == REMOTE_NAME_INQUIRED)
202 							devices[i].state = REMOTE_NAME_REQUEST;
203 					}
204 					if (has_more_remote_name_requests()){
205 						do_next_remote_name_request();
206 						break;
207 					}
208 					start_scan();
209 					break;
210 
211 				case DAEMON_EVENT_REMOTE_NAME_CACHED:
212 					reverse_bd_addr(&packet[3], addr);
213 					printf("Cached remote name for %s: '%s'\n", bd_addr_to_str(addr), &packet[9]);
214 					break;
215 
216 				case HCI_EVENT_REMOTE_NAME_REQUEST_COMPLETE:
217 					reverse_bd_addr(&packet[3], addr);
218 					index = getDeviceIndexForAddress(addr);
219 					if (index >= 0) {
220 						if (packet[2] == 0) {
221 							printf("Name: '%s'\n", &packet[9]);
222 							devices[index].state = REMOTE_NAME_FETCHED;
223 						} else {
224 							printf("Failed to get name: page timeout\n");
225 						}
226 					}
227 					if (has_more_remote_name_requests()){
228 						do_next_remote_name_request();
229 						break;
230 					}
231 					start_scan();
232 					break;
233 
234 				default:
235 					break;
236 			}
237 			break;
238 
239 		default:
240 			break;
241 	}
242 }
243 
main(int argc,const char * argv[])244 int main (int argc, const char * argv[]){
245 	// start stack
246 #ifdef _WIN32
247 	btstack_run_loop_init(btstack_run_loop_windows_get_instance());
248 #else
249 	btstack_run_loop_init(btstack_run_loop_posix_get_instance());
250 #endif
251 	int err = bt_open();
252 	if (err) {
253 		printf("Failed to open connection to BTdaemon\n");
254 		return err;
255 	}
256 	// init table
257 	int i; for (i=0;i<MAX_DEVICES;i++) devices[i].state = 0;
258 
259 	bt_register_packet_handler(packet_handler);
260 	bt_send_cmd(&btstack_set_power_mode, HCI_POWER_ON );
261 	btstack_run_loop_execute();
262 	bt_close();
263 	return 0;
264 }
265