ibeacon.c 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291
  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] = 0x1a; /* Field length */
  113. cmd.data[4] = 0xff; /* Vendor field */
  114. cmd.data[5] = 0x4c; /* Apple (76) - LSB */
  115. cmd.data[6] = 0x00; /* Apple (76) - MSB */
  116. cmd.data[7] = 0x02; /* iBeacon type */
  117. cmd.data[8] = 0x15; /* Length */
  118. memset(cmd.data + 9, 0, 16); /* UUID */
  119. cmd.data[25] = 0x00; /* Major - LSB */
  120. cmd.data[26] = 0x00; /* Major - MSB */
  121. cmd.data[27] = 0x00; /* Minor - LSB */
  122. cmd.data[28] = 0x00; /* Minor - MSB */
  123. cmd.data[29] = 0xc5; /* TX power level */
  124. cmd.data[30] = 0x00; /* Field terminator */
  125. cmd.len = 1 + cmd.data[0] + 1 + cmd.data[3];
  126. bt_hci_send(hci_dev, BT_HCI_CMD_LE_SET_ADV_DATA, &cmd, sizeof(cmd),
  127. adv_data_callback, NULL, NULL);
  128. }
  129. static void local_features_callback(const void *data, uint8_t size,
  130. void *user_data)
  131. {
  132. const struct bt_hci_rsp_read_local_features *rsp = data;
  133. if (rsp->status) {
  134. fprintf(stderr, "Failed to read local features\n");
  135. shutdown_device();
  136. return;
  137. }
  138. if (!(rsp->features[4] & 0x40)) {
  139. fprintf(stderr, "Controller without Low Energy support\n");
  140. shutdown_device();
  141. return;
  142. }
  143. bt_hci_send(hci_dev, BT_HCI_CMD_LE_READ_ADV_TX_POWER, NULL, 0,
  144. adv_tx_power_callback, NULL, NULL);
  145. }
  146. static void start_ibeacon(void)
  147. {
  148. bt_hci_send(hci_dev, BT_HCI_CMD_RESET, NULL, 0, NULL, NULL, NULL);
  149. bt_hci_send(hci_dev, BT_HCI_CMD_READ_LOCAL_FEATURES, NULL, 0,
  150. local_features_callback, NULL, NULL);
  151. }
  152. static void signal_callback(int signum, void *user_data)
  153. {
  154. static bool terminated = false;
  155. switch (signum) {
  156. case SIGINT:
  157. case SIGTERM:
  158. if (!terminated) {
  159. shutdown_device();
  160. terminated = true;
  161. }
  162. break;
  163. }
  164. }
  165. static void usage(void)
  166. {
  167. printf("ibeacon - Low Energy iBeacon testing tool\n"
  168. "Usage:\n");
  169. printf("\tibeacon [options]\n");
  170. printf("Options:\n"
  171. "\t-i, --index <num> Use specified controller\n"
  172. "\t-h, --help Show help options\n");
  173. }
  174. static const struct option main_options[] = {
  175. { "index", required_argument, NULL, 'i' },
  176. { "version", no_argument, NULL, 'v' },
  177. { "help", no_argument, NULL, 'h' },
  178. { }
  179. };
  180. int main(int argc, char *argv[])
  181. {
  182. uint16_t index = 0;
  183. const char *str;
  184. int exit_status;
  185. for (;;) {
  186. int opt;
  187. opt = getopt_long(argc, argv, "i:vh", main_options, NULL);
  188. if (opt < 0)
  189. break;
  190. switch (opt) {
  191. case 'i':
  192. if (strlen(optarg) > 3 && !strncmp(optarg, "hci", 3))
  193. str = optarg + 3;
  194. else
  195. str = optarg;
  196. if (!isdigit(*str)) {
  197. usage();
  198. return EXIT_FAILURE;
  199. }
  200. index = atoi(str);
  201. break;
  202. case 'v':
  203. printf("%s\n", VERSION);
  204. return EXIT_SUCCESS;
  205. case 'h':
  206. usage();
  207. return EXIT_SUCCESS;
  208. default:
  209. return EXIT_FAILURE;
  210. }
  211. }
  212. if (argc - optind > 0) {
  213. fprintf(stderr, "Invalid command line parameters\n");
  214. return EXIT_FAILURE;
  215. }
  216. urandom_fd = open("/dev/urandom", O_RDONLY);
  217. if (urandom_fd < 0) {
  218. fprintf(stderr, "Failed to open /dev/urandom device\n");
  219. return EXIT_FAILURE;
  220. }
  221. mainloop_init();
  222. printf("Low Energy iBeacon utility ver %s\n", VERSION);
  223. hci_dev = bt_hci_new_user_channel(index);
  224. if (!hci_dev) {
  225. fprintf(stderr, "Failed to open HCI user channel\n");
  226. exit_status = EXIT_FAILURE;
  227. goto done;
  228. }
  229. start_ibeacon();
  230. exit_status = mainloop_run_with_signal(signal_callback, NULL);
  231. bt_hci_unref(hci_dev);
  232. done:
  233. close(urandom_fd);
  234. return exit_status;
  235. }