eddystone.c 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298
  1. // SPDX-License-Identifier: GPL-2.0-or-later
  2. /*
  3. *
  4. * BlueZ - Bluetooth protocol stack for Linux
  5. *
  6. * Copyright (C) 2011-2012 Intel Corporation
  7. * Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org>
  8. *
  9. *
  10. */
  11. #ifdef HAVE_CONFIG_H
  12. #include <config.h>
  13. #endif
  14. #include <ctype.h>
  15. #include <stdio.h>
  16. #include <fcntl.h>
  17. #include <unistd.h>
  18. #include <stdlib.h>
  19. #include <string.h>
  20. #include <getopt.h>
  21. #include <sys/ioctl.h>
  22. #include <sys/socket.h>
  23. #include "monitor/bt.h"
  24. #include "src/shared/mainloop.h"
  25. #include "src/shared/timeout.h"
  26. #include "src/shared/util.h"
  27. #include "src/shared/hci.h"
  28. static int urandom_fd;
  29. static struct bt_hci *hci_dev;
  30. static bool shutdown_timeout(void *user_data)
  31. {
  32. mainloop_quit();
  33. return false;
  34. }
  35. static void shutdown_complete(const void *data, uint8_t size, void *user_data)
  36. {
  37. unsigned int id = PTR_TO_UINT(user_data);
  38. timeout_remove(id);
  39. mainloop_quit();
  40. }
  41. static void shutdown_device(void)
  42. {
  43. uint8_t enable = 0x00;
  44. unsigned int id;
  45. bt_hci_flush(hci_dev);
  46. id = timeout_add(5000, shutdown_timeout, NULL, NULL);
  47. bt_hci_send(hci_dev, BT_HCI_CMD_LE_SET_ADV_ENABLE,
  48. &enable, 1, NULL, NULL, NULL);
  49. bt_hci_send(hci_dev, BT_HCI_CMD_RESET, NULL, 0,
  50. shutdown_complete, UINT_TO_PTR(id), NULL);
  51. }
  52. static void set_random_address(void)
  53. {
  54. struct bt_hci_cmd_le_set_random_address cmd;
  55. ssize_t len;
  56. len = read(urandom_fd, cmd.addr, sizeof(cmd.addr));
  57. if (len < 0 || len != sizeof(cmd.addr)) {
  58. fprintf(stderr, "Failed to read random data\n");
  59. return;
  60. }
  61. /* Clear top most significant bits */
  62. cmd.addr[5] &= 0x3f;
  63. bt_hci_send(hci_dev, BT_HCI_CMD_LE_SET_RANDOM_ADDRESS,
  64. &cmd, sizeof(cmd), NULL, NULL, NULL);
  65. }
  66. static void set_adv_parameters(void)
  67. {
  68. struct bt_hci_cmd_le_set_adv_parameters cmd;
  69. cmd.min_interval = cpu_to_le16(0x0800);
  70. cmd.max_interval = cpu_to_le16(0x0800);
  71. cmd.type = 0x03; /* Non-connectable advertising */
  72. cmd.own_addr_type = 0x01; /* Use random address */
  73. cmd.direct_addr_type = 0x00;
  74. memset(cmd.direct_addr, 0, 6);
  75. cmd.channel_map = 0x07;
  76. cmd.filter_policy = 0x00;
  77. bt_hci_send(hci_dev, BT_HCI_CMD_LE_SET_ADV_PARAMETERS,
  78. &cmd, sizeof(cmd), NULL, NULL, NULL);
  79. }
  80. static void set_adv_enable(void)
  81. {
  82. uint8_t enable = 0x01;
  83. bt_hci_send(hci_dev, BT_HCI_CMD_LE_SET_ADV_ENABLE,
  84. &enable, 1, NULL, NULL, NULL);
  85. }
  86. static void adv_data_callback(const void *data, uint8_t size,
  87. void *user_data)
  88. {
  89. uint8_t status = *((uint8_t *) data);
  90. if (status) {
  91. fprintf(stderr, "Failed to set advertising data\n");
  92. shutdown_device();
  93. return;
  94. }
  95. set_random_address();
  96. set_adv_parameters();
  97. set_adv_enable();
  98. }
  99. static void adv_tx_power_callback(const void *data, uint8_t size,
  100. void *user_data)
  101. {
  102. const struct bt_hci_rsp_le_read_adv_tx_power *rsp = data;
  103. struct bt_hci_cmd_le_set_adv_data cmd;
  104. if (rsp->status) {
  105. fprintf(stderr, "Failed to read advertising TX power\n");
  106. shutdown_device();
  107. return;
  108. }
  109. cmd.data[0] = 0x02; /* Field length */
  110. cmd.data[1] = 0x01; /* Flags */
  111. cmd.data[2] = 0x04; /* BR/EDR Not Supported */
  112. cmd.data[3] = 0x03; /* Field length */
  113. cmd.data[4] = 0x03; /* 16-bit Service UUID list */
  114. cmd.data[5] = 0xaa; /* Eddystone UUID */
  115. cmd.data[6] = 0xfe;
  116. cmd.data[7] = 0x0c; /* Field length */
  117. cmd.data[8] = 0x16; /* 16-bit Service UUID data */
  118. cmd.data[9] = 0xaa; /* Eddystone UUID */
  119. cmd.data[10] = 0xfe;
  120. cmd.data[11] = 0x10; /* Eddystone-URL frame type */
  121. cmd.data[12] = 0x00; /* Calibrated Tx power at 0m */
  122. cmd.data[13] = 0x00; /* URL Scheme Prefix http://www. */
  123. cmd.data[14] = 'b';
  124. cmd.data[15] = 'l';
  125. cmd.data[16] = 'u';
  126. cmd.data[17] = 'e';
  127. cmd.data[18] = 'z';
  128. cmd.data[19] = 0x01; /* .org/ */
  129. cmd.data[20] = 0x00; /* Field terminator */
  130. memset(cmd.data + 21, 0, 9);
  131. cmd.len = 1 + cmd.data[0] + 1 + cmd.data[3] + 1 + cmd.data[7];
  132. bt_hci_send(hci_dev, BT_HCI_CMD_LE_SET_ADV_DATA, &cmd, sizeof(cmd),
  133. adv_data_callback, NULL, NULL);
  134. }
  135. static void local_features_callback(const void *data, uint8_t size,
  136. void *user_data)
  137. {
  138. const struct bt_hci_rsp_read_local_features *rsp = data;
  139. if (rsp->status) {
  140. fprintf(stderr, "Failed to read local features\n");
  141. shutdown_device();
  142. return;
  143. }
  144. if (!(rsp->features[4] & 0x40)) {
  145. fprintf(stderr, "Controller without Low Energy support\n");
  146. shutdown_device();
  147. return;
  148. }
  149. bt_hci_send(hci_dev, BT_HCI_CMD_LE_READ_ADV_TX_POWER, NULL, 0,
  150. adv_tx_power_callback, NULL, NULL);
  151. }
  152. static void start_eddystone(void)
  153. {
  154. bt_hci_send(hci_dev, BT_HCI_CMD_RESET, NULL, 0, NULL, NULL, NULL);
  155. bt_hci_send(hci_dev, BT_HCI_CMD_READ_LOCAL_FEATURES, NULL, 0,
  156. local_features_callback, NULL, NULL);
  157. }
  158. static void signal_callback(int signum, void *user_data)
  159. {
  160. static bool terminated = false;
  161. switch (signum) {
  162. case SIGINT:
  163. case SIGTERM:
  164. if (!terminated) {
  165. shutdown_device();
  166. terminated = true;
  167. }
  168. break;
  169. }
  170. }
  171. static void usage(void)
  172. {
  173. printf("eddystone - Low Energy Eddystone testing tool\n"
  174. "Usage:\n");
  175. printf("\teddystone [options]\n");
  176. printf("Options:\n"
  177. "\t-i, --index <num> Use specified controller\n"
  178. "\t-h, --help Show help options\n");
  179. }
  180. static const struct option main_options[] = {
  181. { "index", required_argument, NULL, 'i' },
  182. { "version", no_argument, NULL, 'v' },
  183. { "help", no_argument, NULL, 'h' },
  184. { }
  185. };
  186. int main(int argc, char *argv[])
  187. {
  188. uint16_t index = 0;
  189. const char *str;
  190. int exit_status;
  191. for (;;) {
  192. int opt;
  193. opt = getopt_long(argc, argv, "i:vh", main_options, NULL);
  194. if (opt < 0)
  195. break;
  196. switch (opt) {
  197. case 'i':
  198. if (strlen(optarg) > 3 && !strncmp(optarg, "hci", 3))
  199. str = optarg + 3;
  200. else
  201. str = optarg;
  202. if (!isdigit(*str)) {
  203. usage();
  204. return EXIT_FAILURE;
  205. }
  206. index = atoi(str);
  207. break;
  208. case 'v':
  209. printf("%s\n", VERSION);
  210. return EXIT_SUCCESS;
  211. case 'h':
  212. usage();
  213. return EXIT_SUCCESS;
  214. default:
  215. return EXIT_FAILURE;
  216. }
  217. }
  218. if (argc - optind > 0) {
  219. fprintf(stderr, "Invalid command line parameters\n");
  220. return EXIT_FAILURE;
  221. }
  222. urandom_fd = open("/dev/urandom", O_RDONLY);
  223. if (urandom_fd < 0) {
  224. fprintf(stderr, "Failed to open /dev/urandom device\n");
  225. return EXIT_FAILURE;
  226. }
  227. mainloop_init();
  228. printf("Low Energy Eddystone utility ver %s\n", VERSION);
  229. hci_dev = bt_hci_new_user_channel(index);
  230. if (!hci_dev) {
  231. fprintf(stderr, "Failed to open HCI user channel\n");
  232. exit_status = EXIT_FAILURE;
  233. goto done;
  234. }
  235. start_eddystone();
  236. exit_status = mainloop_run_with_signal(signal_callback, NULL);
  237. bt_hci_unref(hci_dev);
  238. done:
  239. close(urandom_fd);
  240. return exit_status;
  241. }