tizen 2.3.1 release
[framework/connectivity/bluez.git] / tools / ibeacon.c
1 /*
2  *
3  *  BlueZ - Bluetooth protocol stack for Linux
4  *
5  *  Copyright (C) 2011-2012  Intel Corporation
6  *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
7  *
8  *
9  *  This program is free software; you can redistribute it and/or modify
10  *  it under the terms of the GNU General Public License as published by
11  *  the Free Software Foundation; either version 2 of the License, or
12  *  (at your option) any later version.
13  *
14  *  This program is distributed in the hope that it will be useful,
15  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  *  GNU General Public License for more details.
18  *
19  *  You should have received a copy of the GNU General Public License
20  *  along with this program; if not, write to the Free Software
21  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
22  *
23  */
24
25 #ifdef HAVE_CONFIG_H
26 #include <config.h>
27 #endif
28
29 #include <ctype.h>
30 #include <stdio.h>
31 #include <fcntl.h>
32 #include <unistd.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <getopt.h>
36 #include <sys/ioctl.h>
37 #include <sys/socket.h>
38
39 #include "monitor/bt.h"
40 #include "src/shared/mainloop.h"
41 #include "src/shared/timeout.h"
42 #include "src/shared/util.h"
43 #include "src/shared/hci.h"
44
45 static int urandom_fd;
46 static struct bt_hci *hci_dev;
47
48 static bool shutdown_timeout(void *user_data)
49 {
50         mainloop_quit();
51
52         return false;
53 }
54
55 static void shutdown_complete(const void *data, uint8_t size, void *user_data)
56 {
57         unsigned int id = PTR_TO_UINT(user_data);
58
59         timeout_remove(id);
60         mainloop_quit();
61 }
62
63 static void shutdown_device(void)
64 {
65         uint8_t enable = 0x00;
66         unsigned int id;
67
68         bt_hci_flush(hci_dev);
69
70         id = timeout_add(5000, shutdown_timeout, NULL, NULL);
71
72         bt_hci_send(hci_dev, BT_HCI_CMD_LE_SET_ADV_ENABLE,
73                                         &enable, 1, NULL, NULL, NULL);
74
75         bt_hci_send(hci_dev, BT_HCI_CMD_RESET, NULL, 0,
76                                 shutdown_complete, UINT_TO_PTR(id), NULL);
77 }
78
79 static void set_random_address(void)
80 {
81         struct bt_hci_cmd_le_set_random_address cmd;
82         ssize_t len;
83
84         len = read(urandom_fd, cmd.addr, sizeof(cmd.addr));
85         if (len < 0 || len != sizeof(cmd.addr)) {
86                 fprintf(stderr, "Failed to read random data\n");
87                 return;
88         }
89
90         /* Clear top most significant bits */
91         cmd.addr[5] &= 0x3f;
92
93         bt_hci_send(hci_dev, BT_HCI_CMD_LE_SET_RANDOM_ADDRESS,
94                                         &cmd, sizeof(cmd), NULL, NULL, NULL);
95 }
96
97 static void set_adv_parameters(void)
98 {
99         struct bt_hci_cmd_le_set_adv_parameters cmd;
100
101         cmd.min_interval = cpu_to_le16(0x0800);
102         cmd.max_interval = cpu_to_le16(0x0800);
103         cmd.type = 0x03;                /* Non-connectable advertising */
104         cmd.own_addr_type = 0x01;       /* Use random address */
105         cmd.direct_addr_type = 0x00;
106         memset(cmd.direct_addr, 0, 6);
107         cmd.channel_map = 0x07;
108         cmd.filter_policy = 0x00;
109
110         bt_hci_send(hci_dev, BT_HCI_CMD_LE_SET_ADV_PARAMETERS,
111                                         &cmd, sizeof(cmd), NULL, NULL, NULL);
112 }
113
114 static void set_adv_enable(void)
115 {
116         uint8_t enable = 0x01;
117
118         bt_hci_send(hci_dev, BT_HCI_CMD_LE_SET_ADV_ENABLE,
119                                         &enable, 1, NULL, NULL, NULL);
120 }
121
122 static void adv_data_callback(const void *data, uint8_t size,
123                                                         void *user_data)
124 {
125         uint8_t status = *((uint8_t *) data);
126
127         if (status) {
128                 fprintf(stderr, "Failed to set advertising data\n");
129                 shutdown_device();
130                 return;
131         }
132
133         set_random_address();
134         set_adv_parameters();
135         set_adv_enable();
136 }
137
138 static void adv_tx_power_callback(const void *data, uint8_t size,
139                                                         void *user_data)
140 {
141         const struct bt_hci_rsp_le_read_adv_tx_power *rsp = data;
142         struct bt_hci_cmd_le_set_adv_data cmd;
143
144         if (rsp->status) {
145                 fprintf(stderr, "Failed to read advertising TX power\n");
146                 shutdown_device();
147                 return;
148         }
149
150         cmd.data[0] = 0x02;             /* Field length */
151         cmd.data[1] = 0x01;             /* Flags */
152         cmd.data[2] = 0x02;             /* LE General Discoverable Mode */
153         cmd.data[2] |= 0x04;            /* BR/EDR Not Supported */
154
155         cmd.data[3] = 0x1a;             /* Field length */
156         cmd.data[4] = 0xff;             /* Vendor field */
157         cmd.data[5] = 0x4c;             /* Apple (76) - LSB */
158         cmd.data[6] = 0x00;             /* Apple (76) - MSB */
159         cmd.data[7] = 0x02;             /* iBeacon type */
160         cmd.data[8] = 0x15;             /* Length */
161         memset(cmd.data + 9, 0, 16);    /* UUID */
162         cmd.data[25] = 0x00;            /* Major - LSB */
163         cmd.data[26] = 0x00;            /* Major - MSB */
164         cmd.data[27] = 0x00;            /* Minor - LSB */
165         cmd.data[28] = 0x00;            /* Minor - MSB */
166         cmd.data[29] = 0xc5;            /* TX power level */
167
168         cmd.data[30] = 0x00;            /* Field terminator */
169
170         cmd.len = 1 + cmd.data[0] + 1 + cmd.data[3];
171
172         bt_hci_send(hci_dev, BT_HCI_CMD_LE_SET_ADV_DATA, &cmd, sizeof(cmd),
173                                         adv_data_callback, NULL, NULL);
174 }
175
176 static void local_features_callback(const void *data, uint8_t size,
177                                                         void *user_data)
178 {
179         const struct bt_hci_rsp_read_local_features *rsp = data;
180
181         if (rsp->status) {
182                 fprintf(stderr, "Failed to read local features\n");
183                 shutdown_device();
184                 return;
185         }
186
187         if (!(rsp->features[4] & 0x40)) {
188                 fprintf(stderr, "Controller without Low Energy support\n");
189                 shutdown_device();
190                 return;
191         }
192
193         bt_hci_send(hci_dev, BT_HCI_CMD_LE_READ_ADV_TX_POWER, NULL, 0,
194                                         adv_tx_power_callback, NULL, NULL);
195 }
196
197 static void start_ibeacon(void)
198 {
199         bt_hci_send(hci_dev, BT_HCI_CMD_RESET, NULL, 0, NULL, NULL, NULL);
200
201         bt_hci_send(hci_dev, BT_HCI_CMD_READ_LOCAL_FEATURES, NULL, 0,
202                                         local_features_callback, NULL, NULL);
203 }
204
205 static void signal_callback(int signum, void *user_data)
206 {
207         static bool terminated = false;
208
209         switch (signum) {
210         case SIGINT:
211         case SIGTERM:
212                 if (!terminated) {
213                         shutdown_device();
214                         terminated = true;
215                 }
216                 break;
217         }
218 }
219
220 static void usage(void)
221 {
222         printf("ibeacon - Low Energy iBeacon testing tool\n"
223                 "Usage:\n");
224         printf("\tibeacon [options]\n");
225         printf("Options:\n"
226                 "\t-i, --index <num>      Use specified controller\n"
227                 "\t-h, --help             Show help options\n");
228 }
229
230 static const struct option main_options[] = {
231         { "index",   required_argument, NULL, 'i' },
232         { "version", no_argument,       NULL, 'v' },
233         { "help",    no_argument,       NULL, 'h' },
234         { }
235 };
236
237 int main(int argc, char *argv[])
238 {
239         uint16_t index = 0;
240         const char *str;
241         sigset_t mask;
242         int exit_status;
243
244         for (;;) {
245                 int opt;
246
247                 opt = getopt_long(argc, argv, "i:vh", main_options, NULL);
248                 if (opt < 0)
249                         break;
250
251                 switch (opt) {
252                 case 'i':
253                         if (strlen(optarg) > 3 && !strncmp(optarg, "hci", 3))
254                                 str = optarg + 3;
255                         else
256                                 str = optarg;
257                         if (!isdigit(*str)) {
258                                 usage();
259                                 return EXIT_FAILURE;
260                         }
261                         index = atoi(str);
262                         break;
263                 case 'v':
264                         printf("%s\n", VERSION);
265                         return EXIT_SUCCESS;
266                 case 'h':
267                         usage();
268                         return EXIT_SUCCESS;
269                 default:
270                         return EXIT_FAILURE;
271                 }
272         }
273
274         if (argc - optind > 0) {
275                 fprintf(stderr, "Invalid command line parameters\n");
276                 return EXIT_FAILURE;
277         }
278
279         urandom_fd = open("/dev/urandom", O_RDONLY);
280         if (urandom_fd < 0) {
281                 fprintf(stderr, "Failed to open /dev/urandom device\n");
282                 return EXIT_FAILURE;
283         }
284
285         mainloop_init();
286
287         sigemptyset(&mask);
288         sigaddset(&mask, SIGINT);
289         sigaddset(&mask, SIGTERM);
290
291         mainloop_set_signal(&mask, signal_callback, NULL, NULL);
292
293         printf("Low Energy iBeacon utility ver %s\n", VERSION);
294
295         hci_dev = bt_hci_new_user_channel(index);
296         if (!hci_dev) {
297                 fprintf(stderr, "Failed to open HCI user channel\n");
298                 exit_status = EXIT_FAILURE;
299                 goto done;
300         }
301
302         start_ibeacon();
303
304         exit_status = mainloop_run();
305
306         bt_hci_unref(hci_dev);
307
308 done:
309         close(urandom_fd);
310
311         return exit_status;
312 }