advertising.c 24 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025
  1. // SPDX-License-Identifier: GPL-2.0-or-later
  2. /*
  3. *
  4. * BlueZ - Bluetooth protocol stack for Linux
  5. *
  6. * Copyright (C) 2016 Intel Corporation. All rights reserved.
  7. *
  8. *
  9. */
  10. #ifdef HAVE_CONFIG_H
  11. #include <config.h>
  12. #endif
  13. #define _GNU_SOURCE
  14. #include <stdio.h>
  15. #include <stdlib.h>
  16. #include <stdint.h>
  17. #include <stdbool.h>
  18. #include <string.h>
  19. #include "gdbus/gdbus.h"
  20. #include "src/shared/util.h"
  21. #include "src/shared/shell.h"
  22. #include "advertising.h"
  23. #define AD_PATH "/org/bluez/advertising"
  24. #define AD_IFACE "org.bluez.LEAdvertisement1"
  25. struct ad_data {
  26. uint8_t data[25];
  27. uint8_t len;
  28. };
  29. struct service_data {
  30. char *uuid;
  31. struct ad_data data;
  32. };
  33. struct manufacturer_data {
  34. uint16_t id;
  35. struct ad_data data;
  36. };
  37. struct data {
  38. uint8_t type;
  39. struct ad_data data;
  40. };
  41. static struct ad {
  42. bool registered;
  43. char *type;
  44. char *local_name;
  45. char *secondary;
  46. uint32_t min_interval;
  47. uint32_t max_interval;
  48. uint16_t local_appearance;
  49. uint16_t duration;
  50. uint16_t timeout;
  51. uint16_t discoverable_to;
  52. char **uuids;
  53. size_t uuids_len;
  54. struct service_data service;
  55. struct manufacturer_data manufacturer;
  56. struct data data;
  57. bool discoverable;
  58. bool tx_power;
  59. bool name;
  60. bool appearance;
  61. } ad = {
  62. .local_appearance = UINT16_MAX,
  63. .discoverable = true,
  64. };
  65. static void ad_release(DBusConnection *conn)
  66. {
  67. ad.registered = false;
  68. g_dbus_unregister_interface(conn, AD_PATH, AD_IFACE);
  69. }
  70. static DBusMessage *release_advertising(DBusConnection *conn,
  71. DBusMessage *msg, void *user_data)
  72. {
  73. bt_shell_printf("Advertising released\n");
  74. ad_release(conn);
  75. return dbus_message_new_method_return(msg);
  76. }
  77. static const GDBusMethodTable ad_methods[] = {
  78. { GDBUS_METHOD("Release", NULL, NULL, release_advertising) },
  79. { }
  80. };
  81. static void register_setup(DBusMessageIter *iter, void *user_data)
  82. {
  83. DBusMessageIter dict;
  84. const char *path = AD_PATH;
  85. dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &path);
  86. dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
  87. DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
  88. DBUS_TYPE_STRING_AS_STRING
  89. DBUS_TYPE_VARIANT_AS_STRING
  90. DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
  91. dbus_message_iter_close_container(iter, &dict);
  92. }
  93. static void print_uuid(const char *uuid)
  94. {
  95. const char *text;
  96. text = bt_uuidstr_to_str(uuid);
  97. if (text) {
  98. char str[26];
  99. unsigned int n;
  100. str[sizeof(str) - 1] = '\0';
  101. n = snprintf(str, sizeof(str), "%s", text);
  102. if (n > sizeof(str) - 1) {
  103. str[sizeof(str) - 2] = '.';
  104. str[sizeof(str) - 3] = '.';
  105. if (str[sizeof(str) - 4] == ' ')
  106. str[sizeof(str) - 4] = '.';
  107. n = sizeof(str) - 1;
  108. }
  109. bt_shell_printf("UUID: %s(%s)\n", str, uuid);
  110. } else
  111. bt_shell_printf("UUID: (%s)\n", uuid ? uuid : "");
  112. }
  113. static void print_ad_uuids(void)
  114. {
  115. char **uuid;
  116. for (uuid = ad.uuids; uuid && *uuid; uuid++)
  117. print_uuid(*uuid);
  118. }
  119. static void print_ad(void)
  120. {
  121. print_ad_uuids();
  122. if (ad.service.uuid) {
  123. print_uuid(ad.service.uuid);
  124. bt_shell_hexdump(ad.service.data.data, ad.service.data.len);
  125. }
  126. if (ad.manufacturer.data.len) {
  127. bt_shell_printf("Manufacturer: %u\n", ad.manufacturer.id);
  128. bt_shell_hexdump(ad.manufacturer.data.data,
  129. ad.manufacturer.data.len);
  130. }
  131. if (ad.data.data.len) {
  132. bt_shell_printf("Data Type: 0x%02x\n", ad.data.type);
  133. bt_shell_hexdump(ad.data.data.data, ad.data.data.len);
  134. }
  135. bt_shell_printf("Tx Power: %s\n", ad.tx_power ? "on" : "off");
  136. if (ad.local_name)
  137. bt_shell_printf("LocalName: %s\n", ad.local_name);
  138. else
  139. bt_shell_printf("Name: %s\n", ad.name ? "on" : "off");
  140. if (ad.local_appearance != UINT16_MAX)
  141. bt_shell_printf("Appearance: %s (0x%04x)\n",
  142. bt_appear_to_str(ad.local_appearance),
  143. ad.local_appearance);
  144. else
  145. bt_shell_printf("Appearance: %s\n",
  146. ad.appearance ? "on" : "off");
  147. bt_shell_printf("Discoverable: %s\n", ad.discoverable ? "on": "off");
  148. if (ad.duration)
  149. bt_shell_printf("Duration: %u sec\n", ad.duration);
  150. if (ad.timeout)
  151. bt_shell_printf("Timeout: %u sec\n", ad.timeout);
  152. if (ad.min_interval)
  153. bt_shell_printf("Interval: %u-%u msec\n", ad.min_interval,
  154. ad.max_interval);
  155. }
  156. static void register_reply(DBusMessage *message, void *user_data)
  157. {
  158. DBusConnection *conn = user_data;
  159. DBusError error;
  160. dbus_error_init(&error);
  161. if (dbus_set_error_from_message(&error, message) == FALSE) {
  162. ad.registered = true;
  163. bt_shell_printf("Advertising object registered\n");
  164. print_ad();
  165. /* Leave advertise running even on noninteractive mode */
  166. } else {
  167. bt_shell_printf("Failed to register advertisement: %s\n", error.name);
  168. dbus_error_free(&error);
  169. if (g_dbus_unregister_interface(conn, AD_PATH,
  170. AD_IFACE) == FALSE)
  171. bt_shell_printf("Failed to unregister advertising object\n");
  172. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  173. }
  174. }
  175. static gboolean get_type(const GDBusPropertyTable *property,
  176. DBusMessageIter *iter, void *user_data)
  177. {
  178. const char *type = "peripheral";
  179. if (ad.type && strlen(ad.type) > 0)
  180. type = ad.type;
  181. dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &type);
  182. return TRUE;
  183. }
  184. static gboolean uuids_exists(const GDBusPropertyTable *property, void *data)
  185. {
  186. return ad.uuids_len != 0;
  187. }
  188. static gboolean get_uuids(const GDBusPropertyTable *property,
  189. DBusMessageIter *iter, void *user_data)
  190. {
  191. DBusMessageIter array;
  192. size_t i;
  193. dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "as", &array);
  194. for (i = 0; i < ad.uuids_len; i++)
  195. dbus_message_iter_append_basic(&array, DBUS_TYPE_STRING,
  196. &ad.uuids[i]);
  197. dbus_message_iter_close_container(iter, &array);
  198. return TRUE;
  199. }
  200. static gboolean service_data_exists(const GDBusPropertyTable *property,
  201. void *data)
  202. {
  203. return ad.service.uuid != NULL;
  204. }
  205. static gboolean get_service_data(const GDBusPropertyTable *property,
  206. DBusMessageIter *iter, void *user_data)
  207. {
  208. DBusMessageIter dict;
  209. struct ad_data *data = &ad.service.data;
  210. uint8_t *val = data->data;
  211. dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "{sv}", &dict);
  212. g_dbus_dict_append_array(&dict, ad.service.uuid, DBUS_TYPE_BYTE, &val,
  213. data->len);
  214. dbus_message_iter_close_container(iter, &dict);
  215. return TRUE;
  216. }
  217. static gboolean manufacturer_data_exists(const GDBusPropertyTable *property,
  218. void *data)
  219. {
  220. return ad.manufacturer.id != 0;
  221. }
  222. static gboolean get_manufacturer_data(const GDBusPropertyTable *property,
  223. DBusMessageIter *iter, void *user_data)
  224. {
  225. DBusMessageIter dict;
  226. struct ad_data *data = &ad.manufacturer.data;
  227. uint8_t *val = data->data;
  228. dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "{qv}", &dict);
  229. g_dbus_dict_append_basic_array(&dict, DBUS_TYPE_UINT16,
  230. &ad.manufacturer.id,
  231. DBUS_TYPE_BYTE, &val, data->len);
  232. dbus_message_iter_close_container(iter, &dict);
  233. return TRUE;
  234. }
  235. static gboolean includes_exists(const GDBusPropertyTable *property, void *data)
  236. {
  237. return ad.tx_power || ad.name || ad.appearance;
  238. }
  239. static gboolean get_includes(const GDBusPropertyTable *property,
  240. DBusMessageIter *iter, void *user_data)
  241. {
  242. DBusMessageIter array;
  243. dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "as", &array);
  244. if (ad.tx_power) {
  245. const char *str = "tx-power";
  246. dbus_message_iter_append_basic(&array, DBUS_TYPE_STRING, &str);
  247. }
  248. if (ad.name) {
  249. const char *str = "local-name";
  250. dbus_message_iter_append_basic(&array, DBUS_TYPE_STRING, &str);
  251. }
  252. if (ad.appearance) {
  253. const char *str = "appearance";
  254. dbus_message_iter_append_basic(&array, DBUS_TYPE_STRING, &str);
  255. }
  256. dbus_message_iter_close_container(iter, &array);
  257. return TRUE;
  258. }
  259. static gboolean local_name_exits(const GDBusPropertyTable *property, void *data)
  260. {
  261. return ad.local_name ? TRUE : FALSE;
  262. }
  263. static gboolean get_local_name(const GDBusPropertyTable *property,
  264. DBusMessageIter *iter, void *user_data)
  265. {
  266. dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &ad.local_name);
  267. return TRUE;
  268. }
  269. static gboolean appearance_exits(const GDBusPropertyTable *property, void *data)
  270. {
  271. return ad.local_appearance != UINT16_MAX ? TRUE : FALSE;
  272. }
  273. static gboolean get_appearance(const GDBusPropertyTable *property,
  274. DBusMessageIter *iter, void *user_data)
  275. {
  276. dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT16,
  277. &ad.local_appearance);
  278. return TRUE;
  279. }
  280. static gboolean duration_exits(const GDBusPropertyTable *property, void *data)
  281. {
  282. return ad.duration;
  283. }
  284. static gboolean get_duration(const GDBusPropertyTable *property,
  285. DBusMessageIter *iter, void *user_data)
  286. {
  287. dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT16, &ad.duration);
  288. return TRUE;
  289. }
  290. static gboolean timeout_exits(const GDBusPropertyTable *property, void *data)
  291. {
  292. return ad.timeout;
  293. }
  294. static gboolean get_timeout(const GDBusPropertyTable *property,
  295. DBusMessageIter *iter, void *user_data)
  296. {
  297. dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT16, &ad.timeout);
  298. return TRUE;
  299. }
  300. static gboolean data_exists(const GDBusPropertyTable *property, void *data)
  301. {
  302. return ad.data.type != 0;
  303. }
  304. static gboolean get_data(const GDBusPropertyTable *property,
  305. DBusMessageIter *iter, void *user_data)
  306. {
  307. DBusMessageIter dict;
  308. struct ad_data *data = &ad.data.data;
  309. uint8_t *val = data->data;
  310. dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "{yv}", &dict);
  311. g_dbus_dict_append_basic_array(&dict, DBUS_TYPE_BYTE, &ad.data.type,
  312. DBUS_TYPE_BYTE, &val, data->len);
  313. dbus_message_iter_close_container(iter, &dict);
  314. return TRUE;
  315. }
  316. static gboolean discoverable_exists(const GDBusPropertyTable *property,
  317. void *data)
  318. {
  319. return ad.discoverable;
  320. }
  321. static gboolean get_discoverable(const GDBusPropertyTable *property,
  322. DBusMessageIter *iter, void *user_data)
  323. {
  324. dbus_bool_t value = ad.discoverable;
  325. dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &value);
  326. return TRUE;
  327. }
  328. static gboolean discoverable_timeout_exits(const GDBusPropertyTable *property,
  329. void *data)
  330. {
  331. return ad.discoverable_to;
  332. }
  333. static gboolean get_discoverable_timeout(const GDBusPropertyTable *property,
  334. DBusMessageIter *iter, void *user_data)
  335. {
  336. dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT16,
  337. &ad.discoverable_to);
  338. return TRUE;
  339. }
  340. static gboolean secondary_exits(const GDBusPropertyTable *property, void *data)
  341. {
  342. return ad.secondary ? TRUE : FALSE;
  343. }
  344. static gboolean get_secondary(const GDBusPropertyTable *property,
  345. DBusMessageIter *iter, void *user_data)
  346. {
  347. dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING,
  348. &ad.secondary);
  349. return TRUE;
  350. }
  351. static gboolean min_interval_exits(const GDBusPropertyTable *property,
  352. void *data)
  353. {
  354. return ad.min_interval;
  355. }
  356. static gboolean get_min_interval(const GDBusPropertyTable *property,
  357. DBusMessageIter *iter, void *user_data)
  358. {
  359. dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT32,
  360. &ad.min_interval);
  361. return TRUE;
  362. }
  363. static gboolean max_interval_exits(const GDBusPropertyTable *property,
  364. void *data)
  365. {
  366. return ad.max_interval;
  367. }
  368. static gboolean get_max_interval(const GDBusPropertyTable *property,
  369. DBusMessageIter *iter, void *user_data)
  370. {
  371. dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT32,
  372. &ad.max_interval);
  373. return TRUE;
  374. }
  375. static const GDBusPropertyTable ad_props[] = {
  376. { "Type", "s", get_type },
  377. { "ServiceUUIDs", "as", get_uuids, NULL, uuids_exists },
  378. { "ServiceData", "a{sv}", get_service_data, NULL, service_data_exists },
  379. { "ManufacturerData", "a{qv}", get_manufacturer_data, NULL,
  380. manufacturer_data_exists },
  381. { "Data", "a{yv}", get_data, NULL, data_exists },
  382. { "Discoverable", "b", get_discoverable, NULL, discoverable_exists },
  383. { "DiscoverableTimeout", "q", get_discoverable_timeout, NULL,
  384. discoverable_timeout_exits },
  385. { "Includes", "as", get_includes, NULL, includes_exists },
  386. { "LocalName", "s", get_local_name, NULL, local_name_exits },
  387. { "Appearance", "q", get_appearance, NULL, appearance_exits },
  388. { "Duration", "q", get_duration, NULL, duration_exits },
  389. { "Timeout", "q", get_timeout, NULL, timeout_exits },
  390. { "MinInterval", "u", get_min_interval, NULL, min_interval_exits },
  391. { "MaxInterval", "u", get_max_interval, NULL, max_interval_exits },
  392. { "SecondaryChannel", "s", get_secondary, NULL, secondary_exits },
  393. { }
  394. };
  395. void ad_register(DBusConnection *conn, GDBusProxy *manager, const char *type)
  396. {
  397. if (ad.registered) {
  398. bt_shell_printf("Advertisement is already registered\n");
  399. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  400. }
  401. g_free(ad.type);
  402. ad.type = g_strdup(type);
  403. if (!strcasecmp(ad.type, "Broadcast"))
  404. ad.discoverable = false;
  405. if (g_dbus_register_interface(conn, AD_PATH, AD_IFACE, ad_methods,
  406. NULL, ad_props, NULL, NULL) == FALSE) {
  407. bt_shell_printf("Failed to register advertising object\n");
  408. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  409. }
  410. if (g_dbus_proxy_method_call(manager, "RegisterAdvertisement",
  411. register_setup, register_reply,
  412. conn, NULL) == FALSE) {
  413. bt_shell_printf("Failed to register advertising\n");
  414. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  415. }
  416. }
  417. static void unregister_setup(DBusMessageIter *iter, void *user_data)
  418. {
  419. const char *path = AD_PATH;
  420. dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &path);
  421. }
  422. static void unregister_reply(DBusMessage *message, void *user_data)
  423. {
  424. DBusConnection *conn = user_data;
  425. DBusError error;
  426. dbus_error_init(&error);
  427. if (dbus_set_error_from_message(&error, message) == FALSE) {
  428. ad.registered = false;
  429. bt_shell_printf("Advertising object unregistered\n");
  430. if (g_dbus_unregister_interface(conn, AD_PATH,
  431. AD_IFACE) == FALSE)
  432. bt_shell_printf("Failed to unregister advertising"
  433. " object\n");
  434. return bt_shell_noninteractive_quit(EXIT_SUCCESS);
  435. } else {
  436. bt_shell_printf("Failed to unregister advertisement: %s\n",
  437. error.name);
  438. dbus_error_free(&error);
  439. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  440. }
  441. }
  442. void ad_unregister(DBusConnection *conn, GDBusProxy *manager)
  443. {
  444. if (!manager)
  445. ad_release(conn);
  446. if (!ad.registered)
  447. return bt_shell_noninteractive_quit(EXIT_SUCCESS);
  448. g_free(ad.type);
  449. ad.type = NULL;
  450. if (g_dbus_proxy_method_call(manager, "UnregisterAdvertisement",
  451. unregister_setup, unregister_reply,
  452. conn, NULL) == FALSE) {
  453. bt_shell_printf("Failed to unregister advertisement method\n");
  454. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  455. }
  456. }
  457. static void ad_clear_uuids(void)
  458. {
  459. g_strfreev(ad.uuids);
  460. ad.uuids = NULL;
  461. ad.uuids_len = 0;
  462. }
  463. void ad_advertise_uuids(DBusConnection *conn, int argc, char *argv[])
  464. {
  465. if (argc < 2 || !strlen(argv[1])) {
  466. print_ad_uuids();
  467. return bt_shell_noninteractive_quit(EXIT_SUCCESS);
  468. }
  469. ad_clear_uuids();
  470. ad.uuids = g_strdupv(&argv[1]);
  471. if (!ad.uuids) {
  472. bt_shell_printf("Failed to parse input\n");
  473. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  474. }
  475. ad.uuids_len = g_strv_length(ad.uuids);
  476. g_dbus_emit_property_changed(conn, AD_PATH, AD_IFACE, "ServiceUUIDs");
  477. return bt_shell_noninteractive_quit(EXIT_SUCCESS);
  478. }
  479. void ad_disable_uuids(DBusConnection *conn)
  480. {
  481. if (!ad.uuids)
  482. return bt_shell_noninteractive_quit(EXIT_SUCCESS);
  483. ad_clear_uuids();
  484. g_dbus_emit_property_changed(conn, AD_PATH, AD_IFACE, "ServiceUUIDs");
  485. return bt_shell_noninteractive_quit(EXIT_SUCCESS);
  486. }
  487. static void ad_clear_service(void)
  488. {
  489. g_free(ad.service.uuid);
  490. memset(&ad.service, 0, sizeof(ad.service));
  491. return bt_shell_noninteractive_quit(EXIT_SUCCESS);
  492. }
  493. static bool ad_add_data(struct ad_data *data, int argc, char *argv[])
  494. {
  495. unsigned int i;
  496. memset(data, 0, sizeof(*data));
  497. for (i = 0; i < (unsigned int) argc; i++) {
  498. long int val;
  499. char *endptr = NULL;
  500. if (i >= G_N_ELEMENTS(data->data)) {
  501. bt_shell_printf("Too much data\n");
  502. return false;
  503. }
  504. val = strtol(argv[i], &endptr, 0);
  505. if (!endptr || *endptr != '\0' || val > UINT8_MAX) {
  506. bt_shell_printf("Invalid value at index %d\n", i);
  507. return false;
  508. }
  509. data->data[data->len] = val;
  510. data->len++;
  511. }
  512. return true;
  513. }
  514. void ad_advertise_service(DBusConnection *conn, int argc, char *argv[])
  515. {
  516. struct ad_data data;
  517. if (argc < 2 || !strlen(argv[1])) {
  518. if (ad.service.uuid) {
  519. print_uuid(ad.service.uuid);
  520. bt_shell_hexdump(ad.service.data.data,
  521. ad.service.data.len);
  522. }
  523. return bt_shell_noninteractive_quit(EXIT_SUCCESS);
  524. }
  525. if (!ad_add_data(&data, argc - 2, argv + 2))
  526. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  527. ad_clear_service();
  528. ad.service.uuid = g_strdup(argv[1]);
  529. ad.service.data = data;
  530. g_dbus_emit_property_changed(conn, AD_PATH, AD_IFACE, "ServiceData");
  531. return bt_shell_noninteractive_quit(EXIT_SUCCESS);
  532. }
  533. void ad_disable_service(DBusConnection *conn)
  534. {
  535. if (!ad.service.uuid)
  536. return bt_shell_noninteractive_quit(EXIT_SUCCESS);
  537. ad_clear_service();
  538. g_dbus_emit_property_changed(conn, AD_PATH, AD_IFACE, "ServiceData");
  539. return bt_shell_noninteractive_quit(EXIT_SUCCESS);
  540. }
  541. static void ad_clear_manufacturer(void)
  542. {
  543. memset(&ad.manufacturer, 0, sizeof(ad.manufacturer));
  544. return bt_shell_noninteractive_quit(EXIT_SUCCESS);
  545. }
  546. void ad_advertise_manufacturer(DBusConnection *conn, int argc, char *argv[])
  547. {
  548. char *endptr = NULL;
  549. long int val;
  550. struct ad_data data;
  551. if (argc < 2 || !strlen(argv[1])) {
  552. if (ad.manufacturer.data.len) {
  553. bt_shell_printf("Manufacturer: %u\n",
  554. ad.manufacturer.id);
  555. bt_shell_hexdump(ad.manufacturer.data.data,
  556. ad.manufacturer.data.len);
  557. }
  558. return bt_shell_noninteractive_quit(EXIT_SUCCESS);
  559. }
  560. val = strtol(argv[1], &endptr, 0);
  561. if (!endptr || *endptr != '\0' || val > UINT16_MAX) {
  562. bt_shell_printf("Invalid manufacture id\n");
  563. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  564. }
  565. if (!ad_add_data(&data, argc - 2, argv + 2))
  566. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  567. ad_clear_manufacturer();
  568. ad.manufacturer.id = val;
  569. ad.manufacturer.data = data;
  570. g_dbus_emit_property_changed(conn, AD_PATH, AD_IFACE,
  571. "ManufacturerData");
  572. return bt_shell_noninteractive_quit(EXIT_SUCCESS);
  573. }
  574. void ad_disable_manufacturer(DBusConnection *conn)
  575. {
  576. if (!ad.manufacturer.id && !ad.manufacturer.data.len)
  577. return bt_shell_noninteractive_quit(EXIT_SUCCESS);
  578. ad_clear_manufacturer();
  579. g_dbus_emit_property_changed(conn, AD_PATH, AD_IFACE,
  580. "ManufacturerData");
  581. return bt_shell_noninteractive_quit(EXIT_SUCCESS);
  582. }
  583. static void ad_clear_data(void)
  584. {
  585. memset(&ad.manufacturer, 0, sizeof(ad.manufacturer));
  586. return bt_shell_noninteractive_quit(EXIT_SUCCESS);
  587. }
  588. void ad_advertise_data(DBusConnection *conn, int argc, char *argv[])
  589. {
  590. char *endptr = NULL;
  591. long int val;
  592. struct ad_data data;
  593. if (argc < 2 || !strlen(argv[1])) {
  594. if (ad.manufacturer.data.len) {
  595. bt_shell_printf("Type: 0x%02x\n", ad.data.type);
  596. bt_shell_hexdump(ad.data.data.data, ad.data.data.len);
  597. }
  598. return bt_shell_noninteractive_quit(EXIT_SUCCESS);
  599. }
  600. val = strtol(argv[1], &endptr, 0);
  601. if (!endptr || *endptr != '\0' || val > UINT8_MAX) {
  602. bt_shell_printf("Invalid type\n");
  603. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  604. }
  605. if (!ad_add_data(&data, argc - 2, argv + 2))
  606. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  607. ad_clear_data();
  608. ad.data.type = val;
  609. ad.data.data = data;
  610. g_dbus_emit_property_changed(conn, AD_PATH, AD_IFACE, "Data");
  611. return bt_shell_noninteractive_quit(EXIT_SUCCESS);
  612. }
  613. void ad_disable_data(DBusConnection *conn)
  614. {
  615. if (!ad.data.type && !ad.data.data.len)
  616. return bt_shell_noninteractive_quit(EXIT_SUCCESS);
  617. ad_clear_data();
  618. g_dbus_emit_property_changed(conn, AD_PATH, AD_IFACE, "Data");
  619. return bt_shell_noninteractive_quit(EXIT_SUCCESS);
  620. }
  621. void ad_advertise_discoverable(DBusConnection *conn, dbus_bool_t *value)
  622. {
  623. if (!value) {
  624. bt_shell_printf("Discoverable: %s\n",
  625. ad.discoverable ? "on" : "off");
  626. return bt_shell_noninteractive_quit(EXIT_SUCCESS);
  627. }
  628. if (ad.discoverable == *value)
  629. return bt_shell_noninteractive_quit(EXIT_SUCCESS);
  630. ad.discoverable = *value;
  631. g_dbus_emit_property_changed(conn, AD_PATH, AD_IFACE, "Discoverable");
  632. return bt_shell_noninteractive_quit(EXIT_SUCCESS);
  633. }
  634. void ad_advertise_discoverable_timeout(DBusConnection *conn, long int *value)
  635. {
  636. if (!value) {
  637. if (ad.discoverable_to)
  638. bt_shell_printf("Timeout: %u sec\n",
  639. ad.discoverable_to);
  640. return bt_shell_noninteractive_quit(EXIT_SUCCESS);
  641. }
  642. if (ad.discoverable_to == *value)
  643. return bt_shell_noninteractive_quit(EXIT_SUCCESS);
  644. ad.discoverable_to = *value;
  645. g_dbus_emit_property_changed(conn, AD_PATH, AD_IFACE,
  646. "DiscoverableTimeout");
  647. return bt_shell_noninteractive_quit(EXIT_SUCCESS);
  648. }
  649. void ad_advertise_tx_power(DBusConnection *conn, dbus_bool_t *value)
  650. {
  651. if (!value) {
  652. bt_shell_printf("Tx Power: %s\n", ad.tx_power ? "on" : "off");
  653. return bt_shell_noninteractive_quit(EXIT_SUCCESS);
  654. }
  655. if (ad.tx_power == *value)
  656. return bt_shell_noninteractive_quit(EXIT_SUCCESS);
  657. ad.tx_power = *value;
  658. g_dbus_emit_property_changed(conn, AD_PATH, AD_IFACE, "Includes");
  659. return bt_shell_noninteractive_quit(EXIT_SUCCESS);
  660. }
  661. void ad_advertise_name(DBusConnection *conn, bool value)
  662. {
  663. if (ad.name == value)
  664. return bt_shell_noninteractive_quit(EXIT_SUCCESS);
  665. ad.name = value;
  666. if (!value) {
  667. free(ad.local_name);
  668. ad.local_name = NULL;
  669. }
  670. g_dbus_emit_property_changed(conn, AD_PATH, AD_IFACE, "Includes");
  671. return bt_shell_noninteractive_quit(EXIT_SUCCESS);
  672. }
  673. void ad_advertise_local_name(DBusConnection *conn, const char *name)
  674. {
  675. if (!name) {
  676. if (ad.local_name)
  677. bt_shell_printf("LocalName: %s\n", ad.local_name);
  678. else
  679. bt_shell_printf("Name: %s\n", ad.name ? "on" : "off");
  680. return bt_shell_noninteractive_quit(EXIT_SUCCESS);
  681. }
  682. if (ad.local_name && !strcmp(name, ad.local_name))
  683. return;
  684. g_free(ad.local_name);
  685. ad.local_name = strdup(name);
  686. g_dbus_emit_property_changed(conn, AD_PATH, AD_IFACE, "LocalName");
  687. /* Remove local-name from Includes since LocalName would be set */
  688. if (ad.name) {
  689. ad.name = false;
  690. g_dbus_emit_property_changed(conn, AD_PATH, AD_IFACE,
  691. "Includes");
  692. }
  693. return bt_shell_noninteractive_quit(EXIT_SUCCESS);
  694. }
  695. void ad_advertise_appearance(DBusConnection *conn, bool value)
  696. {
  697. if (ad.appearance == value)
  698. return bt_shell_noninteractive_quit(EXIT_SUCCESS);
  699. ad.appearance = value;
  700. if (!value)
  701. ad.local_appearance = UINT16_MAX;
  702. g_dbus_emit_property_changed(conn, AD_PATH, AD_IFACE, "Includes");
  703. return bt_shell_noninteractive_quit(EXIT_SUCCESS);
  704. }
  705. void ad_advertise_local_appearance(DBusConnection *conn, long int *value)
  706. {
  707. if (!value) {
  708. if (ad.local_appearance != UINT16_MAX)
  709. bt_shell_printf("Appearance: %s (0x%04x)\n",
  710. bt_appear_to_str(ad.local_appearance),
  711. ad.local_appearance);
  712. else
  713. bt_shell_printf("Appearance: %s\n",
  714. ad.appearance ? "on" : "off");
  715. return bt_shell_noninteractive_quit(EXIT_SUCCESS);
  716. }
  717. if (ad.local_appearance == *value)
  718. return bt_shell_noninteractive_quit(EXIT_SUCCESS);
  719. ad.local_appearance = *value;
  720. g_dbus_emit_property_changed(conn, AD_PATH, AD_IFACE, "Appearance");
  721. return bt_shell_noninteractive_quit(EXIT_SUCCESS);
  722. }
  723. void ad_advertise_duration(DBusConnection *conn, long int *value)
  724. {
  725. if (!value) {
  726. if (ad.duration)
  727. bt_shell_printf("Duration: %u sec\n", ad.duration);
  728. return bt_shell_noninteractive_quit(EXIT_SUCCESS);
  729. }
  730. if (ad.duration == *value)
  731. return bt_shell_noninteractive_quit(EXIT_SUCCESS);
  732. ad.duration = *value;
  733. g_dbus_emit_property_changed(conn, AD_PATH, AD_IFACE, "Duration");
  734. return bt_shell_noninteractive_quit(EXIT_SUCCESS);
  735. }
  736. void ad_advertise_timeout(DBusConnection *conn, long int *value)
  737. {
  738. if (!value) {
  739. if (ad.timeout)
  740. bt_shell_printf("Timeout: %u sec\n", ad.timeout);
  741. return bt_shell_noninteractive_quit(EXIT_SUCCESS);
  742. }
  743. if (ad.timeout == *value)
  744. return bt_shell_noninteractive_quit(EXIT_SUCCESS);
  745. ad.timeout = *value;
  746. g_dbus_emit_property_changed(conn, AD_PATH, AD_IFACE, "Timeout");
  747. return bt_shell_noninteractive_quit(EXIT_SUCCESS);
  748. }
  749. void ad_advertise_secondary(DBusConnection *conn, const char *value)
  750. {
  751. if (!value) {
  752. if (ad.secondary)
  753. bt_shell_printf("Secondary Channel: %s\n",
  754. ad.secondary);
  755. return bt_shell_noninteractive_quit(EXIT_SUCCESS);
  756. }
  757. if (ad.secondary && !strcmp(value, ad.secondary))
  758. return bt_shell_noninteractive_quit(EXIT_SUCCESS);
  759. free(ad.secondary);
  760. if (value[0] == '\0') {
  761. ad.secondary = NULL;
  762. return bt_shell_noninteractive_quit(EXIT_SUCCESS);
  763. }
  764. ad.secondary = strdup(value);
  765. g_dbus_emit_property_changed(conn, AD_PATH, AD_IFACE,
  766. "SecondaryChannel");
  767. return bt_shell_noninteractive_quit(EXIT_SUCCESS);
  768. }
  769. void ad_advertise_interval(DBusConnection *conn, uint32_t *min, uint32_t *max)
  770. {
  771. if (!min && !max) {
  772. if (ad.min_interval && ad.max_interval)
  773. bt_shell_printf("Interval: %u-%u msec\n",
  774. ad.min_interval, ad.max_interval);
  775. return bt_shell_noninteractive_quit(EXIT_SUCCESS);
  776. }
  777. if (ad.min_interval != *min) {
  778. ad.min_interval = *min;
  779. g_dbus_emit_property_changed(conn, AD_PATH, AD_IFACE,
  780. "MinInterval");
  781. }
  782. if (ad.max_interval != *max) {
  783. ad.max_interval = *max;
  784. g_dbus_emit_property_changed(conn, AD_PATH, AD_IFACE,
  785. "MaxInterval");
  786. }
  787. return bt_shell_noninteractive_quit(EXIT_SUCCESS);
  788. }