Upgrade bluez5_37 :Merge the code from private
[platform/upstream/bluez.git] / tools / eddystone.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] = 0x03;             /* Field length */
156         cmd.data[4] = 0x03;             /* 16-bit Service UUID list */
157         cmd.data[5] = 0xaa;             /* Eddystone UUID */
158         cmd.data[6] = 0xfe;
159
160         cmd.data[7] = 0x0c;             /* Field length */
161         cmd.data[8] = 0x16;             /* 16-bit Service UUID data */
162         cmd.data[9] = 0xaa;             /* Eddystone UUID */
163         cmd.data[10] = 0xfe;
164         cmd.data[11] = 0x10;            /* Eddystone-URL frame type */
165         cmd.data[12] = 0x00;            /* Calibrated Tx power at 0m */
166         cmd.data[13] = 0x00;            /* URL Scheme Prefix http://www. */
167         cmd.data[14] = 'b';
168         cmd.data[15] = 'l';
169         cmd.data[16] = 'u';
170         cmd.data[17] = 'e';
171         cmd.data[18] = 'z';
172         cmd.data[19] = 0x01;            /* .org/ */
173
174         cmd.data[20] = 0x00;            /* Field terminator */
175         memset(cmd.data + 21, 0, 9);
176
177         cmd.len = 1 + cmd.data[0] + 1 + cmd.data[3] + 1 + cmd.data[7];
178
179         bt_hci_send(hci_dev, BT_HCI_CMD_LE_SET_ADV_DATA, &cmd, sizeof(cmd),
180                                         adv_data_callback, NULL, NULL);
181 }
182
183 static void local_features_callback(const void *data, uint8_t size,
184                                                         void *user_data)
185 {
186         const struct bt_hci_rsp_read_local_features *rsp = data;
187
188         if (rsp->status) {
189                 fprintf(stderr, "Failed to read local features\n");
190                 shutdown_device();
191                 return;
192         }
193
194         if (!(rsp->features[4] & 0x40)) {
195                 fprintf(stderr, "Controller without Low Energy support\n");
196                 shutdown_device();
197                 return;
198         }
199
200         bt_hci_send(hci_dev, BT_HCI_CMD_LE_READ_ADV_TX_POWER, NULL, 0,
201                                         adv_tx_power_callback, NULL, NULL);
202 }
203
204 static void start_eddystone(void)
205 {
206         bt_hci_send(hci_dev, BT_HCI_CMD_RESET, NULL, 0, NULL, NULL, NULL);
207
208         bt_hci_send(hci_dev, BT_HCI_CMD_READ_LOCAL_FEATURES, NULL, 0,
209                                         local_features_callback, NULL, NULL);
210 }
211
212 static void signal_callback(int signum, void *user_data)
213 {
214         static bool terminated = false;
215
216         switch (signum) {
217         case SIGINT:
218         case SIGTERM:
219                 if (!terminated) {
220                         shutdown_device();
221                         terminated = true;
222                 }
223                 break;
224         }
225 }
226
227 static void usage(void)
228 {
229         printf("eddystone - Low Energy Eddystone testing tool\n"
230                 "Usage:\n");
231         printf("\teddystone [options]\n");
232         printf("Options:\n"
233                 "\t-i, --index <num>      Use specified controller\n"
234                 "\t-h, --help             Show help options\n");
235 }
236
237 static const struct option main_options[] = {
238         { "index",   required_argument, NULL, 'i' },
239         { "version", no_argument,       NULL, 'v' },
240         { "help",    no_argument,       NULL, 'h' },
241         { }
242 };
243
244 int main(int argc, char *argv[])
245 {
246         uint16_t index = 0;
247         const char *str;
248         sigset_t mask;
249         int exit_status;
250
251         for (;;) {
252                 int opt;
253
254                 opt = getopt_long(argc, argv, "i:vh", main_options, NULL);
255                 if (opt < 0)
256                         break;
257
258                 switch (opt) {
259                 case 'i':
260                         if (strlen(optarg) > 3 && !strncmp(optarg, "hci", 3))
261                                 str = optarg + 3;
262                         else
263                                 str = optarg;
264                         if (!isdigit(*str)) {
265                                 usage();
266                                 return EXIT_FAILURE;
267                         }
268                         index = atoi(str);
269                         break;
270                 case 'v':
271                         printf("%s\n", VERSION);
272                         return EXIT_SUCCESS;
273                 case 'h':
274                         usage();
275                         return EXIT_SUCCESS;
276                 default:
277                         return EXIT_FAILURE;
278                 }
279         }
280
281         if (argc - optind > 0) {
282                 fprintf(stderr, "Invalid command line parameters\n");
283                 return EXIT_FAILURE;
284         }
285
286         urandom_fd = open("/dev/urandom", O_RDONLY);
287         if (urandom_fd < 0) {
288                 fprintf(stderr, "Failed to open /dev/urandom device\n");
289                 return EXIT_FAILURE;
290         }
291
292         mainloop_init();
293
294         sigemptyset(&mask);
295         sigaddset(&mask, SIGINT);
296         sigaddset(&mask, SIGTERM);
297
298         mainloop_set_signal(&mask, signal_callback, NULL, NULL);
299
300         printf("Low Energy Eddystone utility ver %s\n", VERSION);
301
302         hci_dev = bt_hci_new_user_channel(index);
303         if (!hci_dev) {
304                 fprintf(stderr, "Failed to open HCI user channel\n");
305                 exit_status = EXIT_FAILURE;
306                 goto done;
307         }
308
309         start_eddystone();
310
311         exit_status = mainloop_run();
312
313         bt_hci_unref(hci_dev);
314
315 done:
316         close(urandom_fd);
317
318         return exit_status;
319 }